【问题标题】:Writing a factorial function in C用 C 编写阶乘函数
【发布时间】:2021-10-19 07:21:09
【问题描述】:

对于大于 16 的数字,我对阶乘函数的表面实现通常会失败。

#include <stdio.h>

int fact(int x){
  if (x==1)
    return 1;
  else
    return (x* fact(x-1));

}
int main() {
  int x;
  scanf("%d", &x);
  printf("%d\n", fact(x));
  
}

这是否取决于在递归的某个点,整数将无法根据sizeof(int) 表示?

【问题讨论】:

  • 对于大于 16 的值,您期望什么结果?你的机器上整数的大小是多少?答案可能是你是对的,你的整数太小了。
  • 这仅仅是因为fact(16) &gt; MAX_INT, MAX_INT = 2147483647 - fact(16) = 20922789888000(来源:维基百科)
  • C 中的所有类型都有范围限制。在大多数系统上,int 是有符号的 32 位整数类型,范围从大约负二十亿到正二十亿。如果您想要大于 20 亿的数字,那么使用 int 您将有 算术溢出,这会导致 未定义的行为。使用较大的类型,例如uint64_t(这是一个无符号的 64 位整数类型)(如果可用)。
  • @Some 程序员老兄,我相信sizeof(int) 依赖于编译器,通常是平台上最快的整数类型(用于整数运算)...所以如果你的编译器支持64位操作系统,那么它的大小应该是8字节。
  • @WENDYN 拥有 64 位系统并不意味着 int 是 64 位。您还可以拥有 64 位指针和 32 位 int

标签: c factorial


【解决方案1】:

这是否取决于这样一个事实,即在递归的某个点,整数将无法根据 sizeof(int) 表示?

是的。阶乘变得非常非常快,并且将超过N 20的本机整数类型的范围。要计算超出20!的任何东西@你将需要使用任意精度的数学库,例如 GNU GMP:

#include <gmp.h>
...
for ( unsigned int i = 0; i < 100; i++ )
{
  mpz_t result;
  mpz_fac_ui( result, i );
  gmp_printf( "%3u! = %Zd\n", i, result );
}

顺便说一句,虽然阶乘的定义是递归的 - N! = N * (N-1)!, 0! = 1 - 最好使用像

这样的迭代算法来解决
unsigned long fac( unsigned long n )
{
  unsigned long ret = 1;
  while( n )
    ret *= n--;
  return ret;
}

【讨论】:

    【解决方案2】:

    或多或少,但限制不应为 16。

    当整数为 32 位大时,可表示的最高阶乘是

    fact(12) = 479001600 = 0x1c8cfc00
    

    (下一个是fact(13) = 6227020800 = 0x17328cc00

    对于 64 位整数,可表示的最高阶乘是

    fact(20) = 2432902008176640000 = 0x21c3677c82b40000
    

    (下一个是fact(21) = 51090942171709440000 = 0x2c5077d36b8c40000


    16 应该是 AFAIK 不常见的 48 位系统(6 字节)的限制...

    【讨论】:

      【解决方案3】:

      所以在您的平均系统中,int 代表 4 个字节。

      用它表示的最大有符号数是2147483647。 大于阶乘 12 的值超过了这个数字,所以它不能存储在 4 个字节中。

      它一直工作到阶乘 16 的事实可能表明您的 int 代表 6 字节,这种情况很少见。

      您可以选择采用longlong longunsigned 数据类型,这些数据类型通常允许您存储更大的正数。

      【讨论】:

        【解决方案4】:
        #include <stdio.h> 
        
        unsigned int factorial(unsigned int n) 
        { 
            if (n == 0) 
                return 1; 
            return n * factorial(n - 1); 
        } 
        
        int main() 
        { 
            int num = 5; 
            printf("Factorial of %d is %d", 
        
                   num, factorial(num)); 
            return 0; 
        } 
        

        【讨论】:

        • 这如何回答这个问题?
        【解决方案5】:

        正如约翰·博德在他的回答中恰当地说的那样,阶乘变得非常非常快。 这里或多或少是完整的故事:

         1! = 1
         2! = 2
         3! = 6
         4! = 24
         5! = 120
         6! = 720
         7! = 5040                  largest factorial representable as a signed 16-bit int
         8! = 40320                 largest factorial representable as an unsigned 16-bit int
         9! = 362880
        10! = 3628800
        11! = 39916800
        12! = 479001600             largest factorial representable as a 32-bit int
        13! = 6227020800
        14! = 87178291200
        15! = 1307674368000
        16! = 20922789888000
        17! = 355687428096000
        18! = 6402373705728000
        19! = 121645100408832000
        20! = 2432902008176640000   largest factorial representable as a 64-bit int
        21! = 51090942171709440000
        22! = 1124000727777607680000
        23! = 25852016738884976640000
        24! = 620448401733239439360000
        25! = 15511210043330985984000000
        26! = 403291461126605635584000000
        27! = 10888869450418352160768000000
        28! = 304888344611713860501504000000
        29! = 8841761993739701954543616000000
        30! = 265252859812191058636308480000000
        31! = 8222838654177922817725562880000000
        32! = 263130836933693530167218012160000000
        33! = 8683317618811886495518194401280000000
        34! = 295232799039604140847618609643520000000   largest representable in 128 bits
        35! = 10333147966386144929666651337523200000000
        36! = 371993326789901217467999448150835200000000
        37! = 13763753091226345046315979581580902400000000
        38! = 523022617466601111760007224100074291200000000
        39! = 20397882081197443358640281739902897356800000000
        40! = 815915283247897734345611269596115894272000000000
        41! = 33452526613163807108170062053440751665152000000000
        42! = 1405006117752879898543142606244511569936384000000000
        43! = 60415263063373835637355132068513997507264512000000000
        44! = 2658271574788448768043625811014615890319638528000000000
        45! = 119622220865480194561963161495657715064383733760000000000
        46! = 5502622159812088949850305428800254892961651752960000000000
        47! = 258623241511168180642964355153611979969197632389120000000000
        48! = 12413915592536072670862289047373375038521486354677760000000000
        49! = 608281864034267560872252163321295376887552831379210240000000000
        50! = 30414093201713378043612608166064768844377641568960512000000000000
        

        另见http://catb.org/~esr/jargon/html/B/bignum.html

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-05-09
          • 2018-06-08
          • 1970-01-01
          • 2020-02-26
          • 1970-01-01
          • 1970-01-01
          • 2016-05-29
          • 1970-01-01
          相关资源
          最近更新 更多