大数阶乘(数组处理)

1.问题描述:

当控制台输入比较大的数字的时候求出n的阶乘,并且 1<= n <= 5000,请在控制台输出n!的结果

2. 思路分析:

① 首先从题目中可以看到n是比较大的数字,我们知道当n小于20的时候这个阶乘已经非常大了,当再进行计算比20大的时候可能Long的数据类型已经无法保存其中的结果了,Long类型的数据最大能够保存的数字为9223372036854775807,当超过这个数字的时候就会出现溢出的情况,所以不能够使用基本的数据类型来保存阶乘的结果,否则会出现溢出的情况

② 基于上面的出现的问题,我们经常对于比较大的数字的时候将其结果的每一位保存在一个整型数组中,这样就可以避免数字太大出现溢出的情况

③ 使用数组来进行保存,首先要解决的问题就是我们需要将数组的长度声明为多大呢?可以先对其进行预判这样在声明数组的时候就不会造成数组空间上很大的浪费,所以需要分析一下

比如题目中给出了n最大的时候为5000,我们不妨从简单一点的例子开始看一下能不能找出其中的规律,当5000 * 5000的·时候结果为25000000,八位结果,当5000 * 5000 * 5000的时候结果为125000000000,结果为12位,当再乘以5000的时候那么结果为15位...所以在极端的情况下我们的位数为5000 * 4(每一次乘以一个5000那么位数多4)这样5000的阶乘可以声明20000长度的数组,而且这个长度是大于我们最好求得的结果的

所以对于求解n !声明的数组的长度为n * 4

④ 但是有了数组之后怎么样将计算结果保存起来呢?我们知道n ! = (n - 1)! * n,所以我们可以在循环中先计算出(n - 1)!然后再乘以n,将每一位的结果分散在数组中,例如下面的例子将上一次每一位分散在数组中的结果分别乘以当前的n,因为上一次我们是把n - 1的阶乘计算出来并且分散在了数组之中的,所以这一次的时候需要将数组中的每一位乘以当前的系数n,这样才可以得到n的阶乘,而且每一次位于系数n相乘的时候结果可能大于了10,我们需要对其进行处理,进行取模运算得到当前的位上的结果,比如之前计算出3的阶乘那么arr[0] = 6那么下一次进行计算的时候发现6 * 4发现大于了零这个时候取余操作arr[0] = 4

这里非常容易出现的错误是直接将进位加到更高的一位上,这样是不对的,我之前在写的时候也犯过这个错误导致调试了很久,正确的做法是将上一次的进位结果使用一个临时的变量进行保存,因为我们在当前这一次相乘的时候当前位之前是不能够有变化的,每一位乘以n的时候都需要把先将当前这一位与n相乘然后再将上一次的进位加进来,假如上一次的时候就把进位直接加到更高的以为那么在下一次进行计算的时候当前位的结果就改变了,所以不对

比如 4! * 5 = 24 * 5,arr[0] = 4,arr[1] = 2, 4 * 5 = 20 产生进位,而下一次相乘的应该是2 * 5再加上进位,假如把4 * 5的进位加进来那么下一次酒不是4 * 5了而是6 * 5这样就错了,这个是特别要注意的一个点

⑤ 解决了进位的问题那么当前结果的长度怎么样进行计算呢?简单分析一下,对于上一次的结果,我们与当前的系数n相乘之后的位数取决于最高位上的最后的结果是多少,因为上一次结果的倒数第二位假如是9,与5000进行相乘那么产生的进位结果产生了三位,所以这一次的结果的长度取决于上一次结果的最高位与n相乘加上进位之后的长度,最后结果的长度是上一次的长度 - 1加上最后一个数组与n相乘结果分散在数组中的长度

所以第二层循环我们只需要循环到p - 1,对p这一位我们进行额外的处理得到当前的结果的位数

⑥ 所以需要使用一个变量p来保存上一次结果的长度这样在计算这一次的阶乘的时候就知道循环计算到的位置在哪里

⑦ 对于Int类型的数组,最大能够保存的整数位2147483647,而最高位是9 * 5000不超过这个数字,所以整型数组能够表示保存其中的中间结果

最终可以输出5000阶乘的长度,可以知道长度为16326远小于20000的长度

3. 代码如下:

import java.util.Scanner;
public class Main {
	static int arr[];
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		arr = new int[n * 4];
		arr[0] = 1;
		int p = 0;
		for(int i = 1; i <= n; i++){
			int t = 0;
			for(int j = 0; j < p; j++){
				arr[j] = arr[j] * i + t;
				t = 0;
				if(arr[j] >= 10){
					t = arr[j] / 10;
					arr[j] = arr[j] % 10;
				}
			}
			int cur = arr[p] * i + t;
			while(cur >= 10){
				arr[p++] = cur % 10;
				cur /= 10;
			}
			arr[p] = cur;
		}
		for(int k = p; k >= 0; k--){
			System.out.print(arr[k]);
		}
		System.out.print("\n");
		sc.close();
	}
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值