背景
談起這個題目也主要是自己作為面試官參與技術面試多多少少也有五六十次了(算上校招的話更多), 各種各樣的人(有厲害的, 也有奇葩的)都遇到過, 雖然當面試官經驗不是很多, 但這裡也想談談自己的一些看法. 或許你有不同的意見或者覺得我的做法有不恰當的地方, 希望你可以指出或參與討論.
面試本來就是一個雙向選擇的過程, 面試官和候選人的地位本應該是一個平等的位置, 面試官希望通過簡單的交流溝通可以對候選人的技術, 溝通等(可能主要是技術)有一定了解進而確定候選人是否匹配相應的職位.
因為面試時間有限, 1個小時(一般情況)的時間很難去全面瞭解候選人的技術實力. 所以在面試過程中很難做到完全公平.
舉個簡單的例子, 面試官出一道題目, 候選人 A 可能曾經做過或見過, 所以能夠比較輕鬆地回答出這個問題, 而候選人 B 沒有做過, 雖然不能答出讓面試官滿意的答案, 但 B 提供了一些解題的思路, 雖然最終並沒有答出這道題目, 這就一定說明候選人 B 比 A 差麼? 並不見得.
額, 發現編不下去了, 直接上本文 title 裡所指的題目吧, 這道題目是我經常出的一道面試題.
不過這個題目公佈後, 以後面試可能就得換題目了, 不過其實鑑於目前我公號/blog的閱讀量可以直接忽略的 :(
題目
實現一個函式, 完成 開根號 的操作, 方法簽名如下.
double sqrt(int v, double t)
要求:
不能呼叫系統庫函式, 諸如 Math.sqrt(v) 之類的;
假設計算出的結果為 r, 要求滿足如下條件, , 其中 是真實的值, t 為給定的一個誤差範圍, 例如0.1等, 即你計算出的值要在給定的誤差範圍內. (哭, 公眾號文章裡不支援 mathjax)
實現語言不限, 你條件可以比上述更加苛刻, 但不能寬鬆, 舉例而言, 我呼叫你的介面 sqrt(9, 0.21) 返回值屬於 [2.79, 3.21] 這個區間的任意一個都滿足條件.
看到這裡, 其實你可以 拿出筆和紙, 嘗試解答一下, 強調一下, 一定要注意給定的誤差條件, 歡迎溝通交流. 其實這個題目是就是 leetcode 上原題稍加變化得到.
解答
java學習交流群:478052716
其實剛開始, 我認為這道題目比較簡單, 至少在給予提示後, 理想當中大部分一線的碼農都可以給出實實在在 code 的.
然後事實並非如此, 然而在面試很很多人之後, 發現此道題目並不簡單. // 當然, 估計也是 candidate 的質量問題.
其實, 我剛開始面試時還用一些二叉樹相關如非遞迴遍歷等題目的, 後來基本上沒人能寫出(社招) 也就放棄了.
當被問起這道題目之後, 可能遇到的答案如下, 這裡我就直接根據答案的滿意度排個升序.
直接放棄
題目給出後, 我一般首先明確候選人弄清楚了題目的含義然後會給一兩分鐘讓候選人先思考一下.
A: 你有什麼思路嗎?
B: 沒有啊.
可能候選人內心OS是: “你出這樣的題目是不是有病啊, 明明有 lib 函式可以直接用的”.
(同組有小夥伴確實有遇到這樣的候選人, 語言雖沒這樣誇張, 大意是: 實際工作中會出現這樣的問題嗎? 我直接給你百度一個就行了)
也有候選人剛開始抱著那個約束誤差範圍的不等式研究 N 久, 然後沒有然後了的. 剛開始看這個條件當然好, 但如果這個不等式沒有思路可以先放一放, 沒必要在那苦熬.
A: 這樣吧, 如果我問題 根號10 等於多少, 你怎麼回答.
B: 3.? 吧
A: 你怎麼知道是3.幾, 因為你知道9開根號是3, 想象一下, 你可以完全用程式幫忙模擬你大腦思考的過程.
B: ……
其實這裡是希望提醒候選人, 我們首先是要解題, 然後才考慮效率. 即不管用什麼方法能夠給出一個答案的. 這個時候候選人可能進入下一個階段了.
暴力搜尋
實際面試過程中也有人是直接到這個階段的.
先用一個迴圈找到 r, 使得 r^2 是離給定 v 最近的平方數, 即你希望算根號10 , 先找到3, 因為3^2=9 , 計算根號10011 , 先找到100 .
然後再用一個迴圈, 每次 r+=t , 直到 r^2 > v 為止.
A: 這個方法從理論上講, 是一個可行的方案, 設想一下, 如果我的精度要求很高, 希望計算的 v 也很大,
如 sqrt(v = 10000000, t = 0.000001) 之類的, 呼叫你這個方法效率是不是很低, 這個時候應該怎麼優化?
B: 這樣的話, 我這個方法效率確實比較低, 不過可以這樣優化, 比如設定一個步長, 一次迭代後, 如果沒有達到預期, 可以不斷修改這個步長來增大逼近真實值的速度, 比如10倍誤差, 100倍誤差等.
其實, 在與候選人的不斷交流中可以看出候選人的 Problem Solving 的能力, 這也是面試考察中的一點. 例如關於上面問題的優化, 也可能用於在實際工作中遇到的問題.
例如, 我們在實際工作中可能經常會寫一些非同步的回撥通知介面等, 這個介面可能是其他團隊維護的, 有可能由於網路問題等回撥介面可能會失敗進而需要重試, 對於重試的機制其實就可以借鑑上面的”步長”機制, 第一次回撥失敗, 我等待 1s 後重試, 失敗再重試, 也許間隔 1s 不太恰當, 是否可以修改等待的步長, 等待比如 5s, 10s? 等等再重試直到成功. 為什麼要這樣做? 也許對方 server 本來現在就處於峰值, 你不停的重試不但沒有增加你介面呼叫成功的機會, 反而增加對方 server 的負擔.
額, 跑題了, 回到這個問題本身, 繼續
A: 恩, 這樣做確實可以優化, 是不是稍微有些複雜, 你聽說過二分搜尋/折半查詢嗎? 可以借用一下這個思路.
B: 我想想…
二分搜尋
當然, 部分候選人提示二分後, 就直接能夠 get 到點, 並且能夠寫出二分大體框架, 但一般結束條件寫的不對. 實際上能正確寫出二分搜尋的候選人就已經較少了 (你確實應該試著寫一下).
如果候選人還沒有思路, 就會繼續
A: 這樣理解吧, 你剛剛的搜尋整數部分的過程其實是線性的, 一個一個數去暴力窮舉, 藉助二分的意思就是, 比如算 根號10, 你搜尋範圍是, [0, 10] (其實除了幾個數之外範圍可以更小[0, v/2], 你能證明麼?).
因為5^2 = 25 > 10 , 所以 r 屬於[0, 5)
因為(5/2) ^2 = 6.25 < 10 , 所以 r 屬於 (2.5, 5)
因為(3.75^2 = 14.0625 > 10) , 所以 r 屬於 (3.75, 5)
繼續, 如果你結束條件不太確定, 可以暫時不管…
我覺得我提示到這裡, 已經很明顯了.
A: 你現在明白了嗎?
B: 明白了.
A: 那你寫一下程式碼吧.
一個二分搜尋, 演算法都結合例子講一遍了, 在候選人回答明白的情況下, 理想當中, 作為一線開發者寫出來應該不成問題吧.
然而…理想和現實還是有差距的.
很多人都喜歡用遞迴寫, 可是很多人遞迴裡面的最重要的結束條件都木有, 一些邊界條件等等都木有. 所以一般情況下, 程式碼寫完後, 我會讓候選人自己寫測試用例.
A: 寫好了是吧, 你寫幾個測試用例吧, 假設這個介面是別人寫的, 你應該從哪幾個角度去測試.
B: sqrt(-4, 0.21), 哎呀, 我這裡忘了判斷了, 改一下程式碼
B: sqrt(0, 0.21), sqrt(4, 0.21)… 還有問題, 再改改
A: ……
為什麼要別人提示要測試用例, 才去 check 自己寫的程式碼的正確性呢. 有的候選人寫的程式碼, 就不拿一些異常情況去 check, 就用上面講的 sqrt(10, 0.21) 的例子都得不到預期結果.
能夠到達這一個步驟的人已經較少了, 如果你有較全測試用例和邊界條件的判斷, 再加上後面的結束條件能夠正確, 基本上這道題目就算滿意了.
關於結束條件
本質上講, 這個演算法就是一個迭代逼近的過程, 用二分的思路後, 關鍵就在於什麼時候結束. 題目中已經給了誤差條件, , 難點在於其中的不知道, 不太方便直接進行計算判斷.
不少人用一個另外的結束條件來進行了判斷即: , 其實這兩個條件是不一樣的, 應該是不符合本題目的條件的.
對於這個結束條件, 你有什麼看法嗎? 能證明你的想法嗎?
面試的人多了, 感覺預期都有所下降了. 現在基本上如果能夠把整個二分整體框架寫出來, 還能分析個二分複雜度之類的, 一些基礎還說得過去, 我這裡也就算過了. 當然目前我司是3輪技術面過才能拿到 Offer.
其他解法
當然本題還有一些其他的數學解法, 例如用牛頓迭代法, 梯度下降法(最速下降法), 泰勒公式展開等等.
如果候選人能想到這些, 說明他還是有一定數學基礎的, 如果願意可以讓他講講.
對於這道題目, 你有什麼比較好的思路嗎? 歡迎討論.
結語
本文題目是"從一道面試題談談一線碼農應該具備的基本素質”, 其實, 上面大部分內容只談到了這道題目本身(也穿插了一些對這道題目的分析和理解).
上述題目的場景是社招面試中的, 對於這樣的題目來說校招的反饋會更好, 但我想說的是, 難道社招確實寫不出來麼? 我其實想表達的是, 作為在最前線 coding 的碼農, 在別人講解了二分演算法的條件下, 能寫出這個二分演算法難道不是一線碼農應該具備的基本素質?
一線碼農難道不應該對一些基本的演算法有所瞭解? 對常見的演算法複雜度有所瞭解? 比如二分搜尋複雜度為什麼是 .
很多人對演算法複雜度的概念瞭解甚微, 面試前死記硬背, 但二分搜尋的複雜度應該還是能推匯出來吧, 沒讓推導快排啊(啊, 我自己貌似也忘記了快排複雜度的推導).
之前有一個候選人, Java 開發七八年經驗了, 問 ArrayList, HashMap 怎麼實現的都不知道.
還有一個印象比較深, 在 XX 做搜尋, 面試職位也是開發啊, 結果落實到程式碼就根本下不了筆.
還有候選人寫精通 Java, 結果連 GC 原理都不清楚, 還有什麼熟練掌握 Vim, 結果連基本文字替換都不會.