《演算法的樂趣》作者王曉華:“玩”過就是收穫(圖靈訪談)

盼盼姐發表於2015-05-13

王曉華是一位熱衷於演算法研究的程式設計師,他是CSDN演算法專欄的超人氣博主,也是《演算法的樂趣》一書的作者。他2005年畢業於華中科技大學,目前在中興通訊上海研發中心從事光纖接入網通訊裝置開發,擔任EPON(乙太網無源光網路)業務軟體開發經理,參與開發的PON裝置在全球部署過億線,為數億家庭提供寬頻接入服務。

王曉華最大的樂趣就是用程式解決生活中的問題。當年為了方便使用Visual Studio 6.0開發軟體,他特意編寫了一個tabbar外掛,並隨後開源了這個軟體。為了文件安全,他開發了一個基於layerFSD技術的透明檔案加密系統,在朋友圈內廣為流傳。後來他在使用Source Insight軟體的時候,又以外掛的形式為Source Insight開發了TabSiPlus外掛,受到了很多程式設計師朋友的歡迎。

enter image description here

問:你從什麼時候開始迷上了演算法?

這個要從上大學時候說起了。像我這樣的懶人覺得拼音輸入法太繁瑣,於是就去學“表形碼”(當然,因為笨的原因,現在還在用拼音輸入法)。資深一點的遊戲玩家都還記得,那個時候為了玩中文DOS遊戲,很多人都裝了UCDOS中文環境,UCDOS沒有表形碼,但是支援通過碼錶檔案增加自定義輸入法。我研究了Windows上的表形碼碼錶檔案(老師給的,適用於Windows 3.x版本)和UCDOS碼錶檔案的格式,發現二者有一定的相似性:都是文字檔案,有固定的格式,每一行由一組編碼和一個字或片語成一個編碼對。二者的區別僅僅在於一個檔案是編碼在前,對應的字或詞在後面,另一個剛好相反,字或詞在前,對應的編碼在後面。碼錶檔案有十幾萬行,手工修改是不可能了,剛好當時在上C語言課,就決定寫個程式來做這個事情。

第一次執行我寫的程式,十幾秒鐘都沒有結束,我覺得程式掛了,於是我就“Ctrl+Break”了,因為我之前寫的C語言作業,從來沒有哪個程式執行時間超過1秒鐘的。但是分析程式碼又覺得沒有問題,看了遺留在磁碟上的碼錶檔案,發現轉換了幾千行,並且內容是對的。於是再次執行程式,等了2-3分鐘,程式結束了。我急不可待地將得到的碼錶檔案匯入UCDOS,發現可以用,當時就感覺非常有成就感。這是我第一次為解決一個實際的問題編寫演算法,儘管當時還沒有演算法、軟體的概念,但是隱隱約約就覺得這東西有用,不是隻用於完成作業,還可以解決實際的問題。後來就是接觸更多的有用演算法以及各種演算法競賽,一直到現在,寫個程式解決問題幾乎成了一種習慣。

問:《演算法的樂趣》這本書在內容的設計上和其他同型別的書相比,最大的特點是什麼?

在我學習演算法的過程中,興趣是最大的推動力,興趣來自於我覺得這東西能解決問題,對自己有用。但是在我寫《演算法專欄》部落格的時候,網上能看到的部落格都是對各種競賽題目的解答,這些競賽題目雖然有趣,但是對很多人來說依然抽象,與程式設計師們日常的工作關係不大,難以提起大多數人的興趣來學習。於是我就想,如過能通過一些現實中普遍應用的演算法做切入點,通過這些例子讓大家覺得演算法是有用的,並通過這個來吸引大家關注演算法,學習演算法,從而達到提高能力的作用,豈不是個很好的主意?

在策劃《演算法的樂趣》這本書的時候,我依然沿用了這種思想。選取的例子都是大家生活中沒有在意,但是卻處處都會遇到的演算法,希望以此來“鼓動”起大家學習演算法的興趣。所以本書只用了很少的章節介紹演算法的本質和設計演算法的常用思想,把主要內容放在介紹這些生活中的趣味演算法的解決過程,並在這個過程中給出各種演算法設計中常用的模式、技巧和思想,提醒讀者在閱讀過程中不僅關注本題的解決,更要關注解決這個問題過程背後的思想,既培養了興趣,又提高了解決問題的能力。

問:演算法學習是一條並不簡單的路,請問你是否有其他優秀的演算法書願意推薦給讀者們?閱讀這些書的順序和注意事項是什麼?

