常見演算法在實際專案中的應用
近日Emanuele Viola在Stackexchange上提了這樣的一個問題,他希望有人能夠列舉一些目前軟體、硬體中正在使用的演算法的實際案例來證明演算法的重要性,對於大家可能給到的回答,他還提出了幾點要求:
- 使用這些演算法的軟體或者硬體應該是被廣泛應用的;
- 例子需要具體,並給出確切的系統、演算法的引用地址;
- 在經典的本科生或者博士的課程中應該教過這些演算法或者資料結構;
Vijay D的回覆獲得了最佳答案,他的具體回覆內容如下:
Linux核心中的基本資料結構和演算法
這是一個簡單的B+樹實現,我寫它的目的是作為練習,並以此瞭解B+樹的工作原理。結果該實現發揮了它的實用價值。
…
一個不經常在教科書中提及的技巧:最小值應該放在右側,而不是左側。一個節點內所有被使用的槽位應該在左側,沒有使用的節點應該為NUL,大部分的操作只遍歷一次所有的槽位,在第一個NUL處終止。
- 紅黑樹用於排程、虛擬記憶體管理、跟蹤檔案描述符和目錄條目等;
- 區間樹
- Radix樹,用於記憶體管理、NFS相關查詢和網路相關的功能;
radix樹的一個常見的用法是儲存頁面結構體的指標; - 優先順序堆,文字上的描述,主要是在教科書中實現,用於control
group系統;
包含指標的只允許簡單插入的靜態大小優先順序堆,基於CLR(演算法導論)第七章 - 雜湊函式,引用Knuth和他的一篇論文:
Knuth建議選擇與機器字長所能表達的最大整數約成黃金比例的素數來做乘法雜湊,Chuck Lever 證實了這個技術的有效性;這些選擇的素數是位稀疏的,也就是說對他們的操作可以使用位移和加法來替換機器中很慢的乘法操作;
- 有些程式碼,比如這個驅動,他們是自己實現的雜湊函式。
- 雜湊表,用於實現索引節點、檔案系統完整性檢查等;
- 位陣列,用於處理flags、中斷等,在Knuth第四卷中有對其特性的描述;
- 二叉樹搜尋用於中斷處理、登記快取查詢等;
- 深度優先搜尋和他的變體被應用於目錄配置;
在名稱空間樹中執行一個修改過的深度優先演算法,開始(和終止於)start_handle所確定的節點。當與引數匹配的節點被發現以後,回撥函式將會被呼叫。如果回撥函式返回一個非空的值,搜尋將會立即終止,這個值將會回傳給呼叫函式;
Knuth、Morris和 Pratt [1]實現了一個線性時間複雜度字串匹配演算法。該演算法完全規避了對轉換函式DELTA的顯式計算。其匹配時間為O(n)(其中n是文字長度),只使用一 個輔助函式PI[1…m](其中m是模式的長度),模式的預處理時間是O(m)。PI這個陣列允許DELTA函式在需要時能迅速執行。大體上,對任意 狀態q=0,1,…,m和任意SIGMA中的字元”a”,PI[“q”]儲存了獨立於”a”的資訊,並用於計算DELTA(“q”, “a”)。由於PI這個陣列只包含m個條目,而DELTA包含O(m|SIGMA|)個條目,我們通過計算PI進而在預處理時間儲存|SIGMA|的系 數,而非計算DELTA。
[1] Cormen, Leiserson, Rivest, Stein Introdcution to Algorithms, 2nd Edition, MIT Press
[2] See finite automation theory
- Boyer-Moore模式匹配,如下是引用和對其他演算法的使用建議;
Boyer-Moore字串匹配演算法:
[1] A Fast String Searching Algorithm, R.S. Boyer and Moore. Communications of the Association for Computing Machinery, 20(10), 1977, pp. 762-772. http://www.cs.utexas.edu/users/moore/publications/fstrpos.pdf
注意:由於Boyer-Moore(BM)自右向左做匹配,有一種可能性是一個匹配分佈在不同的塊中,這種情況下是不能找到任何匹配的。
如果你想確保這樣的事情不會發生,使用Knuth-Pratt-Morris(KMP)演算法來替代。也就是說,根據你的設定選擇合適的字串查詢演算法。
如果你使用文字搜尋架構來過濾、網路入侵檢測(NIDS)或者任何安全為目的,那麼選擇KMP。如果你關乎效能,比如你在分類資料包,並應用服務質量(QoS)策略,並且你不介意可能需要在分佈在多個片段中匹配,然後就選擇BM。
Chromium 瀏覽器中的資料結構和演算法
此樹會被分配策略引數化,這個策略負責在C的自由儲存空間和區域中分配列表,參見zone.h
同時,程式碼中還包含了一些第三方的演算法和資料結構,例如:
程式語言類庫
- Java API 非常廣泛,包含的太多;
- Boost C++ 類庫,包含了諸如Boyer-Moore和Knuth-Morris-Pratt字串匹配演算法等;
分配和排程演算法
- 最近最少使用演算法有多種實現方式,在Linux核心中是基於列表實現的;
- 其他可能需要了解的是先入先出、最不常用和輪詢;
- VAX、VMS系統中大量使用FIFO的變體;
- Richard Carr的時鐘演算法被用於Linux中頁面幀替換;
- Intel i860處理器中使用了隨機替換策略;
- 自適應快取替換被用於一些IBM的儲存控制中,由於專利原因在PostgreSQL只有簡單的應用;
- Knuth在TAOCP第一卷中提到的夥伴記憶體分配演算法被用於Linux核心中,FreeBSD和Facebook都在使用jemalloc併發分配器;
*nix系統中的核心元件
- grep和awk都實現了使用Thompson-McNaughton-Yamada構建演算法實現從正則表示式中建立NFA;
- tsort實現了拓撲排序;
- GNU grep,據作者Mike Haertel所說,實現了Boyer-Moore演算法;
- Unix中的crypt(1)實現了啞謎機(Enigma Machine)中的加密演算法的變種;
- Doug Mcllroy基於和James合作的原型實現的Unix diff,比用來計算Levenshtein距離的標準動態規劃演算法更好,Linux版本被用來計算最短編輯距離;
加密演算法
- MD5用於為軟體包提供校驗碼,還用於*nix系統(Linux實現)中的完整性校驗,同時他還支援Windows和OS X系統;
- OpenSSL實現了需要加密演算法,諸如AES,Blowfish,DES,SHA-1,SHA-2,RSA,DES等;
編譯器
- yacc和bison實現了LALR解析器;
- 支配演算法用於基於SSA形式的最優化編譯器;
- lex和flex將正則表示式編譯為NFA;
壓縮和圖片處理
- 為GIF圖片格式而出現的Lempel-Zivsraf演算法在圖片處理程式中經常被應用,從一個簡單的*nix元件轉化為一個複雜的程式;
- 執行長度編碼被用於生成PCX檔案(用於Paintbrush這個程式中),壓縮BMP檔案和TIFF檔案;
- 小波壓縮(Wavelet壓縮)是JPEG 2000的基礎,所以所有生成JPEG 2000檔案的數碼相機都是實現了這個演算法;
- Reed-Solomon糾錯用於Linux核心、CD驅動、條形碼讀取,並且結合卷積從航行團隊進行圖片傳輸;
衝突驅動條款學習演算法(Conflict Driven Clause Learning)
自2000年以來,在工業標準中的SAT(布林滿足性問題)求解器的執行時間每年都在成倍減少。這一發展的一個非常重要的原因是衝突驅動條款學習算 法(Conflict Driven Clause Learning)的使用,它結合了Davis Logemann和Loveland的約束程式設計和人工智慧研究技術的原始論文中關於布林約束傳播的演算法。具體來說,工業建模中SAT被認為是一個簡單的問 題(見討論)。對我來說,這是近代最偉大的成功故事之一,因為它結合了先進的演算法、巧妙的設計思路、實驗反饋,並以一致的共同努力來解決這個問題。Malik和Zhang的CACM論文是一個很好的閱讀材料。許多大學都在教授這個演算法,但通常是在邏輯或形式化方法的課程中。