【问题标题】:C++ function to calculate factorial returns negative value计算阶乘的 C++ 函数返回负值
【发布时间】:2016-01-28 05:03:28
【问题描述】:

我编写了一个 C++ 函数来计算阶乘,并用它来计算 22C11(组合)。我已经声明了一个变量ans 并将其设置为0。我试图计算

22C11 = fact(2*n)/(fact(n)*fact(n))

我将n 发送为 11。出于某种原因,我在答案中存储了一个负值。我该如何解决这个问题?

long int fact(long int n) {
    if(n==1||n==0)
        return 1;
    long int x=1;
    if(n>1)
    x=n*fact(n-1);
    return x;
}

主函数中包含以下几行:

long int ans=0;
    ans=ans+(fact(2*n)/(fact(n)*fact(n)));
cout<<ans;

我得到的答案是 -784 正确答案应该是705432

注意:此函数在 n

【问题讨论】:

  • 正如我所说,我已经尝试过long long int 和long int,但它们不起作用。
  • 嗯,这就是答案,要获得无效代码的帮助,您需要发布该代码
  • long long int 通常为 64 位。 22! 是 1124000727777607680000,至少需要 70 位。

标签: c++ algorithm factorial integer-overflow


【解决方案1】:

实际计算阶乘是不明智的——它们增长得非常快。一般来说,对于组合公式,寻找一种方法来重新排序运算以在一定程度上限制中间结果是一个好主意。

例如,让我们看看(2*n)!/(n!*n!)。可以改写为((n+1)*(n+2)*...*(2*n)) / (1*2*...*n) == (n+1)/1 * (n+2)/2 * (n+3)/3 ... * (2*n)/n。通过交错乘法和除法,降低了中间结果的增长率。​​p>

所以,是这样的:

int f(int n) {
  int ret = 1;
  for (int i = 1; i <= n; ++i) {
    ret *= (n + i);
    ret /= i;
  }
  return ret;
}

Demo

【讨论】:

    【解决方案2】:

    22! = 1,124,000,727,777,607,680,000

    264 = 18,446,744,073,709,551,615

    所以除非你有 128 位整数 unsigned long long 你有整数溢出。

    【讨论】:

      【解决方案3】:

      您正在触发整数溢出,这会导致未定义的行为。实际上,您可以使用long long int 或unsigned long long int 来获得更高的精度,例如:

      unsigned long long fact(int n)
      {
          if(n < 2)
              return 1;
      
          return fact(n-1) * n;
      }
      

      你说你试过这个但没有用,但我猜你忘了更新 x 的类型或其他东西。 (在我的版本中,我删除了x,因为它是多余的)。和/或您的计算量仍然太大以至于溢出unsigned long long int。

      您可能对this thread 感兴趣,它展示了一种不需要太多中间存储的计算 nCr 的算法。

      【讨论】:

      • 虽然递归非常大的数字通常不会导致任何好的结果。使用阶乘,无论如何你都会保持这个数字很小,但是每次你从自身调用一个函数时,它都会建立一个新的堆栈框架。您可以很快耗尽堆栈空间。例如: unsigned long long foo(int n){ if (n == 1
      • @Goodies 是一个很好的观点,尽管我希望编译器在这种情况下实现 尾递归优化。其实你可以see that happening here
      【解决方案4】:

      您通过避免使用蛮力方法来增加成功的机会。

      COMB(N1, N2) = FACT(N1)/(FACT(N1-N2)*FACT(N2))
      

      您可以利用提名者和分母都有很多共同术语这一事实。

      COMB(N1, N2) = (N1-N2+1)*(N1-N2+2)*...*N1/FACT(N1)
      

      这是一个利用该知识并计算COMB(22,11) 的实现,整数溢出的风险要小得多。

      unsigned long long comb(int n1, int n2)
      {
         unsigned long long res = 1;
         for (int i = (n1-n2)+1; i<= n1; ++i )
         {
            res *= i;
         }
      
         for (int i = 2; i<= n2; ++i )
         {
            res /= i;
         }
      
         return res;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-01-08
        • 1970-01-01
        • 2018-10-16
        • 1970-01-01
        • 1970-01-01
        • 2023-03-22
        • 1970-01-01
        相关资源
        最近更新 更多