面試 5:手寫 Java 的 pow() 實現

咱們在處理一道編程面試題的時候,一般除了注意代碼規範之外,千萬要記得本身心中模擬一個單元測試。主要經過三方面來處理。java

  • 功能性測試
  • 邊界值測試
  • 負面性測試

無論如何,必定要保證本身代碼考慮的全面,而不要簡單地猜測用戶的輸入必定是正確的,只是去實現功能。一般你編寫一個能接受住考驗的代碼,會讓面試官對你另眼相看,你能夠不厲害,但已經充分說明了你的靠譜。面試

今天咱們的面試題目是:算法

面試題:嘗試實現 Java 的 Math.pow(double base,int exponent) 函數算法,計算 base 的 exponent 次方,不得使用庫函數,同時不須要考慮大數問題。編程

面試題來源於《劍指 Offer》第 11 題,數字的整數次方。函數

不要介意 Java 真正的方法是 Math.pow(double var1,double var2)。單元測試

因爲不須要考慮大數問題,很多小夥伴心中暗自竊喜,這題目也太簡單了,給我撞上了,運氣真好,因而直接寫出下面的代碼:測試

public class Test11 { private static double power(double base, int exponent) { double result = 1.0; for (int i = 0; i < exponent; i++) { result *= base; } return result; } public static void main(String[] args) { System.out.println(power(2, 2)); System.out.println(power(2, 4)); System.out.println(power(3, 1)); System.out.println(power(3, 0)); } } 

寫的快天然是好事,若是正確的話會被面試官認爲是思惟敏捷。但若是考慮不周的話,恐怕就極容易被面試官認爲是不靠譜的人了。在技術能力和靠譜度之間,大多數面試官更青睞於靠譜度。優化

咱們上面確實作到了功能測試,但面試官可能會直接提示咱們,假設咱們的 exponent 輸入一個負值,能獲得正確值麼?spa

跟着本身的代碼走一遍,終於意識到了這個問題,當 exponent 爲負數的時候,循環根本就進不去,不管輸入的負數是什麼,都會返回 1.0,這顯然是不正確的算法。代碼規範

咱們在數學中學過,給一個數值上負數次方,至關於給這個數值上整數次方再求倒數。

意識到這點,咱們修正一下代碼。

public class Test11 { private static double power(double base, int exponent) { // 由於除了 0 之外,任何數值的 0 次方都爲 1,因此咱們默認爲 1.0; // 0 的 0 次方,在數學書是沒有意義的,爲了貼切,咱們也默認爲 1.0 double result = 1.0; // 處理負數次方狀況 boolean isNegetive = false; if (exponent < 0) { isNegetive = true; exponent = -exponent; } for (int i = 0; i < exponent; i++) { result *= base; } if (isNegetive) return 1 / result; return result; } public static void main(String[] args) { System.out.println(power(2, 2)); System.out.println(power(2, 4)); System.out.println(power(3, 1)); System.out.println(power(3, -1)); } } 

咱們在代碼中增長了一個判斷是否爲負數的 isNegetive 變量,當爲負數的時候,咱們就置爲 true,並計算它的絕對值次冪,最後返回結果的時候返回它的倒數。

面試官看到這樣的代碼,可能就有點按捺不住心裏的怒火了,不過因爲你此前一直面試回答的較好,也打算再給你點機會,面試官提示你,當 base 傳入 0,exponent 傳入負數,會怎樣?

瞬間發現了本身的問題,這不是犯了數學最多見的問題,給 0 求倒數麼?

雖然 Java 的 Math.pow() 方法也存在這個問題,但咱們這裏忽略不計。

因而立刻更新代碼。

public class Test11 { private static double power(double base, int exponent) { // 由於除了 0 之外,任何數值的 0 次方都爲 1,因此咱們默認爲 1.0; // 0 的 0 次方,在數學書是沒有意義的,爲了貼切,咱們也默認爲 1.0 double result = 1.0; // 處理底數爲 0 的狀況,底數爲 0 其餘任意次方結果都應該是 0 if (base == 0) return 0.0; // 處理負數次方狀況 boolean isNegetive = false; if (exponent < 0) { isNegetive = true; exponent = -exponent; } for (int i = 0; i < exponent; i++) { result *= base; } if (isNegetive) return 1 / result; return result; } public static void main(String[] args) { System.out.println(power(2, 2)); System.out.println(power(2, 4)); System.out.println(power(3, 1)); System.out.println(power(0, -1)); } } 

有了上一次的經驗,此次並不敢直接上交代碼了,而是認真檢查邊界值和各類狀況。檢查 1 遍,2 遍,均沒有發現問題,提交代碼。

計算機表示小數均有偏差,這個在 Python 中尤爲嚴重,但經數次測試,《劍指 Offer》中講的雙精度偏差問題彷佛在 Java 的 == 運算符中並不存在。若有問題,歡迎指正。

上面的代碼基本還算整,健壯性也還不錯,但面試官可能還想問問有沒有更加優秀的算法。

仔細查看,確實彷佛是有辦法優化的,好比咱們要求 power(2,16) 的值,咱們只須要先求出 2 的 8 次方,再平方就能夠了;以此類推,咱們計算 2 的 8 次方的時候,能夠先計算 2 的 4 次方,而後再作平方運算.....妙哉妙哉!

須要注意的是,若是咱們的冪數爲奇數的話,咱們須要在最後再乘一次咱們的底數。

咱們嘗試修改代碼以下:

public class Test11 { private static double power(double base, int exponent) { // 由於除了 0 之外,任何數值的 0 次方都爲 1,因此咱們默認爲 1.0; // 0 的 0 次方,在數學書是沒有意義的,爲了貼切,咱們也默認爲 1.0 double result = 1.0; // 處理底數爲 0 的狀況,底數爲 0 其餘任意次方結果都應該是 0 if (base == 0) return 0.0; // 處理負數次方狀況 boolean isNegetive = false; if (exponent < 0) { isNegetive = true; exponent = -exponent; } result = getTheResult(base, exponent); if (isNegetive) return 1 / result; return result; } private static double getTheResult(double base, int exponent) { // 若是指數爲0,返回1 if (exponent == 0) { return 1; } // 指數爲1,返回底數 if (exponent == 1) { return base; } // 遞歸求一半的值 double result = getTheResult(base, exponent >> 1); // 求最終值,若是是奇數,還要乘一次底數 result *= result; if ((exponent & 0x1) == 1) { result *= base; } return result; } public static void main(String[] args) { System.out.println(power(2, 2)); System.out.println(power(2, 4)); System.out.println(power(3, -1)); System.out.println(power(0.1, 2)); } } 

完美解決。

在提交代碼的時候,還能夠主動提示面試官,咱們在上面用右移運算符代替了除以 2,用位與運算符代替了求餘運算符 % 來判斷是一個奇數仍是一個偶數。讓他知道咱們對編程的細節真的很重視,這大概也就是細節決定成敗吧。一兩個細節的打動說不定就讓面試官下定決心給咱們發放 Offer 了。

位運算的效率比乘除法及求餘運算的效率要高的多。

由於移位指令佔 2 個機器週期,而乘除法指令佔 4 個機器週期。從硬件上看,移位對硬件更容易實現,因此咱們更優先用移位。

好了,今天咱們的面試精講就到這裏,咱們明天再見!

相關文章
相關標籤/搜索