阶乘之溢出解决——Java实现

目录

写在前面

溢出原因分析

溢出解决方案

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;
    }
}


参考:http://t.csdn.cn/pxHyP

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值