c语言-大数阶乘

大数阶乘的由来

    一个正整数的阶乘(Factorial)是所有小于及等于该数的正整数,并且0的阶乘为1。自然数n的阶乘写作n!。亦即n!=1×2×3×...×(n-1)×n。阶乘亦可以递归方式定义:0!=1,n!=(n-1)!×n。 但是在求解数字较大的阶乘时,由于阶乘累乘的性质,导致结果过大,在C语言中,哪怕是double和Longlong都无法储存过多的数位,而解决这个问题的办法,最简单的就是由数组来储存。

大致思路

  由于是超过了一个定义变量的最大范围,所以使用数组解决,毕竟C语言中 int的范围为-(2的31次方-1)到(2的31次方-1),数字为-2 147 483 647~2 147 483 647。double的表示范围为+1.111111111111111111111*2^1023(1.后面52个1)为1.7*10^308,看起来double型应该够了的,可是,精度却并不高,数值精度只有 15~16 位,就是说,一个 308 位长的浮点数,只有前 15~16 位的精度是可信和有保证的,超出部分就没有精度可言了,而且,越靠后,就越不靠谱。而数组申请一个过万的空间轻轻松松,每个空间再存int或double型数据,加在一起可以输出的空间就可想而知了。

  用数组解决该问题,简单来说就是就是将大数的各个位置用取余取出放在数组的连续空间上,然后逆序输出。而其中需要注意的就是化繁为简,分解成多个子问题进行计算(是不是有些耳熟),即在已经分布好位置的n-1的数位上进行下一次的阶乘,保证了每位相乘都是一个很小的数字去乘下一个阶乘数。运用双循环分别进行乘数的移动和每位数字与此时乘数的计算,最后输出。下面是代码:

 1 #define N 10001
 2 int a[N];
 3 double arr[N];
 4 
 5 void  factorial1(int n)
 6 {
 7     a[0] = 1;
 8     int len = 1;
 9     int tmp, next;
10     for (int i = 1; i <= n; i++)
11     {
12         next = 0;
13         for (int j = 0; j < len; j++)
14         {
15             tmp = a[j] * i+next; //当前阶乘值
16             a[j] = tmp % 10; //当前位置仅保留阶乘值的最后一位
17             next = tmp / 10; //保存大于10的余下阶乘值,进行下一次存储
18             if (j >= len - 1 && next > 0) //动态增加数组长度
19             {
20                 len++;          //当数组在最后一位且存在next值时,就增加一个长度进行下一次存储。
21             }
22         }
23     }
24     for (int i = len-1; i >=0 ; i--)//倒叙输出数组
25     {
26         printf("%d",a[i]);
27         if (i % 5 == 0&&i!=0)//分组输出,每组5个
28             printf(",");
29     }
30 }

  笔者前段时间刚结束对滚动数组和动态规划的浅略总结,所以在开始便想用动态规划解决,所以分析了一下最优化原理和无后效原则。

  • 和斐波那契数列相同,因为相乘的数是固定且没有其它因素影响的,所以符合最优化原理
  • 前面的数列由于是固定数值,无法改变影响后面结果(比如乘到某个数时,前面的一个值要进行改变,导致该值后面一系列的数据都进行改变),所以符合无后效原则。

因此理论上来说是可以DP的,简单来想,普通阶乘可以DP吗?当然可以(毕竟百度就能看见)。但如果这里我们直接简单的计算并输出的话,数字过大时就会出现结果不准确,即:

 1 void factorial2(int n)
 2 {
 3     arr[1] = 1;
 4     arr[0] = 1;
 5     for (int i = 2; i <= n; i++)
 6     {
 7         arr[i] = arr[i - 1] * i;
 8     }
 9     printf("%lf\n", arr[n]);
10 }

  在main函数中使用

int main()
{
    int n;//阶乘位数
    double a;
    scanf("%d",&n);
    factorial1(n);
    printf("\n");
    printf("double:\n");
    factorial2(n);
    return 0;
}

  输出结果:

 

   可以看到 factorial2 函数输出的内容是个 inf,而 factorial1 使用的数组输出则完美的输出了内容。而可不可以使用DP来解决大数的问题,笔者愚钝,只是觉得行,在这里未能解决。还请各位大佬看官如有想法,多多指教。

By the way,学个新单词。

 

 

posted @ 2022-10-13 20:32  000100110111  阅读(950)  评论(0编辑  收藏  举报