今天我试图用LINQ解决https://projecteuler.net/problem=5。
这可以正常工作,并且可以在我的计算机上不到2秒执行一次,但是有点冗长:
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()
所以我也尝试将2-19范围也设为Enumerable,并像这样进行交叉连接
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
第二个解决方案的问题是它分配大量资源,在x86 LINQPad中崩溃,并带有OutOfMemoryException,并且在我手动杀死它之前吞噬了x64 LINQPad版本中的大量内存。
我的问题是为什么? 还有LINQ查询可以避免该问题吗?
CLR堆分配分析器插件告诉我,内部正在进行堆分配
.Select(d => (n, d))
由于捕获了“ n”。 所以我认为这是OutOfMemoryException的原因,但是... 因为我使用First()而不在其间实现查询,所以我认为这应该不成问题,因为linq会实现该组并再次丢弃它,因为它在释放内存时不满足条件。 selectmany或groupby是否发生一些时髦的事情,迫使所有数据首先被实现,还是我的思维模型在这里是错误的?
答案 0 :(得分:2)
如果您尝试了以下代码,则会发生相同的问题:
Enumerable.Range(1, 1000000000)
.GroupBy(x => x)
.First();
这意味着在查询执行期间将实现所有组,这就是抛出OutOfMemoryException
的原因。
要解决该问题,可以使用@ haim770在注释部分中提到的以下LINQ代码:
Enumerable
.Range(1, 1000000000)
.First(i => Enumerable
.Range(2, 19)
.All(j => i % j == 0))
为了进行更多优化,我找到了更好的解决方案。很抱歉没有使用LINQ,但是它已经进行了优化,也许有一种方法可以使用LINQ来实现。
与其循环浏览大量数字并检查每个数字,何不直接建立所需的答案。
所需的输出是数字x
被所有数字1..n
整除而没有任何余数。因此,它是1..n
范围内数字的所有质数的倍数。通过仅使用这些主要因子的最小量,我们可以获得最小的x
。
例如,如果n = 10
则:
i = 2: prime factors of 2 are [2] -> neededPrimes = [2]
i = 3: prime factors of 3 are [3] -> neededPrimes = [2, 3]
i = 4: prime factors of 4 are [2, 2] -> neededPrimes = [2, 2, 3] // we add just one 2 because the other already exists in neededPrimes
i = 5: prime factors of 5 are [5] -> neededPrimes = [2, 2, 3, 5]
i = 6: prime factors of 6 are [2, 3] -> neededPrimes = [2, 2, 3, 5] // we add nothing because [2, 3] are already in neededPrimes
i = 7: prime factors of 7 are [7] -> neededPrimes = [2, 2, 3, 5, 7]
i = 8: prime factors of 8 are [2, 2, 2] -> neededPrimes = [2, 2, 2, 3, 5, 7] // we add one 2 because the other 2's already exist in neededPrimes
i = 9: prime factors of 9 are [3, 3] -> neededPrimes = [2, 2, 2, 3, 3, 5, 7]
i = 10: prime factors of 10 are [2, 5] -> neededPrimes = [2, 2, 2, 3, 3, 5, 7]
x = 2 * 2 * 2 * 3 * 3 * 5 * 7 = 2520
这是一个代码,我希望很清楚:
public static void Main(string[] args)
{
// The number to find its smallest multiple
var n = 20;
// A list that contains all primes that are founded across the calculation
var calculatedPrimes = new List<int>();
// Start through the numbers that x (the output number) should be divisible by
for (var i = 2; i <= n; i++)
{
// Get primes of i
var primes = GetPrimeFactors(i);
// Loop through primes of i and add to "calculatedPrimes" the ones that are not
// in "calculatedPrimes"
primes.ForEach(prime =>
{
if (!calculatedPrimes.Contains(prime) ||
calculatedPrimes.Count(p => p == prime) < primes.Count(p => p == prime))
{
calculatedPrimes.Add(prime);
}
});
}
// The output number should be the multiple of all primes in "calculatedPrimes" list
var x = calculatedPrimes.Aggregate(1, (res, p) => res * p);
Console.WriteLine(x);
Console.ReadLine();
}
// A function to get prime factors of a given number n
// (example: if n = 12 then this will return [2, 2, 3])
private static List<int> GetPrimeFactors(int n)
{
var res = new List<int>();
while (n % 2 == 0)
{
res.Add(2);
n /= 2;
}
for (var i = 3; i <= Math.Sqrt(n); i += 2)
{
while (n % i == 0)
{
res.Add(i);
n /= i;
}
}
if (n > 2)
res.Add(n);
return res;
}