就我的經驗而言,在計算機上學習演算法首先需要熟悉程式語言和資料結構,關於程式語言和資料結構有很多經典的書籍,我就不多介紹了。但就演算法而言,我看過的書有Cormen的《演算法導論》、Knuth的《計算機程式設計藝術》、Weiss的《資料結構與演算法分析》、Levitin的《演算法設計與分析基礎》、Kleigberg的《演算法設計》等等。想參加演算法競賽的同學可以參考劉汝佳等人編寫的《演算法藝術與資訊學競賽》,以及《ACM國際大學生程式設計競賽題解》。因為時間過去太久了,已經不記得看這些書的順序了,只記得最早看的是《演算法導論》,關於順序實在沒有什麼經驗可談。

至於如何讀這些演算法的經典書籍,我倒是有一些經驗和大家分享。不管哪一本書,都不要泛泛地看一遍就覺得看懂了,而是要把書中的每一個例子演算法都用程式寫出來,這樣才能留下深刻的印象。如果遇到看不懂的地方,不要停下來,跳過去看其他演算法,過幾天在回頭來看,如果還看不懂就再過幾天再回頭來看。千萬不要在一個地方停下來,否則你很可能就此失去興趣,恐怕永遠也看不完這本書了。

問:有哪些學習演算法的網站值得推薦?

有很多遊戲開發相關的演算法介紹:
http://www.gamedev.net
http://theory.stanford.edu/~amitp/GameProgramming
http://www.gamasutra.com
http://www.sudoku.com

俄羅斯方塊遊戲的演算法網站:
http://gforge.inria.fr/projects/mdptetris http://colinfahey.com/tetris/tetris.html

leetcode,最近很火的演算法網站:
http://www.leetcode.com

Topcoder,也很經典,每週都有競賽,有獎金的:
http://community.topcoder.com/tc

晉中教育網的“資訊學競賽輔導”:
http://www.jzsyz.jzedu.cn/xxjs/suanfa/index.html

很多大學也有自己的競賽題庫,比如:
北大:http://poj.org/
杭電:http://acm.hdu.edu.cn/
華中科技大學:http://acm.hust.edu.cn/vjudge/toIndex.action

問:很多人都認為“學習演算法很難”,你是否知道有哪些方法可以降低學習演算法的難度?

許多演算法是有難度的,理論很複雜,不容易理解和掌握,這是客觀存在的現實。但是我們可以通過提高自身分析問題和解決問題的能力來相對地降低學習難度。從基礎來講,要深入理解資料結構,至少要非常熟練地掌握一種排序演算法,各種線性表的插入、刪除演算法,樹的遍歷和插入、刪除演算法,圖的遍歷演算法等等。然後要多學習,掌握一些常見問題的解決模式,比如窮舉演算法如何應用,動態規劃演算法如何應用等等。最後要勤思考,對應已經掌握並解決的演算法,要想想為什麼用這種方法解決,有沒有其他方法,類似的問題怎麼辦,提高舉一反三的能力。

問:想學好計算機演算法,是否需要重新學數學?

這個問題無法迴避,數學的重要性不在於演算法中用了哪些公式或數學原理,其重要性在於一種思維方式的培養。當我們遇到一個新的問題的時候,通常有兩種解決問題的方式,一種方式是創造一種新的方法來解決這個問題,另一種方式是將新問題分解、轉化成已知問題,然後用已知的方法解決這個問題。這兩種方式都很需要抽象思維能力,現實生活中很少有機會鍛鍊抽象思維能力,而學習數學是一種很好地培養這種能力的方法。我強調數學學習的目的不是說要學好演算法就必需成為數學大咖,而是通過學習數學促進抽象思維能力的提高。

問:背演算法和理解演算法是一般演算法學習者都能夠做到的,但是很多人會在設計演算法的過程中遇到困難,請問演算法設計為什麼那麼難?如何能完成從“輸入”到“輸出”的轉化?

參加演算法競賽其實是一件很痛苦的事情,要經過大量的訓練,很多人會選擇背一些演算法或整理一些演算法模板,到時候可以直接套用。在理解演算法的基礎上熟記一些經典的演算法實現其實也是一件無可厚非的事情,我上學的時候也參加過一些比賽,通常在比賽之前也會突擊訓練一兩個月,並背很多東西,基本上也是事後一個月就全忘了。

比賽的題目一般都有特定的套路,但是現實生活中的演算法往往就事而論,有可能只有你遇到過,沒有現成的經驗,需要自己從無到有來解決問題。我在書中強調演算法設計有三個方面:模型的建立、演化演算法和輸入、輸出的轉換。這三個方面中模型的建立是關鍵,很多情況下,模型建好了,演化演算法和輸入、輸出的轉換就水到渠成。

