C ++函数计算阶乘返回负值

时间:2016-01-28 05:03:28

标签: c++ algorithm factorial integer-overflow

我编写了一个C ++函数来计算阶乘,并用它来计算 22 C 11 (组合)。我已声明变量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 <= 10时工作正常。我已经尝试了long long int而不是long int但它仍然没有工作。

4 个答案:

答案 0 :(得分:3)

实际计算阶乘是不明智的 - 它们增长得非常快。一般来说,使用组合公式,寻找一种重新排序操作的方法是一个好主意,以保持中间结果在某种程度上受到限制。

例如,让我们看一下(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。通过交叉乘法和除法,中间结果的增长率降低。

所以,像这样:

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

Demo

答案 1 :(得分:2)

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

2 64 = 18,446,744,073,709,551,615

因此除非你有unsigned long long的128位整数,否则你会有整数溢出。

答案 2 :(得分:0)

您正在触发整数溢出,这会导致未定义的行为。实际上,您可以使用new JLabel("<html>2 <sup>2</sup>&frasl;<sub>3</sub></html>")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

您可能对this thread感兴趣,{{3}}显示了用于计算不需要这么多中间存储的nCr的算法。

答案 3 :(得分:0)

通过避免蛮力方法,您可以增加成功的机会。

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;
}