Eduardo

胜,不妄喜;败,不遑馁;胸有激雷而面如平湖者,可拜上将军!

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
上一篇《关于大数值n!的讨论——习题118》中讲到:当n比较大的时候,常规类型由于取值范围限定容易造成溢出。

如何解决溢出问题呢?有两种解决方法:

其一,自己构造一个类型,接纳数据。或者采用取值范围比较大的已有类型(Java中BigIngeger和BigDecimal类型)。

其二,对阶乘乘积方式改进。

下面我将两种解决方法分别比较。

方法1:
import java.math.BigInteger;
import java.util.Scanner;

public class Factorial {

    
/**
     * 
@version 1.6 2009/11/22
     * 
@author Eduardo
     
*/
    
public static void main(String[] args) {
        Scanner cin 
= new Scanner(System.in);
        BigInteger product 
= BigInteger.valueOf(1);
        
int n;

        
while (cin.hasNextInt()) {
            n 
= cin.nextInt();
            
long start = System.currentTimeMillis();
            
for (int i = 2; i <= n; i++) {
                product 
= product.multiply(BigInteger.valueOf(i));
            }

            System.out.println(product);
            product 
= BigInteger.valueOf(1);
            
long end = System.currentTimeMillis();
            System.out.println(
"Time: " + (end - start));
        }
    }

}
5000!用时:327ms

65535!用时:91162ms

方法2:

思路是这样的:大数用一个动态数组来表示,数组的每一个元素表示一位十进制数字,高位在前,低位在后。那么,用这种表示法,如何做乘法运算呢?其实这个问题并不困难,可以使用模拟手算的方法来实现。回忆一下我们在小学时,是如何做一位数乘以多位数的乘法运算的。例如:2234*8。

我们将被乘数表示为一个数组A[], a[1],a[2],a[3],a[4]分别为2,2,3,4,a[0]置为0。

Step1: 从被乘数的个位a[4]起,取出一个数字4.

Step2: 与乘数8相乘,其积是两位数32,其个位数2作为结果的个位,存入a[4], 十位数3存入进位c。

Step3: 取被乘数的上一位数字a[3]与乘数相乘,并加上上一步计算过程的进位C,得到27,将这个数的个位7作为结果的倒数第二位,存入a[3],十位数2存入进位c。

Step4:重复Step3,取a[i](i依次为4,3,2,1)与乘数相乘并加上c,其个位仍存入a[i], 十位数字存入c,直到i等于1为止。

Step5:将最后一步的进位c作为积的最高位a[0]。

同样,求阶乘则对上述步骤改动既可。

import java.util.ArrayList;
import java.util.Scanner;


public class FactorialClass {

    
/**
     * 
@version 1.6 2009/11/19
     * 
@author Eduardo
     
*/
    
public void factorial(int n){
        
        
if(n<0)System.out.println(0);
        
if(n==1||n==0)System.out.println(1);
        
//定义动态数组变量
        ArrayList<Integer> al = new ArrayList<Integer>();
        
int product,carry=0;//定义乘积、进位值
        int digit=(int)(Math.ceil((0.5*Math.log(2*n*Math.PI)+n*Math.log(n)-n)/Math.log(10)));
        
        al.add(
0,1);
        
        
for(int i=2;i<=n;i++){
            carry
=0;
            
for(int j=0;j<=num;j++){
                product
=al.get(j)*i+carry;                
                al.set(j,product
%10);                
                carry
=product/10;                
            }        
            
while(carry>0){
                al.add(
++num, carry%10);
                carry
/=10;
            }
        }
        
for(int i=digit-1;i>=0;i--){
            System.out.print(al.get(i));
        }
        al.clear();
    }
    
public static void main(String[] args) {
        Scanner cin 
= new Scanner(System.in);
        
int n=0;
        
        
while(cin.hasNextInt()){
            n 
= cin.nextInt();
            
long start = System.currentTimeMillis();
            FactorialClass test
=new FactorialClass();
            test.factorial(n);
            
long end = System.currentTimeMillis();
            System.out.println(
"Time: " + (end - start));
        }
    }
    
private int num=0;
}
这个方法对n的取值没有限制,不过程序中将n定义为Int型,所以取值不能超过Int型的范围,计算更大的数值只要将类型改变就可以了。

5000!用时:5367ms

65535!用时:787156ms

这个方法相对于方法1,效率很低,但对n没限制。
posted on 2009-11-22 21:01  Eduardo  阅读(189)  评论(0编辑  收藏  举报