【问题标题】:Recursive factorial function program returns 0 for large number input in C递归阶乘函数程序为 C 中的大量输入返回 0
【发布时间】:2020-05-19 07:57:33
【问题描述】:

我编写了使用递归进行阶乘计算的程序,正如课堂上所教的那样。我观察到这一点:大于 65 的数字的阶乘始终给出输出为 0。

//program to recursively calculate the value of factorial for a given integer!

#include <stdio.h>
#include <stdint.h>

unsigned long rfacta(uint32_t n){
    if(n>0)
        return rfacta(n-1)*n;
    else
        return 1;
}

unsigned long rfactd(uint32_t n){
    if(n>0)
        return n*rfactd(n-1);
    else
        return 1;
}

int main(void){
    uint32_t number, methd;
    unsigned long fctorial;
    printf(" Enter 0 for call-time recursion\n Enter 1 for return-time recursion\n Enter 2 to print the table of passible factorials on this system\n\n");
    scanf("%d",&methd);
    switch(methd){
    case 0:
        printf("Compute factorial for number = ? \n");
        scanf("%d",&number);
        fctorial = rfacta(number);
        printf("Factorial(%d) = %lu\n",number,fctorial);
    break;

    case 1:
        printf("Compute factorial for number = ? \n");
        scanf("%d",&number);
        fctorial = rfactd(number);
        printf("Factorial(%d) = %lu\n",number,fctorial);
    break;

    case 2:
      for(int i=0;rfacta(i)!=0;i++){
        printf("Factorial(%d) = %lu\n",i,rfacta(i));
      }
      break;

    default:
      printf("Incorrect entry! \n");
      break; 
    }
    return 0;
}

output Screenshot attachment: Recursion program for factorial returning 0

问题:

正如我们在输出中看到的[附上截图图像],有三个区域:

区域 1:在该区域中,阶乘的输出是正确的。原因是,

log2(20!) ~= 62, [这里 logm(n) 表示以 m 为基数的 n 的对数] 即假设一台 64 位机器,那么在单个地址可以正确表示的最大阶乘数是 20 !,然后发生内存溢出,因为 log2(21!) ~= 65。因此,这么大的数字不能用 64 位地址表示。

区域 2:在该区域中,阶乘的值非零但不正确。其原因,如上所述,是因为阶乘整数太大,无法用单个 64 位地址表示。因此,我们看到的函数输出实际上是随机的 64 位长无符号整数。

区域 3:对于阶乘 (n > 65),阶乘 (n)=0。

我无法理解为什么 n>65, n! = 0 由程序返回?

据我了解,区域 2 必须继续进行而不是停止。但是它一直在我在 n=65 处尝试过我的代码的所有机器上停止,这让我感到困惑。提前感谢您的帮助。

【问题讨论】:

    标签: c recursion


    【解决方案1】:

    如果您以十六进制打印值,发生的事情会变得更加明显:

    Factorial(0) = 0000000000000001
    Factorial(1) = 0000000000000001
    Factorial(2) = 0000000000000002
    Factorial(3) = 0000000000000006
    Factorial(4) = 0000000000000018
    Factorial(5) = 0000000000000078
    Factorial(6) = 00000000000002d0
    Factorial(7) = 00000000000013b0
    Factorial(8) = 0000000000009d80
    Factorial(9) = 0000000000058980
    Factorial(10) = 0000000000375f00
    Factorial(11) = 0000000002611500
    Factorial(12) = 000000001c8cfc00
    Factorial(13) = 000000017328cc00
    Factorial(14) = 000000144c3b2800
    Factorial(15) = 0000013077775800
    Factorial(16) = 0000130777758000
    Factorial(17) = 0001437eeecd8000
    Factorial(18) = 0016beecca730000
    Factorial(19) = 01b02b9306890000
    Factorial(20) = 21c3677c82b40000
    Factorial(21) = c5077d36b8c40000
    Factorial(22) = eea4c2b3e0d80000
    Factorial(23) = 70cd7e2933680000
    Factorial(24) = 9343d3dcd1c00000
    Factorial(25) = 619fb0907bc00000
    Factorial(26) = ea37eeac91800000
    Factorial(27) = b3e62c3358800000
    Factorial(28) = ad2cd59dae000000
    Factorial(29) = 9e1432dcb6000000
    Factorial(30) = 865df5dd54000000
    Factorial(31) = 4560c5cd2c000000
    Factorial(32) = ac18b9a580000000
    Factorial(33) = 2f2fee5580000000
    Factorial(34) = 445da75b00000000
    Factorial(35) = 58cde17100000000
    Factorial(36) = 7cf3b3e400000000
    Factorial(37) = 0f38fff400000000
    Factorial(38) = 4275fe3800000000
    Factorial(39) = 1ff9ba8800000000
    Factorial(40) = ff05254000000000
    Factorial(41) = d7d2f74000000000
    Factorial(42) = 689c908000000000
    Factorial(43) = 924c458000000000
    Factorial(44) = 251bf20000000000
    Factorial(45) = 85e98a0000000000
    Factorial(46) = 0ff6cc0000000000
    Factorial(47) = ee4f740000000000
    Factorial(48) = aee5c00000000000
    Factorial(49) = 79f9c00000000000
    Factorial(50) = d2c7800000000000
    Factorial(51) = fdbe800000000000
    Factorial(52) = 8ab2000000000000
    Factorial(53) = b6da000000000000
    Factorial(54) = 91fc000000000000
    Factorial(55) = 5d24000000000000
    Factorial(56) = 5fe0000000000000
    Factorial(57) = 58e0000000000000
    Factorial(58) = 22c0000000000000
    Factorial(59) = 0240000000000000
    Factorial(60) = 8700000000000000
    Factorial(61) = 2b00000000000000
    Factorial(62) = 6a00000000000000
    Factorial(63) = 1600000000000000
    Factorial(64) = 8000000000000000
    Factorial(65) = 8000000000000000
    

    当您继续乘以包含 2 作为因子的值时,尾随零的数量会继续增加。当你乘以 66 时,所有非零位都被推到左边,所以你只剩下 0。

    另外,从 21 开始的值!到65!实际上不是随机值,而是结果的 低 64 位。无符号整数运算以 2bitlen 为模执行,其中“bitlen”是所讨论类型的位长度,在本例中为 64。

    【讨论】:

    • 这个问题的维度真正揭示了幕后发生的事情。万分感谢。这很有帮助。
    【解决方案2】:

    只要你乘以二进制补码并换行(C 标准不保证,但似乎在你的程序中发生),计算结果就是完整的数学结果(没有换行)模 264 .

    对于 n > 65,n!是 264 的倍数(从 1 到 66 的因子中包含了 64 个或更多的二),所以模 264 的值为零:

    • 当您乘以 2 时,将提供 2 的一个因数。
    • 当您乘以 4 时,会提供两个因数。
    • 当您乘以 6 时,将提供一个因子。
    • 乘以 8 时,会提供三个因子。
    • n 中二的因数总数!是 n/2 + n/4 + n/8 + …,使用整数运算(即截断除法,所以 3/ 2 = 1) 并一直持续到项达到零。对于 66!,这是 66/2 + 66/4 + 66/8 + 66/16 + 66/32 + 66/64 = 33 + 16 + 8 + 4 + 2 + 1 = 64。

    【讨论】:

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