Today I tried to solve https://projecteuler.net/problem=5 with LINQ.
this works fine and executes in under 2sec on my machine, but is a little verbose:
Enumerable.Range(1, 1000000000)
.Where(i => i % 2 == 0
&& i % 3 == 0
&& i % 4 == 0
&& i % 5 == 0
&& i % 6 == 0
&& i % 7 == 0
&& i % 8 == 0
&& i % 9 == 0
&& i % 10 == 0
&& i % 11 == 0
&& i % 12 == 0
&& i % 13 == 0
&& i % 14 == 0
&& i % 15 == 0
&& i % 16 == 0
&& i % 17 == 0
&& i % 18 == 0
&& i % 19 == 0
&& i % 20 == 0
)
.First()
so I tried to put the 2-19 range into a Enumerable too and do a crossjoin like so
Enumerable.Range(1, 1000000000)
.SelectMany(n =>
Enumerable.Range(2, 19)
.Select(d => (n, d))
)
.GroupBy(x => x.n)
.Where(g => g.All(y => y.n % y.d == 0))
.First()
.Key
the issue with the second solution is that it allocates heavily, crashes in x86 LINQPad with an OutOfMemoryException, and eats up a whole lot of mem in the x64 LINQPad version before I kill it manually.
My question is why? And is there a LINQ query that can avoid that issue?
The CLR Heap Allocation Analyzer plugin tells me that there is a heap allocation going on inside
.Select(d => (n, d))
due to the capturing of 'n'. So I assume that is the reason for the OutOfMemoryException, but ... since I use First() without materializing the query in between, I assumed that this should not be a problem, because linq would materialize the group and discard it again because it does not satisfy the condition while releasing the memory. is there something funky going on with selectmany or groupby that forces all data to be materialized first, or is my mental model just wrong here?