建立模型的基礎是資料結構。當一個問題的資料存在先進先出的特性時,你要想到佇列。當一個問題的資料需要頻繁查詢操作時,你要想到有序表、hash、map。當一個問題的資料需要頻繁的插入、刪除操作時,你要想到連結串列。當一個問題中的資料包含父子關係時,你要想到樹。當一個問題中的資料中既有節點又有路徑什麼的,你要想到圖。這些都和使用資料結構的經驗有關,只要多練習就能掌握。

建立模型還需要抽象的邏輯思維能力,簡單地說,就是運用抽象的邏輯思維,抓住問題的主要因素,忽略次要因素,建立問題的框架。演算法設計之難體現在思維方式的轉換和模型的建立,前面說過,這需要有抽象思維能力,缺乏這種能力,連問題都很難想明白,更不用說設計解決問題的演算法了。幸運的是這種能力是可以培養的,學好數學,多研究一些演算法,積累些經驗,都是很好的提高抽象思維能力的方法。

問:演算法在大資料領域有著廣泛的應用,《演算法的樂趣》的推薦者王益和黃鑫也都是機器學習方面的專家,請問程式設計演算法和資料探勘涉及的演算法有什麼區別和聯絡嗎?

程式設計演算法應該只是演算法的一種表達形式,我們還可以用表格或流程圖來表達演算法。資料探勘領域涉及的演算法和其他領域的演算法並無本質的區別,只是問題域不同。資料探勘和機器學習常用的方法,比如決策樹、貝葉斯學習、神經網路、遺傳演算法等等,在其他領域也都是有應用的。神經網路、遺傳演算法作為啟發性搜尋演算法在人工智慧和最優化求解領域也得到了廣泛應用,本書中就介紹了遺傳演算法,只不過所舉的例子是用來解決0-1揹包問題,有點“大材小用”。貝葉斯學習在一些電子郵件應用程式中也有應用,主要用於垃圾郵件的識別。在人工智慧領域或各種專家系統中,決策樹演算法也是常用演算法。各種演算法之間的聯絡還是很普遍的,在不同的領域扮演著不同的角色,本質上沒有區別。

問:在日常生活中,我們有機會見到越來越多的推薦演算法,請問你對現在流行的推薦演算法是否有一些研究?要做好推薦演算法,有哪些問題需要特別注意?

確實是這樣,我曾經想買一個佳能鏡頭,於是用了一下百度,想了解幾種鏡頭的評價,結果一連幾天,只要登入京東或淘寶,首頁都會給我推薦與數位相機有關的配件或鏡頭資訊。

因為工作領域的原因,我對推薦演算法沒有深入的研究,這種演算法應該主要是建立在大資料上的資料分類、篩選和過濾吧。不過就我對演算法的理解來說,目前的推薦演算法還是有改進的餘地的,比如要能區分同一臺計算機的不同使用者,不要在我用電腦的時候向我推薦女式內衣和化妝品,那是我老婆關注的東西。

問:除了書中介紹的很多經典演算法,你是否在工作或生活中發現其他讓您印象深刻的演算法?

應該說,在我的工作和生活中也是處處都有演算法。說到網路協議,TCP協議中的“滑動視窗演算法”也是很經典的演算法。路由器和交換機中為了避免出現埠環路,都會使用最小生成樹演算法。在接入網裝置中,通常語音報文的優先順序高於視訊報文,視訊報文的優先順序又高於一般的資料包文,當一大波各種報文來臨時,裝置如何處理?這個演算法也很經典,就是用多個佇列加上一個排程演算法,簡單說就是六個字:分分類,排排隊。

隨著寬頻的普及,現在幾乎家家都有路由器,很多人都會在路由器上給各個埠配置頻寬,這就會用到頻寬分配演算法,令牌演算法和Max-Min Fairness頻寬分配演算法都是經典的頻寬分配演算法。網路傳輸的資料包文出了錯碼怎麼辦?那就要糾錯,Reed-Solomon編碼和解碼演算法就是這樣的經典糾錯演算法,除此之外,Reed-Solomon編碼和解碼演算法還用於光碟、磁碟的資料糾錯。

問:研究演算法以來,對你來說工作和生活中最大的收穫是什麼?

說實話,我曾經“玩”過的演算法,絕大多數都沒有機會直接應用到工作當中,但是“玩”演算法對我的影響是顯而易見的,它提高了我的動手能力,以及遇到問題時分析並解決問題能力,這就是收穫,也是任何一個軟體企業對程式設計師最基本的要求。


更多精彩,加入圖靈訪談微信!

相關文章