目录
写在前面
溢出原因分析
溢出解决方案
1.使用Java封装的大整数类解决
2.使用数组存储每一位解决
3.使用可变数组解决数组越界实现不溢出阶乘
(1)为什么使用ArrayList
(2)ArrayList代码实现
4.使用Map解决
(1)使用Map的原因
(2)Map代码实现
完整代码
写在前面
在一次面试中,面试官让我按照心目中好的工程代码的规范写出阶乘,在答题过程中出现了溢出问题,写此文章记录一下解决办法。
先看一下最开始的代码
//普通求阶乘,会出现溢出
public static Long factorial1(int n) {
if (n < 0) {
return -1l;
}
Long res = 1l;
if (n == 0) {
return res;
}
for (int i = 1; i <= n; i++) {
res *= i;
}
return res;
}
溢出原因分析
最开始我所采用的返回值类型是int出现了溢出,后来使用Long类型依然出现了,归根结底是int和Long类型的存储范围有限。
int 4个字节存储,范围为[-2^31 ~ 2^31-1] ,即[-2147483648 ~ 2147483647]
Long 8个字节存储,范围为[-2^63 ~ 2^63-1]
存储范围有限,故而在阶乘运算时会出现溢出
溢出解决方案
1.使用Java封装的大整数类解决
代码比较简单直接贴下面
//使用Java封装大整数去计算
public static BigInteger factorial3(int n) {
if (n < 0) {
return BigInteger.valueOf(-1);
}
BigInteger res = BigInteger.valueOf(1);
if (n == 0) {
return res;
}
for (int i = 1; i <= n; i++) {
res = res.multiply(BigInteger.valueOf(i));//大整数乘法
}
return res;
}
2.使用数组存储每一位解决
使用数组去存储结果的每一位
(1)对于0和1的阶乘进行单独处理
(2)进行阶乘时,将每一位与接下来的数进行相乘,即将数组的每一个元素将后一个数相乘。举例说:求5!时,数组里先存放4!的结果即a[0] = 4,a[1] = 2,将每一个与5相乘,得到a[0] = 20,a[1] = 10,进位后得120,所以此时涉及一个最核心的地方进位处理。
(3)处理进位。分为:最高位和非最高位两部分处理
非最高位:直接对其进行模10作为当前值,除以10即为需要进位的值,加上上一位的数值,即为进位后的数值
最高位:如果按非最高位处理则可能出现仍为多位数的情况,所以只要除以10后结果不为零,就对其循环非最高位处理。
(4)返回为String,方便打印结果
代码如下:
//阶乘
//使用数组用于存放结果,防止溢出,但可能越界
public static String factorial2(int n){
if(n<0){
return "-1";
}
//创建数组保存结果
int[] array = new int[1000];
array[0] = 1;
if(n==-0||n==1){
return "1";
}
//用于临时存放进位的数
int carry = 0;
//用于保存结果的位数
int bit = 1;
String str ="";
//从2进行阶乘
for (int i = 2; i <= n; i++) {
//对每一位进行乘法操作
for (int j = 0; j < bit; j++) {
array[j] = array[j]*i;
}
//处理除最高位以外的进行情况
for (int j = 0; j < bit - 1; j++) {
carry = array[j] /10;
array[j] %=10;
array[j+1] += carry;
}
//对最高位进行进位处理
if(array[bit - 1]>=10){
int temp = array[bit - 1] /10;
array[bit - 1] %=10;
array[bit] +=temp;
while (temp!=0){
array[bit] =temp%10;
temp /=10;
bit++;
}
}
}
// for (int m = bit - 1; m >= 0; m--) {
// System.out.print(array[m]);
// }
for (int m = bit - 1; m >= 0; m--) {
str += array[m];
}
return str;
}
3.使用可变数组解决数组越界实现不溢出阶乘
(1)为什么使用ArrayList
在第二种解决方案中,能够计算阶乘的大小取决于我们所开辟的空间大小,空间开辟太小则计算的n有限,否则则会报“ArrayIndexOutOfBoundsException”,但空间开辟很大,容易造成空间的浪费。
(2)ArrayList代码实现
//阶乘
//使用可变数组用于存放结果解决数组越界,防止溢出,
public static String factorial4(int n){
if(n<0){
return "-1";
}
//创建数组保存结果
ArrayList<Integer> array = new ArrayList<>();
array.add(1);
if(n==-0||n==1){
return "1";
}
//用于临时存放进位的数
int carry = 0;
//用于保存结果的位数
int bit = 1;
String str ="";
//从2进行阶乘
for (int i = 2; i <= n; i++) {
//对每一位进行乘法操作
for (int j = 0; j < bit; j++) {
array.set(j,array.get(j)*i);
}
//处理除最高位以外的进行情况
for (int j = 0; j < bit - 1; j++) {
carry = array.get(j) /10;
array.set(j,array.get(j)%10);
array.set(j+1,array.get(j+1)+carry);
}
//对最高位进行进位处理
if(array.get(bit-1)>=10){
int temp = array.get(bit-1) /10;
array.set(bit-1,array.get(bit-1)%10);
array.add(bit,temp);
while (temp!=0){
array.add(bit,temp%10);
temp /=10;
bit++;
}
}
}
// for (int m = bit - 1; m >= 0; m--) {
// System.out.print(array[m]);
// }
for (int m = bit - 1; m >= 0; m--) {
str += array.get(m);
}
return str;
}
4.使用Map解决
(1)使用Map的原因
在使用ArrayList时,对于数组值的更新时需要使用add和set方法进行配合使用,而在Map中对值的更新有着极大的优势,因为Map是<key,value>存储结构,而Map的put的方法是集插入和更新一体的方法,只需要这一个方法就可以完成插入和更新:对已存在的key执行更新,对未存在的key进行插入。
(2)Map代码实现
//阶乘
//使用Map用于存放结果,防止溢出
public static String factorial5(int n){
if(n<0){
return "-1";
}
//创建数组保存结果
Map<Integer,Integer> map = new HashMap<>();
map.put(0,1);
//用于临时存放进位的数
int carry = 0;
//用于保存结果的位数
int bit = 1;
String str ="";
//从2进行阶乘
for (int i = 2; i <= n; i++) {
//对每一位进行乘法操作
for (int j = 0; j < bit; j++) {
map.put(j,map.get(j)*i);
}
//处理除最高位以外的进行情况
for (int j = 0; j < bit - 1; j++) {
carry = map.get(j) /10;
map.put(j, map.get(j) %10);
map.put(j+1, map.get(j+1) +carry);
}
//对最高位进行进位处理
if(map.get(bit - 1)>=10){
int temp = map.get(bit - 1) /10;
map.put(bit-1,map.get(bit - 1) %10);
map.put(bit,temp);
while (temp!=0){
map.put(bit,temp%10);
temp /=10;
bit++;
}
}
}
// for (int m = bit - 1; m >= 0; m--) {
// System.out.print(array[m]);
// }
for (int m = bit - 1; m >= 0; m--) {
str += map.get(m);
}
return str;
}
完整代码
import java.math.BigInteger;
import java.util.*;
public class Main {
public static void main(String[] args) {
System.out.println(factorial1(50));
System.out.println(factorial2(5000));
System.out.println(factorial3(5));
System.out.println(factorial4(5000));
System.out.println(factorial5(5000));
}
//普通求阶乘,会出现溢出
public static Long factorial1(int n) {
if (n < 0) {
return -1l;
}
Long res = 1l;
if (n == 0) {
return res;
}
for (int i = 1; i <= n; i++) {
res *= i;
}
return res;
}
//使用Java封装大整数去计算
public static BigInteger factorial2(int n) {
if (n < 0) {
return BigInteger.valueOf(-1);
}
BigInteger res = BigInteger.valueOf(1);
if (n == 0) {
return res;
}
for (int i = 1; i <= n; i++) {
res = res.multiply(BigInteger.valueOf(i));
}
return res;
}
//阶乘
//使用数组用于存放结果,防止溢出,但可能越界
public static String factorial3(int n){
//创建数组保存结果
int[] array = new int[1000];
array[0] = 1;
if(n==-0||n==1){
return "1";
}
//用于临时存放进位的数
int carry = 0;
//用于保存结果的位数
int bit = 1;
String str ="";
//从2进行阶乘
for (int i = 2; i <= n; i++) {
//对每一位进行乘法操作
for (int j = 0; j < bit; j++) {
array[j] = array[j]*i;
}
//处理除最高位以外的进行情况
for (int j = 0; j < bit - 1; j++) {
carry = array[j] /10;
array[j] %=10;
array[j+1] += carry;
}
//对最高位进行进位处理
if(array[bit - 1]>=10){
int temp = array[bit - 1] /10;
array[bit - 1] %=10;
array[bit] =temp;
while (temp!=0){
array[bit] =temp%10;
temp /=10;
bit++;
}
}
}
// for (int m = bit - 1; m >= 0; m--) {
// System.out.print(array[m]);
// }
for (int m = bit - 1; m >= 0; m--) {
str += array[m];
}
return str;
}
//阶乘
//使用可变数组用于存放结果解决数组越界,防止溢出,
public static String factorial4(int n){
//创建数组保存结果
ArrayList<Integer> array = new ArrayList<>();
array.add(1);
if(n==-0||n==1){
return "1";
}
//用于临时存放进位的数
int carry = 0;
//用于保存结果的位数
int bit = 1;
String str ="";
//从2进行阶乘
for (int i = 2; i <= n; i++) {
//对每一位进行乘法操作
for (int j = 0; j < bit; j++) {
array.set(j,array.get(j)*i);
}
//处理除最高位以外的进行情况
for (int j = 0; j < bit - 1; j++) {
carry = array.get(j) /10;
array.set(j,array.get(j)%10);
array.set(j+1,array.get(j+1)+carry);
}
//对最高位进行进位处理
if(array.get(bit-1)>=10){
int temp = array.get(bit-1) /10;
array.set(bit-1,array.get(bit-1)%10);
array.add(bit,temp);
while (temp!=0){
array.add(bit,temp%10);
temp /=10;
bit++;
}
}
}
// for (int m = bit - 1; m >= 0; m--) {
// System.out.print(array[m]);
// }
for (int m = bit - 1; m >= 0; m--) {
str += array.get(m);
}
return str;
}
//阶乘
//使用Map用于存放结果,防止溢出
public static String factorial5(int n){
if(n<0){
return "-1";
}
//创建数组保存结果
Map<Integer,Integer> map = new HashMap<>();
map.put(0,1);
//用于临时存放进位的数
int carry = 0;
//用于保存结果的位数
int bit = 1;
String str ="";
//从2进行阶乘
for (int i = 2; i <= n; i++) {
//对每一位进行乘法操作
for (int j = 0; j < bit; j++) {
map.put(j,map.get(j)*i);
}
//处理除最高位以外的进行情况
for (int j = 0; j < bit - 1; j++) {
carry = map.get(j) /10;
map.put(j, map.get(j) %10);
map.put(j+1, map.get(j+1) +carry);
}
//对最高位进行进位处理
if(map.get(bit - 1)>=10){
int temp = map.get(bit - 1) /10;
map.put(bit-1,map.get(bit - 1) %10);
map.put(bit,temp);
while (temp!=0){
map.put(bit,temp%10);
temp /=10;
bit++;
}
}
}
// for (int m = bit - 1; m >= 0; m--) {
// System.out.print(array[m]);
// }
for (int m = bit - 1; m >= 0; m--) {
str += map.get(m);
}
return str;
}
}