如何计算 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使用等其他方面进行测试