如何计算这个阶乘

时间:2014-10-20 20:24:59

标签: c

#include <stdio.h>
int main(){
    int n, v;
    printf("Please enter a value from 39 to 59: \n");
    scanf("%d", &n);
    printf("Please enter a value from 3 to 7: \n");
    scanf("%d", &v);

}

当我从用户那里获得这些值时,我该如何进行这种因子计算:

n! / ((n-v)! * v!))

我尝试过不同的数据类型,但显然没有人可以保留结果。

例如:n = 49,v = 6。结果是:13,983,816,但我怎么能得到它呢?

4 个答案:

答案 0 :(得分:5)

你最好的办法是抛弃天真的阶乘实现,通常是基于递归,然后切换到返回伽玛函数的自然对数的实现。

伽玛函数与阶乘有关:gamma(n) = (n-1)!

最重要的是natural log of gamma,因为你可以像这样重写那个表达式:

ln(n!/(n-v)!v!) = ln(n!) - ln((n-v)!) - ln(v!)

但是

(n-v)! = gamma(n-v+1)
n! = gamma(n+1)
v! = gamma(v+1)

所以

ln(n!/(n-v)!v!) = lngamma(n+1) - lngamma(n-v+1) - lngamma(v+1)

您可以在Numerical Recipes中找到lngamma的实现。

lngamma会返回一个双精度数,因此它甚至适用于较大的值。

不言而喻,您将双方exp()获取您想要的原始表达。

答案 1 :(得分:3)

@duffymo想法看起来太有趣了:从lgamma()使用<math.h>

过去的结果可能是x=1e15,开始失去有效的有效数字。能够获得1000000.0!仍然很有趣。

void factorial_expo(double x, double *significand, double *expo) {
    double y = lgamma(x+1);
    const static double ln10 = 2.3025850929940456840179914546844;
    y /= ln10;
    double ipart;
    double fpart = modf(y, &ipart);
    if (significand) *significand = pow(10.0, fpart);
    if (expo) *expo = ipart;
}

void facttest(double x) {
  printf("%.1f!  =  ", x);
  double significand, expo;
  factorial_expo(x, &significand, &expo);
  int digits = expo > 15 ? 15 : expo;
  if (digits < 1) digits++;
  printf("%.*fe%.0f\n", digits, significand, expo);
}


int main(void) {
  facttest(0.0);
  facttest(1.0);
  facttest(2.0);
  facttest(6.0);
  facttest(10.0);
  facttest(69.0);
  facttest(1000000.0);
  return 0;
}

0.0!  =  1.0e0
1.0!  =  1.0e0
2.0!  =  2.0e0
6.0!  =  7.20e2
10.0!  =  3.628800e6
69.0!  =  1.711224524281441e98
1000000.0!  =  8.263931668544735e5565708

答案 2 :(得分:2)

在评论中,您最后说您不需要确切的结果。

只需使用浮点数。您需要处理的最大中间结果是59!,大约是1.3868e80;类型double足以容纳该值。

编写如下函数:

double factorial(int n);

(我认为你知道如何实现它)并使用它。

如果您要进行大量的这些计算,您可能希望通过将结果存储在数组中来缓存结果。如果您定义如下数组:

double fact[60];

然后您可以将N!的值存储在fact[N]中,以便N从0到59,并且您可以在大约计算59!所需的时间内填充整个数组就一次。否则,你将在每次计算时进行几十次浮点乘法和除法 - 如果你这样做一次是微不足道的,但如果你这样做,可能会很重要,比如数千或数百万次。

如果您需要精确的结果,您可以使用像GNU MP这样的扩展整数库,正如其他人所建议的那样。或者您可以使用内置支持任意长度整数的语言(例如Python)。

或者你可以按照避免溢出的顺序执行乘法和除法;我不知道该怎么做,但由于n! / ((n-v)! * v!))是一个常见的公式,我强烈怀疑已经完成了工作。

答案 3 :(得分:0)

您无法以简单的方式使用59!这么长的数字。 但是,您可以使用特殊的C库,这些库使用大于8字节的长数字,例如GMP