劍指Offer(Java版):數值的整數次方

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

一、自覺得很簡單的解法:面試

因爲不須要考慮大數問題,這道題看起來很簡單,可能很多應聘者在看到題目30秒後就能寫出以下的代碼:app

 

[java] view plain copy函數

 

  1. public double powerWithExponent(double base,int exponent){  
  2.         double result = 1.0;  
  3.         for(int i = 1;i<= exponent;i++){  
  4.             result = result*base;  
  5.         }  
  6.         return result;  
  7.     }  

不錯遺憾的是,寫的快不必定就能獲得面試官的青睞,由於面試官會問輸入的指數(exponent)小於1即 是0和負數的時候怎麼辦?上面的代碼徹底沒有考慮,只包括了指數爲正數的狀況。優化

 

二、全面但不夠高效的解法,咱們離Offer已經不遠了spa

咱們知道當指數爲負數的時候,能夠先對指數求絕對值,而後算出次方的 結果以後再取倒數。既然有求倒數,咱們很天然的就要想到有沒有可能對0求倒數,若是對0求倒數怎麼辦?當底數base是零且指數是負數的時候,咱們不作特 殊的處理,就會發現對0求倒數從而致使程序運行出錯。怎麼告訴函數的調用者出現了這種錯誤?在Java中能夠拋出異常來解決。.net

最後須要指出的是,因爲0的0次方在數學上沒有意義的,所以不管是輸出0仍是1都是能夠接收的,但這都須要和麪試官說清楚,代表咱們已經考慮到了這個邊界值了。blog

有了這些相對而言已經全面不少的考慮,咱們就能夠把最初的代碼修改以下:遞歸

  1. /** 
  2.  * 題目:實現函數double Power(double base,int exponent),求base的exponent次方。不得使用庫函數,同時不須要考慮大數問題 
  3.  * 對於這道題,要考慮四種狀況: 
  4.  * 一、底數爲0,指數爲負數的狀況,無心義 
  5.  * 二、指數爲0,返回1 
  6.  * 三、指數爲負數,返回1.0/base,-exponent 
  7.  * 四、指數正數,base,exponent 
  8.  */

 

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代碼以下:

  1. private  static  double powerWithExponent2(double base,int exponent){  
  2.         if(exponent == 0)  
  3.             return 1;  
  4.         if(exponent == 1)  
  5.             return base;  
  6.         double result = powerWithExponent2(base,exponent >>1);  
  7.         result *= result;  
  8.         if((exponent&0x1) == 1)  
  9.             result *=base;  
  10.         return result;  
  11.     }  

最後再提醒一個細節:咱們用右移運算代替除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

相關文章
相關標籤/搜索