1.2 大数阶乘问题

目录

一、大数的概念

二、问题分析

三、代码实现

四、测试结果

五 、扩展

六、总结


一、大数的概念

首先我们要知道计算机语言中的数据类型是有范围的,比如在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的阶乘。

 

二、问题分析

既然常规数据类型保存不了这么大的值,我们可以这样思考,不存储一个数的数值大小,转而存储每一位的值。

那么存储到哪里呢?答案是数组,我么可以定义一个较大的数组,来存放一个数阶乘的每一位。

 

有了这个思想,我们再来模拟一下真实的情况; 

  1. 开始需要输入一个待求阶乘的数N,我们按照1X2X3······N这样的顺序求阶乘,首先需要一个外层循环,控制是否到N。
  2. 如何得到中间状态的某一阶乘呢?  让当前值的每一位乘以下一个数,以此类推,比如 :

        

     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  ,提供了很多方便操作的容器,但他们或多或少是建立在数组的基础上,我们学好一些底层原理,以后才能有更多的机会去突破自己。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值