C语言中大数的阶乘计算

C语言中大数的阶乘计算,c,algorithm,C,Algorithm,在我的C代码中,我想计算范围为1到100的数字的阶乘。对于较小的数字,该函数起作用,但对于较大的数字(例如100!),它返回不正确的结果。有没有办法处理C中大数的阶乘 我使用的编译器是gcc v4.3.3。 我的代码如下: #include <stdio.h> #include <math.h> double print_solution(int); int main(void) { int no_of_inputs, n; int c

在我的C代码中,我想计算范围为1到100的数字的阶乘。对于较小的数字,该函数起作用,但对于较大的数字(例如100!),它返回不正确的结果。有没有办法处理C中大数的阶乘

我使用的编译器是gcc v4.3.3。 我的代码如下:

#include <stdio.h>
#include <math.h>

double print_solution(int);

int main(void)
{
        int no_of_inputs, n;
        int ctr = 1;

        scanf("%d",&no_of_inputs); //Read no of inputs

        do
        {
                scanf("%d",&n); //Read the input
                printf("%.0f\n", print_solution(n));
                ctr++;  
        } while(ctr <= no_of_inputs);

        return 0;       
}

double print_solution(int n)
{
        if(n == 0 || n == 1)
                return 1;
        else
                return n*print_solution(n-1);
}
#包括
#包括
双打印解决方案(int);
内部主(空)
{
int无输入,n;
int ctr=1;
scanf(“%d”,&无输入);//读取无输入
做
{
scanf(“%d”,&n);//读取输入
printf(“%.0f\n”,print_溶液(n));
ctr++;

}当(ctr我猜这是因为您溢出了整数范围,该范围高达约20亿。如果使用无符号整数,您最多可以获得40亿,但除此之外,您还必须使用。

没有标准的C数据类型能够准确地处理大到100%的数字。您唯一的选择是使用,无论是通过库还是自己完成。

> 如果这只是一个爱好项目,我建议你自己尝试一下。这是一种有趣的锻炼。如果这是与工作相关的,使用一个已有的图书馆


您通常会得到的最大C数据类型是64位整数。100!的顺序为10157,这至少需要525位才能准确地存储为整数。

100!=933262154439415268169923885626670049071596826643816214685929 6389521759999322991560894146397156518286253697920827223758251185210 916864000000000000000000000000


不能用整型或长型来表示这么大的数字。

100阶乘是巨大的,准确地说,它是9332621544394152681699238856266700490715968264381624685923895217 59999322991560894146397615651828625369792082722375825118521091686400 00000000000000


也许你应该使用bignum库,比如。它有很好的文档,相当一致的界面,速度,如果你在Linux上,你的发行版可能有一个包(我想我的默认安装了它)

这肯定是由于溢出。你需要一种表示大数字的方法(
unsigned long
甚至不能覆盖21!).

您可以尝试使用“unsigned long long”类型,但这是内置类型的最大值。
我建议(正如克莱特斯已经提到的)要么使用已知的大数实现,要么自己编写一个。“这是一个很好的练习”x2.

如果你不想使用bigint库,那么使用stdlib最好使用
long double
tgamal()
from
math.h

long double fact(unsigned n)
{
    return tgammal(n + 1);
}
这将使您在x86上获得精度为18位小数的
100!
(即80位
长双精度

确切的实现也没有那么复杂:

#include <math.h>
#include <stdio.h>
#include <string.h>

void multd(char * s, size_t len, unsigned n)
{
    unsigned values[len];
    memset(values, 0, sizeof(unsigned) * len);
    for(size_t i = len; i--; )
    {
        unsigned x = values[i] + (s[i] - '0') * n;
        s[i] = '0' + x % 10;
        if(i) values[i - 1] += x / 10;
    }
}

void factd(char * s, size_t len, unsigned n)
{
    memset(s, '0', len - 1);
    s[len - 1] = '1';
    for(; n > 1; --n) multd(s, len, n);
}

int main(void)
{
    unsigned n = 100;
    size_t len = ceill(log10l(tgammal(n + 1)));
    char dstr[len + 1];
    dstr[len] = 0;
    factd(dstr, len, n);
    puts(dstr);
}
#包括
#包括
#包括
void multd(字符*s,大小长度,无符号n)
{
无符号值[len];
memset(值,0,sizeof(无符号)*len);
对于(尺寸i=len;i--;)
{
无符号x=值[i]+(s[i]-'0')*n;
s[i]=“0”+x%10;
如果(i)值[i-1]+=x/10;
}
}
无效事实(字符*s,大小长度,无符号n)
{
memset(s,'0',len-1);
s[len-1]=“1”;
对于(;n>1;--n)multd(s,len,n);
}
内部主(空)
{
无符号n=100;
尺寸长度=天花板(log10l(t加仑(n+1));
字符dstr[len+1];
dstr[len]=0;
factd(dstr,len,n);
看跌期权(dstr);
}

每个人都在告诉你正确答案,但还有几点需要进一步说明

  • 您最初使用double获得更大范围的想法不起作用,因为double无法精确存储此数据。它可以进行计算,但需要大量舍入。这就是bigint库存在的原因

  • 我知道这可能是一个来自教程或示例网站的示例,但在某些情况下,执行无限递归会让你感到痛苦。你有一个递归解决方案,本质上是一个迭代过程。当你尝试使用更大的值运行程序时(尝试10000),你会理解为什么这个网站会被命名为它

  • 一种简单的迭代方法如下

      int answer, idx;
    
      for (answer = 1, idx = 1; idx <= no_of_inputs; idx++ ) {
        answer = answer * idx;
      }
      printf("Factorial of %3d =  %d\n", no_of_inputs, answer);
    
    int-answer,idx;
    
    对于(answer=1,idx=1;idx如果您只想使用标准数据类型,而不需要精确的答案,那么计算n!的对数,而不是n!本身。n!的对数很容易用
    双精度
    (除非n很大)

    不要使用递归算法,我认为它是超慢的,即使它被缓存,也会很慢。这只是你应该考虑的事情。

    这是因为当您调用事实(100)时,实际上并没有运行它100次,而是实际运行该函数5050次。这是不好的,如果缓存它,那么它可能会运行100次,但是,使用if语句运行函数调用然后运行循环的速度仍然较慢

    double print_solution(int n)
    {
        double rval = 1;
        unsigned int i;
    
        for( i = 1; i <= n; i++ ) {
            rval *= i;
        }
    
        return rval;
    
    }
    
    双打印解决方案(int n)
    {
    双rval=1;
    无符号整数i;
    
    对于(i=1;i要近似计算大数的阶乘,可以采用以下方法:

    n! = n * (n-1)! so log(n!) = log(n) + log(n-1!) n!=n*(n-1)! 所以log(n!)=log(n)+log(n-1!) 现在,您可以使用动态编程来计算log(n!)并计算

    n!as(base)^(log value)

    除了其他人的建议外,我建议您熟悉您实际使用的任何计算机/平台的基本类型(int、long、long等)的存储限制。(“如果有疑问,请打印更多信息!”)

    早期的一张海报提到80位精度限制,但这是x86 CPU所特有的


    另一个人多次引用ISO C90,尽管C99是最新的标准;即使许多编译器还没有完全实现C99,你可能会发现它们很可能至少支持long-long,这应该对应于>=64位精度。

    这是我几年前为解决谷歌之谜而做的,它是ses GMP图书馆:

    #包括
    #包括“gmp.h”
    无效事实(mpz_t r,int n){
    无符号整数i;
    
    #include <stdio.h>
    #include "gmp.h"
    
    void fact(mpz_t r,int n){
        unsigned int i;
        mpz_t temp;
        mpz_init(temp);
        mpz_set_ui(r,1);
        for(i=1;i<=n;i++){
            mpz_set_ui(temp,i);
            mpz_mul(r,r,temp);
        }
        mpz_clear(temp);
    }
    int main(void) {
        mpz_t r;
        mpz_init(r);
        fact(r,188315);
        /* fact(r,100); */
        gmp_printf("%Zd\n",r);
        mpz_clear(r);
        return(0);
    }
    
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    static char *strfact_mult(char *s, unsigned x) {
      unsigned sum = 0;
      size_t len = strlen(s);
      size_t i = len;
      while (i > 0) {
        sum += (s[--i] - '0') * x;
        s[i] = sum % 10 + '0';
        sum /= 10;
      }
      while (sum) {
        len++;
        memmove(&s[1], s, len);
        s[i] = sum % 10 + '0';
        sum /= 10;
      }
      return s;
    }
    
    char *str_fact(char *dest, unsigned n) {
      strcpy(dest, "1");
      while (n > 1) {
        strfact_mult(dest, n--);
      }
      return dest;
    }
    
    void test_fact(unsigned n) { 
      char s[1000];
      printf("%3u %s\n", n, str_fact(s, n));
    }
    
    int main(void) {
      test_fact(0);
      test_fact(4);
      test_fact(54);
      test_fact(100);
    }
    
      0 1
      4 24
     54 230843697339241380472092742683027581083278564571807941132288000000000000
    100 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
    
    #include <stdio.h>
    void factorial(int b){
        int temp = 0, r, size = 0, x;
        int arr[200] = {0};
        int l_b = b-1;
        while(b>0){
            r = b%10;
            arr[size++] = r;
            b = b/10;
        }
        while(l_b >= 2){
            int i=0;
            while(size>0){
             x = arr[i]*l_b+temp ;
             arr[i++] = x%10;
             temp = x/10;
             size--;
            }
           while(temp>0){
              arr[i++] = temp%10;
              temp = temp/10;
           }
          size = i;  --l_b;
        }
        for(int k=size-1;k>=0;k--)
            printf("%d",arr[k]);//ok i'm taking space here
        printf("\n");
    }
    int main(void) {
        // your code goes here
        int fact;
    
        scanf("%d\n",&fact);
        factorial(fact); 
    
        return 0;
    }
    
    #!/usr/bin/bc -l
    
    define f(n) {
       auto r, i
       r = 1
       for (i = 1; i <= n; i++)
       {
           r *= i;
           print "n = ", i, ", log2 = ", l(r)/l(2), ", n! = ", r, "\n"
       }
    }
    
    f(35)
    quit
    
    # Key values
    # n =  1, log2 =   0.00000000000000000000, n! = 1
    # n =  2, log2 =   1.00000000000000000000, n! = 2
    # n =  3, log2 =   2.58496250072115618147, n! = 6
    # n =  4, log2 =   4.58496250072115618149, n! = 24
    # n =  5, log2 =   6.90689059560851852938, n! = 120
    # n =  6, log2 =   9.49185309632967471087, n! = 720
    # n =  7, log2 =  12.29920801838727881834, n! = 5040
    # n =  8, log2 =  15.29920801838727881836, n! = 40320
    # n =  9, log2 =  18.46913301982959118130, n! = 362880
    # n = 10, log2 =  21.79106111471695352921, n! = 3628800
    # n = 11, log2 =  25.25049273335425078544, n! = 39916800
    # n = 12, log2 =  28.83545523407540696694, n! = 479001600
    # n = 13, log2 =  32.53589495221649912738, n! = 6227020800
    # n = 14, log2 =  36.34324987427410323486, n! = 87178291200
    # n = 15, log2 =  40.25014046988262176421, n! = 1307674368000
    # n = 16, log2 =  44.25014046988262176426, n! = 20922789888000
    # n = 17, log2 =  48.33760331113296117256, n! = 355687428096000
    # n = 18, log2 =  52.50752831257527353551, n! = 6402373705728000
    # n = 19, log2 =  56.75545582601885902935, n! = 121645100408832000
    # n = 20, log2 =  61.07738392090622137726, n! = 2432902008176640000
    # n = 21, log2 =  65.46970134368498166621, n! = 51090942171709440000
    # ...
    # n = 34, log2 = 127.79512061296909618950, n! = 295232799039604140847618609643520000000
    # n = 35, log2 = 132.92440362991406264487, n! = 10333147966386144929666651337523200000000
    # ...
    # n = 57, log2 = 254.48541573017643505939
    # n = 58, log2 = 260.34339672530400718017
    # ...
    # n = 98, log2 = 511.49178048020535201128
    # n = 99, log2 = 518.12113710028496163045
    # n = 100, log2 = 524.76499329005968632625