題目:實現函數double Power(double base,int exponent),求base的exponent次方。不得使用庫函數,同時不須要考慮大數問題java
一、自覺得很簡單的解法:面試
因爲不須要考慮大數問題,這道題看起來很簡單,可能很多應聘者在看到題目30秒後就能寫出以下的代碼:app
[java] view plain copy函數
不錯遺憾的是,寫的快不必定就能獲得面試官的青睞,由於面試官會問輸入的指數(exponent)小於1即 是0和負數的時候怎麼辦?上面的代碼徹底沒有考慮,只包括了指數爲正數的狀況。優化
二、全面但不夠高效的解法,咱們離Offer已經不遠了spa
咱們知道當指數爲負數的時候,能夠先對指數求絕對值,而後算出次方的 結果以後再取倒數。既然有求倒數,咱們很天然的就要想到有沒有可能對0求倒數,若是對0求倒數怎麼辦?當底數base是零且指數是負數的時候,咱們不作特 殊的處理,就會發現對0求倒數從而致使程序運行出錯。怎麼告訴函數的調用者出現了這種錯誤?在Java中能夠拋出異常來解決。.net
最後須要指出的是,因爲0的0次方在數學上沒有意義的,所以不管是輸出0仍是1都是能夠接收的,但這都須要和麪試官說清楚,代表咱們已經考慮到了這個邊界值了。blog
有了這些相對而言已經全面不少的考慮,咱們就能夠把最初的代碼修改以下:遞歸
package cglib;ip
public class List1
{
public static double power(double base,int exponent) throws 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 static double powerWithExponent(double base,int exponent){
double result = 1.0;
for(int i = 1;i<= exponent;i++){
result = result*base;
}
return result;
}
//判斷兩個double型數據,計算機有偏差
private static 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[] args) throws Exception{
System.out.println(power(3, -1));
}
}
因爲計算機表示小數(包括float和double型小數)都會有偏差,咱們不能直接用等號(==)判斷兩個小數是否相等。若是兩個小數的差的絕對值很小,好比小於0.0000001,就能夠認爲他們相等。
此時咱們考慮得已經很周詳了,已經可以獲得不少面試官的要求了。可是若是咱們碰到的面試官是一個在效率上追求完美的人,那麼他有可能提醒咱們函數PowerWithExponent還有更快的辦法。
三、全面而高效的解法,確保咱們能拿到Offer
若是輸入的指數exponent爲32,咱們在函數 powerWithExponent的循環中須要作31次乘方。但咱們能夠換一種思路考慮:咱們的目標是求出一個數字的32次方,若是咱們已經知道了它的 16次方,那麼只要16次放的基礎上再平方一次就能夠了。而16次方又是8次方的平方。這樣以此類推,咱們求32次方只須要5次乘方:先求平方,在平方的 基礎上求4次方,在4次方的基礎上求8次方,在8次方的基礎上求16次方,最後在16此方的基礎上求32次方。
也就是說咱們能夠利用下面這個公示求a的n次方:
這個公式就是咱們前面利用O(logn)時間求斐波那契數列時,討論的公式,這個公式很容易就能用遞歸實現。新的PowerWithExponent代碼以下:
最後再提醒一個細節:咱們用右移運算代替除2,用位與運算符代替了求餘運算符(%)來判斷一個數是奇數仍是偶數。位運算的效率比乘除法及求餘運算的效率要高不少。既然要優化代碼,咱們就把優化作到極致。
最終代碼:
package cglib;
public class List1
{
public static double power(double base,int exponent) throws 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 static double powerWithExponent(double base,int exponent){
if(exponent == 0)
return 1;
if(exponent == 1)
return base;
double result = powerWithExponent(base,exponent >>1);
result *= result;
if((exponent&0x1) == 1)//0000011&00000001=00000001,10&01=0
{
System.out.println("奇數次冪");
result *=base;
}
return result;
}
//判斷兩個double型數據,計算機有偏差
private static 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[] args) throws Exception{
System.out.println(power(3, -5));
}
}
輸出:
奇數次冪 0.004115226337448559