號外,號外 -幾乎所有的binary search和mergesort都有錯

來源:互聯網
上載者:User

這是Joshua Bloch(Effective Java的作者)在google blog上發的文章。在說這個文章之前,不得不強力重複Joshua Bloch的推薦:如果你還沒有讀過Programming Pearls (中文版叫《編程珠璣》)這本書,現在就去讀吧。如果你唯讀了一遍,現在就去再讀一遍吧。

還是說回Joshua的文章。當初Programming Pearls的作者Jon Bentley到CMU做講座。他叫在場的電腦系博士生們寫出binary search的演算法,然後當場分析了其中一份。當然,那份演算法以及絕大部分人寫的演算法都錯了。Jon Bentley在Programming Pearls裡也提到,雖然1946年就有人發表binary search,但直到1962第一個正確啟動並執行演算法才寫出來。這個小故事的關鍵教訓就是寫程式時要仔細考慮演算法的不變數(invariant)。如果我記得沒錯,Programming Pearls第4章講解了怎麼證明binary search的正確性。當然,每本離散數學的教科書都會教我們列出pre-condition, invariant, 和post-condition,證明迴圈開始前pre-condition成立,迴圈中invariant始終成立,而迴圈結束後post-condition被滿足,而幾乎每本教科書(至少我看過的)都會用binary search作例子。所以有興趣的自己去看吧,俺就不羅嗦了。

JDK裡的binary search代碼是這樣實現的(Joshua Bloch本人寫的)

1:     public static int binarySearch(int[] a, int key) {
2: int low = 0;
3: int high = a.length - 1;
4:
5: while (low <= high) {
6: int mid = (low + high) / 2;
7: int midVal = a[mid];
8:
9: if (midVal < key)
10: low = mid + 1;
11: else if (midVal > key)
12: high = mid - 1;
13: else
14: return mid; // key found
15: }
16: return -(low + 1); // key not found.
17: }

錯誤就在第6行:
6:             int mid = (low + high) / 2;
這行的問題是當low和high的和超過2^31-1, 也就是Java裡最大整數值時,整數溢出就發生了,而mid就變成負數了, 於是JVM就抓狂了,於是ArrayIndexOutOfBoundsException就發生了。

當一個數組包含多過2^30元素時,這個錯誤就會被發現。那麼大的數組在80年代Programming Pearls第一版寫就的時候難以想象,但在現在卻很常見。所以說,儘管1962年正確的binary search問世,現實卻是直到現在流行系統裡的binary search還有錯。

解決的辦法不難。把第6行改寫成

6:             int mid = low + ((high - low) / 2);

或者
6:             int mid = (low + high) >>> 1;

C和C++裡沒有這個">>>",我們可以這樣做:

6:            int mid = ((unsigned) (low + high)) >> 1。

那現在binary search就完全正確了嗎?我們還是不知道。我們得到的深刻教訓是,僅僅證明一個程式正確是不夠的。我們必須仔細測試。高德納在寫給Peter van Emde Boas的信裡說,“上面那段程式可能有錯。我只證明了它是正確的,但還沒有測過”。人們往往用這段話來彰顯高德納的一絲不苟和學究氣,誰知道這句話背後是高德納深刻的洞察力。人們常說“理論上講實踐和理論沒有差別。實踐上講,兩者確有差別”,可為旁證。

binary search的這個錯誤同樣會出現在其它“分而治之”的演算法裡,比如說mergesort。如果你有類似的演算法代碼,趕快修改吧。Joshua說,他從中學到的教訓是謙卑:哪怕一個簡單的程式都很難寫對,而整個社會卻運行在龐大而複雜的代碼上面。

最後的總結很有意思:我們程式員需要各種協助,別無它法。仔細設計很好。測試很好。形式化方法很好(不過我還是覺得有教授研究用形式化電子商務需求(比如用範疇論),純粹無事找事)。程式碼檢閱很好,靜態分析很好。但他們並不能幫我們徹底消除代碼錯誤--他們將永遠存在。我們半個世紀以來竭盡全力都不能消除一個程式錯誤。我們必須小心翼翼,防禦性地編程,並且保持警醒。    
                                                                         

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.