目录
一、大数的概念
首先我们要知道计算机语言中的数据类型是有范围的,比如在C语言中我们可以看到 unsigned int 和 unsigned long long 的最大值,下面代码会给出相关信息。
#include<stdio.h>
#include<limits.h>
int main(){
unsigned int num=10;
printf("unsigned int 字节数:%-3d\t\t",sizeof(num));
printf("unsigned int 最大值:%u \n",UINT_MAX);
unsigned long long num2=20;
printf("unsigned long long 字节数:%d ",sizeof(num2));
printf("unsigned long long 最大值:%llu",ULLONG_MAX);
return 0;
}
由上述信息我们可以看到unsigned long long 最大值为20位,那么一个数的阶乘会很大,比如说是30的阶乘265252859812191058636308480000000 ,远超于20位。更不要说1000的阶乘。
二、问题分析
既然常规数据类型保存不了这么大的值,我们可以这样思考,不存储一个数的数值大小,转而存储每一位的值。
那么存储到哪里呢?答案是数组,我么可以定义一个较大的数组,来存放一个数阶乘的每一位。
有了这个思想,我们再来模拟一下真实的情况;
- 开始需要输入一个待求阶乘的数N,我们按照1X2X3······N这样的顺序求阶乘,首先需要一个外层循环,控制是否到N。
- 如何得到中间状态的某一阶乘呢? 让当前值的每一位乘以下一个数,以此类推,比如 :
3.这样解决了中间某个阶乘的问题,还有就是2X6已经大于10,如果直接输出是不对的,十进制的数每一位不能超过十,我们应该向后进位,进多少呢?
2X6 /10 =1 即向后进一位
2X6 % 10=2 代表当前位的实际数值
1X6 =6,加上前面的进位1等于7 如此不断重复,直到遍历完当前状态的每一位。
那么6!=0 2 7 ,逆序输出为 720 ,与预期效果一样。
三、代码实现
#include<stdio.h>
#include<string.h>
#define MAX 5000
int main(){
int res[MAX],num;
scanf("%d",&num);
memset(res,0,sizeof(res));//初始化数组全为0
int i,bit=1,j;//bit存储每一步结果的位数
res[0]=1,res[1]=1;
for(i=1;i<=num;i++)
{
for(j=1;j<=bit;j++)
res[j]*=i; //每一位都乘上i
for(j=1;j<=bit;j++)//遍历每一位
{
if(res[j]>9)
{ //如果某一位大于9,那么这一位就要进位,并且还要进位考虑对后面位的影响
for(int r=j;r<=bit;r++)
{
if(res[bit]>9) //如果最高位大于9,那么位数就要加一
bit++;
res[r+1] +=res[r] / 10;
res[r] %=10;
}
}
}
}
printf("%d!= ",num);
for(int k=bit;k>0;k--) //逆序输出
printf("%d",res[k]);
printf("\n");
return 0;
}
四、测试结果
我们求一下1000的阶乘
五 、扩展
上述方法虽说可行,但是嵌套了三层循环,而且随着后面数的增大,肯定要发生很多进位的情况,这会不断增加循环的次数。
那么我们可以思考,与其求出每一位的值之后再循环遍历是否大于9,不如我们直接记录每一步的操作产生的进位,这样也会导致一个问题,即当前的进位不断加到后面的位上,最后一位肯定是个很大的数,我们在退出循环时,还要单独判断。
代码如下:
#include<stdio.h>
#include<string.h>
#define MAX 5000
int main(){
int res[MAX],num;
scanf("%d",&num);
memset(res,0,sizeof(res));//初始化数组全为0
int i,bit=1,j,temp;//bit存储每一步结果的位数,temp存储当前值
res[0]=1,res[1]=1;
for(i=2;i<=num;i++)
{ int up=0;//进位
for(j=1;j<=bit;j++)
{
temp=res[j]*i+up;
res[j]=temp % 10;
up=temp/10;
}
//至此我们还要判断退出循环时up是否为0,即是否还要向后进位
while(up)
{
res[bit+1]=up%10;
up/=10;
bit++;
}
}
printf("%d!= ",num);
for(int k=bit;k>0;k--) //逆序输出
printf("%d",res[k]);
printf("\n");
return 0;
}
测试:
1000的阶乘
六、总结
主要用到了不存储一个数的数值大小,转而存储每一位的值这一思想。这给我们处理大的数据提供了一个思路,我们也可以发现,数组是个很常用但是也不简单的存储数据的结构,当然任何事物都是相对的,比如数组需要预定义容量,很多操作需要我们自己实现,不如C++中 STL ,提供了很多方便操作的容器,但他们或多或少是建立在数组的基础上,我们学好一些底层原理,以后才能有更多的机会去突破自己。