为什么不同的求和算法不匹配?

时间:2015-09-28 14:17:13

标签: c# .net algorithm math integer-arithmetic

假设我想得到从M到N的所有方格的总和。我用google搜索了一下这个公式:

  

(1 ^ 2 + 2 ^ 2 + 3 ^ 2 + ... + N ^ 2)=(N *(N + 1)*(2N + 1))/ 6

所以我写了这段代码:

static void Main(string[] args)
{
    const int from = 10;
    const int to = 50000;
    Console.WriteLine(SumSquares(from, to));
    Console.WriteLine(SumSquares2(from, to));
}

static long SumSquares(int m, int n)
{
    checked
    {
        long x = m - 1;
        long y = n;
        return (((y*(y + 1)*(2*y + 1)) - (x*(x + 1)*(2*x + 1)))/6);
    }
}

static long SumSquares2(int m, int n)
{
    long sum = 0;

    for (int i = m; i <= n; ++i)
    {
        sum += i * i;
    }
    return sum;
}

它可以正常工作直到40k,但当N变为50k时它会失败。输出为50k:

41667916674715
25948336371355
Press any key to continue . . .

我认为这是溢出或其他内容,因此我添加了checked关键字并尝试将long更改为double,但我得到了相同的结果。怎么解释?如何在没有循环的情况下获得正确的结果?

3 个答案:

答案 0 :(得分:32)

你的第二种方法是溢出的,因为你在循环中使用int。将其更改为long,如下所示(并添加checked):

static long SumSquares2(int m, int n)
{
    checked
    {
        long sum = 0;

        for (long i = m; i <= n; ++i)
        {
            sum += i*i;
        }
        return sum;
    }
}

出现问题的是i*i内部计算为int数据类型,即使结果被转换为long数据类型(即变量{{1}它溢出了。

答案 1 :(得分:12)

当您对结果使用long时,您仍然使用int作为运算符。我将M和N定义为long或甚至BigInteger,结果相同。如果不这样做,即使结果类型为long,也可能仍在进行int算术。

我尝试了你的代码,得到了你得到的结果。但后来我将每个int改为long并得到两个匹配的数字,最高为N的1600000。

使用BigInteger,我达到160000000并仍然正常工作(m = 10和n = 160000000的结果是13653333461333333359999715,两种方式)。

要使用BigInteger,您需要在项目中添加对System.Numerics dll的引用,并且需要在代码顶部包含一个语句,包括该库。

using System.Numerics;

namespace ConsoleFiddle
{
  class Program
  {
    static void Main(string[] args)
    {
        BigInteger from = 10;
        BigInteger to = 160000000;
        Console.WriteLine(SumSquares(from, to));
        Console.WriteLine(SumSquares2(from, to));
        Console.ReadKey();
    }
    static BigInteger SumSquares(BigInteger m, BigInteger n)
    {
        checked
        {
            BigInteger x = m - 1;
            BigInteger y = n;
            return (((y * (y + 1) * (2 * y + 1)) - (x * (x + 1) * (2 * x + 1))) / 6);
        }
    }
    static BigInteger SumSquares2(BigInteger m, BigInteger n)
    {
      checked
      {
        BigInteger sum = 0;
        for (BigInteger i = m; i <= n; ++i)
        {
            sum += i * i;
        }
        return sum;
      }
    }

对于M为4000000000000000000(4 x 10 ^ 18),N为4000000000100000000.此代码仍然有效,并使用第一种方法(1600000016040000000400333333338333333350000000)立即生成结果。使用第二种方法需要一段时间(1亿次循环迭代),但结果相同。

答案 2 :(得分:3)

您很可能遇到integer overflow,因为long的范围有限。您可能已禁用整数溢出的异常,因此不会抛出异常。如果我没有弄错的话,可以在Visual Studio的项目属性中禁用和启用整数溢出的异常。