如何计算 LINQ 中谓词过滤的巨大范围的总和
How to calculate the sum of a huge range filtered by a predicate in LINQ
我正在尝试通过传递 int.MaxValue
而不是 1000 来测试 Problem 1 from projecteuler.net 的三个解决方案的性能。
第一个解法:
long sum = SumDivisibleBy(3) + SumDivisibleBy(5) - SumDivisibleBy(15);
Console.WriteLine(sum);
其中 SumDivisibleBy 是:
public static long SumDivisibleBy(int k, int n = int.MaxValue)
{
long m = n / k;
return k * (m * (m + 1) / 2);
}
比
快(约 27 秒)
第二种解法:
long sum = 0;
for (int i = 0; i < int.MaxValue; i++)
{
if (i % 3 == 0 || i % 5 == 0)
{
sum += (long)i;
}
}
Console.WriteLine(sum);
第三种解决方案(我认为这是一个优雅的解决方案)是:
Console.WriteLine(Enumerable.Range(1, 999)
.Where(x => x % 3 == 0 || x % 5 == 0)
.Sum());
但我无法实现(出于测试性能目的):
Console.WriteLine(Enumerable.Range(1, int.MaxValue)
.Where(x => x % 3 == 0 || x % 5 == 0)
.Sum());
因为它给了我一个 OverflowException
这是自然的,因为 int Sum(this IEnumerable<int> source)
.
我的问题是:
如何在下面的代码中将 int Sum(this IEnumerable<int> source)
向上转换为 long Sum(this IEnumerable<long> source)
:
Console.WriteLine(Enumerable.Range(1, int.MaxValue)
.Where(x => x % 3 == 0 || x % 5 == 0)
.Sum());
尝试将过滤后的整数序列投影到长整数序列:
.Where(x => x % 3 == 0 || x % 5 == 0)
.Select(x => (long)x)
.Sum()
请注意,.Cast<long>()
看起来更优雅,但 won't work for this kind of conversion。
您可以像下面这样扩展 Enumerable
class:
public static class MyExtensions
{
public static IEnumerable<long> Range(long start, long count)
{
for (long i = start; i < start + count; i++)
{
yield return i;
}
}
}
这样您就可以毫无问题地使用 Sum
方法,如下所示:
MyExtensions.Range(1, int.MaxValue)
.Where(x => x % 3 == 0 || x % 5 == 0)
.Sum());
仅供参考...我测试了所有推荐的解决方案(LinqPad)并检查了执行时间。
方法1)转换为Select - 时间:1:23.360s(中)
.Select(x => (long)x)
方法2)扩展方法-时间:02:06.768s(慢)
MyExtensions.Range(1, int.MaxValue)
.Where(x => x % 3 == 0 || x % 5 == 0)
.Sum());
方法 3) T.Glatzer 在评论 Ani 的解决方案时建议 - 时间:00:54.567(快速)
Enumerable.Range(1, int.MaxValue).Where(x=> x%3==0 || x%5==0).Sum(x=>(long)x);
结论:
每种方法都比您的 SumDivisibleBy 函数慢得多,那么除了优雅的代码之外,Linq 解决方案还有哪些好处? None。
注意:我没有在内存使用,CPU使用等其他方面进行测试
我正在尝试通过传递 int.MaxValue
而不是 1000 来测试 Problem 1 from projecteuler.net 的三个解决方案的性能。
第一个解法:
long sum = SumDivisibleBy(3) + SumDivisibleBy(5) - SumDivisibleBy(15);
Console.WriteLine(sum);
其中 SumDivisibleBy 是:
public static long SumDivisibleBy(int k, int n = int.MaxValue)
{
long m = n / k;
return k * (m * (m + 1) / 2);
}
比
快(约 27 秒)第二种解法:
long sum = 0;
for (int i = 0; i < int.MaxValue; i++)
{
if (i % 3 == 0 || i % 5 == 0)
{
sum += (long)i;
}
}
Console.WriteLine(sum);
第三种解决方案(我认为这是一个优雅的解决方案)是:
Console.WriteLine(Enumerable.Range(1, 999)
.Where(x => x % 3 == 0 || x % 5 == 0)
.Sum());
但我无法实现(出于测试性能目的):
Console.WriteLine(Enumerable.Range(1, int.MaxValue)
.Where(x => x % 3 == 0 || x % 5 == 0)
.Sum());
因为它给了我一个 OverflowException
这是自然的,因为 int Sum(this IEnumerable<int> source)
.
我的问题是:
如何在下面的代码中将 int Sum(this IEnumerable<int> source)
向上转换为 long Sum(this IEnumerable<long> source)
:
Console.WriteLine(Enumerable.Range(1, int.MaxValue)
.Where(x => x % 3 == 0 || x % 5 == 0)
.Sum());
尝试将过滤后的整数序列投影到长整数序列:
.Where(x => x % 3 == 0 || x % 5 == 0)
.Select(x => (long)x)
.Sum()
请注意,.Cast<long>()
看起来更优雅,但 won't work for this kind of conversion。
您可以像下面这样扩展 Enumerable
class:
public static class MyExtensions
{
public static IEnumerable<long> Range(long start, long count)
{
for (long i = start; i < start + count; i++)
{
yield return i;
}
}
}
这样您就可以毫无问题地使用 Sum
方法,如下所示:
MyExtensions.Range(1, int.MaxValue)
.Where(x => x % 3 == 0 || x % 5 == 0)
.Sum());
仅供参考...我测试了所有推荐的解决方案(LinqPad)并检查了执行时间。
方法1)转换为Select - 时间:1:23.360s(中)
.Select(x => (long)x)
方法2)扩展方法-时间:02:06.768s(慢)
MyExtensions.Range(1, int.MaxValue)
.Where(x => x % 3 == 0 || x % 5 == 0)
.Sum());
方法 3) T.Glatzer 在评论 Ani 的解决方案时建议 - 时间:00:54.567(快速)
Enumerable.Range(1, int.MaxValue).Where(x=> x%3==0 || x%5==0).Sum(x=>(long)x);
结论:
每种方法都比您的 SumDivisibleBy 函数慢得多,那么除了优雅的代码之外,Linq 解决方案还有哪些好处? None。
注意:我没有在内存使用,CPU使用等其他方面进行测试