劍指Offer面試題11:數值的整數次方 Java實現

題目:數值的整數次方
          實現函數 double Power(double base,int exponent),求base的exponent次方。不得使用庫函數,同時不需要考慮大數問題。

算法分析:
我們都知道在C語言庫函數中有一個pow函數可以用來求乘方,本題要求實現類似pow的功能。
1、自以爲很簡單的解法:
由於不需要考慮大數問題,這道題看起來很簡單,可能不少應聘者在看到題目30秒後就能寫出如下的代碼:

public double powerWithExponent(double base,int exponent){  
    double result = 1.0;  
    for(int i = 1;i<= exponent;i++){  
        result = result*base;  
    }  
    return result;  
}    
不過遺憾的是,寫的快不一定就能得到面試官的青睞,因爲面試官會問輸入的指數(exponent)小於1即 是0和負數的時候怎麼辦?上面的代碼完全沒有考慮,只包括了指數爲正數的情況。


2、全面但不夠高效的解法,我們離Offer已經不遠了
我們知道當指數爲負數的時候,可以先對指數求絕對值,然後算出次方的結果之後再取倒數。既然有求倒數,我們很自然的就要想到有沒有可能對0求倒數,如果對0求倒數怎麼辦?當底數base是零且指數是負數的時候,我們不做特殊的處理,就會發現對0求倒數從而導致程序運行出錯。怎麼告訴函數的調用者出現了這種錯誤?在Java中可以拋出異常來解決。
最後需要指出的是,由於0的0次方在數學上沒有意義的,因此無論是輸出0還是1都是可以接收的,但這都需要和麪試官說清楚,表明我們已經考慮到了這個邊界值了。
有了這些相對而言已經全面很多的考慮,我們就可以把最初的代碼修改如下:
 
 public class E11Power {  
    
    public double power(double base,int exponentthrows Exception{  
        double result = 0.0;  
        if(equal(base,0.0) && exponent<0){  
            throw new Exception("0的負數次冪無意義");  
        }  
        if(equal(exponent,0)){  
            return 1.0;  
        }  
        if(exponent <0){  
            result= powerWithExponent(1.0/base, -exponent);  
        }  
        else{  
            result = powerWithExponent(base,exponent);  
        }  
        return result;  
    }  
    private double powerWithExponent(double base,int exponent){  
        double result = 1.0;  
        for(int i = 1;i<= exponent;i++){  
            result = result*base;  
        }  
        return result;  
    }  
    //判斷兩個double型數據,計算機有誤差  
    private boolean equal(double num1,double num2){  
        if((num1-num2>-0.0000001) && (num1-num2<0.0000001)){  
            return true;  
        }else{  
            return false;  
        }  
    }  
    public static void main(String[] argsthrows Exception{  
        E11Power test = new E11Power();  
        System.out.println(test.power(3, -1));  
    }  
}  

由於計算機表示小數(包括float和double型小數)都會有誤差,我們不能直接用等號(==)判斷兩個小數是否相等。如果兩個小數的差的絕對值很小,比如小於0.0000001,就可以認爲他們相等。
此時我們考慮得已經很周詳了,已經能夠得到很多面試官的要求了。但是如果我們碰到的面試官是一個在效率上追求完美的人,那麼他有可能提醒我們函數PowerWithExponent還有更快的辦法。

3、全面而高效的解法,確保我們能拿到Offer
如果輸入的指數exponent爲32,我們在函數powerWithExponent的循環中需要做31次乘方。但我們可以換一種思路考慮:我們的目標是求出一個數字的32次方,如果我們已經知道了它的16次方,那麼只要16次放的基礎上再平方一次就可以了。而16次方又是8次方的平方。這樣以此類推,我們求32次方只需要5次乘方:先求平方,在平方的基礎上求4次方,在4次方的基礎上求8次方,在8次方的基礎上求16次方,最後在16此方的基礎上求32次方。
也就是說我們可以利用下面這個公示求a的n次方:
 
這個公式就是我們前面利用O(logn)時間求斐波那契數列時,討論的公式,這個公式很容易就能用遞歸實現。新的PowerWithExponent代碼如下:

/**************************************************************      
* Copyright (c) 2016, 
* All rights reserved.                   
* 版 本 號:v2.0                   
* 題目描述:數值的整數次方
* 		      實現函數 double Power(double base,int exponent),求base的exponent次方。不得使用庫函數,同時不需要考慮大數問題。
* 輸入描述:請輸入要計算的數值:
*			8
*			請輸入要計算的次方:
*			3
* 程序輸出: 計算的結果爲:512.0
* 問題分析: 無
* 算法描述:如果輸入的指數exponent爲32,我們在函數powerWithExponent的循環中需要做31次乘方。
* 			但我們可以換一種思路考慮:我們的目標是求出一個數字的32次方,如果我們已經知道了它的16次方,
* 			那麼只要16次放的基礎上再平方一次就可以了。而16次方又是8次方的平方。這樣以此類推,
* 			我們求32次方只需要5次乘方:先求平方,在平方的基礎上求4次方,在4次方的基礎上求8次方,
* 			在8次方的基礎上求16次方,最後在16此方的基礎上求32次方。
* 完成日期:2016-10-30
***************************************************************/ 

package org.marsguo.offerproject11;

import java.util.Scanner;

public class Power {
	public static double PowerWithUnsignedExponent(double base,long exponent){
		if(exponent == 0)
			return 1;
		if(exponent == 1)
			return base;
		
		double result = PowerWithUnsignedExponent(base,exponent>>1);			//利用右移運算來代替除2運算
		result *= result;
		if(exponent%2 ==1)
			result *= base;
		
		return result;
	}
	public static void main(String[] args){
		Scanner scanner = new Scanner(System.in);
		System.out.println("請輸入要計算的數值:");
		double base = scanner.nextDouble();
		System.out.println("請輸入要計算的次方:");
		long exp = scanner.nextLong();
		
		scanner.close();
		System.out.println("計算的結果爲:" + PowerWithUnsignedExponent(base, exp));
	}
}

程序運行結果:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章