Java实现大数的阶乘(数组存放数据)

我们知道一个数的阶乘是很长大的数据,那么用基本数据类型存放的数据范围有限,就算是Long有8 个字节,也无能为力。那我们自然而然的考虑用数组存放数据。

首先我们先考虑一个数组元素就存放一位数据。

    /*
     * 用整数数组的每一个元素保存每一个数据
     * 但是每一个数组是整形,只保留一位数字造成了空间浪费
     */
    public static void function1(int n){
        if(n<0){
            System.out.println("error input");
        }else{
//10000!大概37000多位,我们用每一个数组元素保存一个位数
//如a[0]保存个位数,a[1]保存百位数,这样倒着存放数据是因为方便我们进位
            long startOfFunction=System.currentTimeMillis();
            int max=40000;
            int[] a=new int[max];
            int carry=0;//carry表示进位
            a[0]=1;
            for(int i=1;i<=n;i++){
                for(int j=0;j<max;j++){
                    a[j]=a[j]*i+carry;
                    carry=a[j]/10;
                //一个数如果乘i大于10,就进位,当前位置只保留个位数,
                    a[j]=a[j]%10;
                    //carry保留进位数,留给数组元素的下一位
                }
            }
            int i=max-1;
            for(;i>=0;i--){
                if(a[i]!=0){
                    break;
                }
            }//这个for循环用于寻找数组中首位数据
            for(int count=0;i>=0;i--,count++){
                if(count==40){
                    System.out.println();//用于控制输出格式
                    count=0;
                }
                System.out.print(a[i]);
            }
        long during=System.currentTimeMillis()-startOfFunction;
        //这个用于测试程序运行的时间
            System.out.println("\n一共经历了"+during+"纳秒");
        }
    }

我们输入5000,下面是控制台输出

一共经历了699纳秒

我们输入10000,下面是控制台输出

一共经历了1446纳秒

我们可以看出现在的计算机运算还是非常快的,最开始我输出n比较小,发现输出为0,把我吓一跳(如果这时候实在想测试时间长短,可以换一个计算纳秒的方法,也是在System包下)

现在问题来了,我们发现一个数组中一个元素就存放一位数据,那么我们就考虑一下一个元素存放多位数据并且我们也想让时间优化一下。

我们首先来看一下整形数组一个元素可以存放的最大数据
关于计算一个数据的最大值,点击查看另外一个文章

int b= 0b0111 1111 1111 1111 1111 1111 1111 1111;
System.out.println(b)

控制台输出

2147483647

那么我们就知道了一个整形数据最多存放9位数,10位数显然不行。直接上代码

    /**
     * n最大的范围为10000
     * 经过测试,int占用四个字节,那么最大正数值为2147483647
     */
    public static void function2(int n){
        if(n<0){
            System.out.println("error input");
        }else{
            //10000!大概37000多位,我们用每一个数组元素保存8位数
            //倒着存放数据是因为方便我们进位
            long startOfFunction=System.currentTimeMillis();
             //10000!大概37000多位,我们用每一个数组元素保存5位数,
             //至于原因请看下面的双重for循环里的注释
            //那么我们只需要8000个大小的整形的数组即可,8000*5=40000位
            int max=8000;  
            int[] a=new int[max];
            int carry=0;//carry表示进位
            a[1]=1;//本次测试,a[0]都没有使用
            int t=1;
            for(int i=1;i<=n;i++){
                carry=0;
                for(int j=1;j<=t;j++){
                    a[j]=a[j]*i+carry;
                    carry=a[j]/100000;
                    a[j]=a[j]%100000;
                    //carry保留进位数,留给数组元素的下一位
                }
    /*
    * 经过测试,int占用四个字节,那么最大正数值为2147483647
    * 那么问题来了,我们能不能一个数组元素就保存9位数呢?
    * 答案是不行的,原因在于n最大为10000,即四位数,那么数组元素乘以10000时,数组元素的位数
    * 就增加了四位(比如一个数乘以1-9之内的数,那么这个数最多增加一位数),就有可能溢出
    * 所以最多保存5位数(当然是针对N=10000的情况)
    */
                if(carry!=0){
                    t++;
                    a[t]=carry;
                }
    /*
     * 当数组前一个元素的位数已经超过了5位数,那么carry就记录下超过进位,
     * 因为数组元素t+1到a.length-1都还没使用,所以a[t]=carry;
    */
            }
            System.out.print(a[t]);
            //第一个元素不用考虑,数组元素前面的0会忽略
            for(int i=t-1,count=0;i>0;i--,count++){
                if(count==10){
                    count=0;
                    System.out.println();
                }
                //但是这个元素前5位,前面的0不能忽略
                System.out.printf("%05d",a[i]);
            }
            long during=System.currentTimeMillis()-startOfFunction;
            System.out.println("\n一共经历了"+during+"纳秒");
        }
    }

输入5000,控制台输出(数据过大直接忽视,只截取运行时间的输出)

一共经历了153纳秒

输入10000,控制台输出

一共经历了330纳秒

我们发现同样的5000!时间从699纳秒变成了153纳秒。
而10000!时间从1446纳秒变为了330纳秒,时间与空间都得到了优化!!!

/********************************************/
有不足之处欢迎指出(抱拳),觉得不错请点下赞后者评论噢,这是我更新博客的动力(啊哈哈哈)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值