IT评测·应用市场-qidao123.com技术社区
标题:
c语言-大数阶乘
[打印本页]
作者:
东湖之滨
时间:
2022-10-13 20:07
标题:
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吗?当然可以(毕竟百度就能看见)。但如果这里我们直接简单的计算并输出的话,数字过大时就会出现结果不准确,即:
[code] 1 void factorial2(int n) 2 { 3 arr[1] = 1; 4 arr[0] = 1; 5 for (int i = 2; i
欢迎光临 IT评测·应用市场-qidao123.com技术社区 (https://dis.qidao123.com/)
Powered by Discuz! X3.4