I'm doing Project Euler problem 5 as a kata, focussing on TDD and code readability. The challenge is:
What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?
For reference, a "less-readable" solution that reeks of goto was something along these lines:
public int Solution_GotoVersion()
{
int solution = 1;
outer: while (solution++ < int.MaxValue)
{
for (int j = 1; j <= 20; j++)
{
if (solution % j != 0)
goto outer;
}
return solution;
}
throw new Exception("Solution not found");
}
This takes about 4 seconds to run, relative to the other solutions.
Of course I was trying to avoid such code (in light of readability, for one), I wrote it specifically to compare it to my first solution, that uses an alternative along the lines of this SO answer. So, focussing on readability, here's something a little nicer:
public int Solution_LinqVersion()
{
var dividers = Enumerable.Range(1, 20);
int solution = Enumerable
.Range(1, int.MaxValue)
.FirstOrDefault(candidate
=> dividers.All(divider => candidate.IsDivisibleBy(divider)));
return solution;
}
This performs much worse, taking about 25 seconds, relatively. I don't mind loosing some performance when traded for readability, but a factor 8 is a bit much.
I've tried an intermediate solution, like this:
public int Solution_LittleOfBothWorlds()
{
var dividers = Enumerable.Range(1, 20).ToArray();
int solution = 1;
while (solution++ < int.MaxValue)
{
if (dividers.All(divider => solution % divider == 0))
return solution;
}
throw new Exception("Solution not found");
}
This is in the middle, performance-wise, taking about 14 seconds, relatively.
What can I do to keep relative performance close to the first example, with code that utilizes Linq's readability?
Note: I'm aware there are of many other optimizations possible, amongst others in the algorithm itself. In this case however, I'm specifically interested in the performance of Linq queries relative to more "oldskool" constructs, and how to keep Linq's nice syntax without giving up too much performance (if possible).
dividers.All(candidate.IsDivisibleBy)
. And I think your "newskool" vs "oldskool" concept is a wrong frame of mind - each tool has its uses, and none replaces the other. \$\endgroup\$