實際專案中的常見演算法

infoq發表於2013-12-01

  本文原始內容來源於 stackexchange,遵循 cc-wiki 協議;

  近日 Emanuele Viola 在 Stackexchange 上提了這樣的一個問題,他希望有人能夠列舉一些目前軟體、硬體中正在使用的演算法的實際案例來證明演算法的重要性,對於大家可能給到的回答,他還提出了幾點要求:

  1. 使用這些演算法的軟體或者硬體應該是被廣泛應用的;
  2. 例子需要具體,並給出確切的系統、演算法的引用地址;
  3. 在經典的本科生或者博士的課程中應該教過這些演算法或者資料結構;

  Vijay D 的回覆獲得了最佳答案,他的具體回覆內容如下:

  Linux 核心中的基本資料結構和演算法

  1. 連結串列雙向連結串列無鎖連結串列
  2. B+ 樹,程式碼中的註釋將會告訴你一些教科書中不能學到的內容:

    這是一個簡單的B+ 樹實現,我寫它的目的是作為練習,並以此瞭解B+ 樹的工作原理。結果該實現發揮了它的實用價值。

    ...

    一個不經常在教科書中提及的技巧:最小值應該放在右側,而不是左側。一個節點內所有被使用的槽位應該在左側,沒有使用的節點應該為 NUL,大部分的操作只遍歷一次所有的槽位,在第一個 NUL 處終止。

  3. 帶權重的有序列表用於互斥鎖驅動等;

  4. 紅黑樹用於排程、虛擬記憶體管理、跟蹤檔案描述符和目錄條目等;
  5. 區間樹
  6. Radix 樹,用於記憶體管理、NFS 相關查詢和網路相關的功能;

    radix 樹的一個常見的用法是儲存頁面結構體的指標;

  7. 優先順序堆,文字上的描述,主要是在教科書中實現,用於 control group 系統;

    包含指標的只允許簡單插入的靜態大小優先順序堆,基於 CLR(演算法導論)第七章

  8. 雜湊函式,引用 Knuth 和他的一篇論文:

    Knuth 建議選擇與機器字長所能表達的最大整數約成黃金比例的素數來做乘法雜湊,Chuck Lever 證實了這個技術的有效性;

    http://www.citi.umich.edu/techreports/reports/citi-tr-00-1.pdf

    這些選擇的素數是位稀疏的,也就是說對他們的操作可以使用位移和加法來替換機器中很慢的乘法操作;

  9. 有些程式碼,比如這個驅動,他們是自己實現的雜湊函式

  10. 雜湊表,用於實現索引節點檔案系統完整性檢查等;
  11. 位陣列,用於處理 flags、中斷等,在 Knuth 第四卷中有對其特性的描述;
  12. Semaphoresspin locks
  13. 二叉樹搜尋用於中斷處理登記快取查詢等;
  14. 使用B-樹進行二叉樹查詢
  15. 深度優先搜尋和他的變體被應用於目錄配置

    在名稱空間樹中執行一個修改過的深度優先演算法,開始(和終止於)start_handle 所確定的節點。當與引數匹配的節點被發現以後,回撥函式將會被呼叫。如果回撥函式返回一個非空的值,搜尋將會立即終止,這個值將會回傳給呼叫函式;

  16. 廣度優先搜尋用於在執行時檢查鎖的正確性;
  17. 連結串列上的合併排序用於垃圾回收檔案系統管理等;
  18. 在某個驅動程式的庫函式裡,氣泡排序居然也被實現了
  19. Knuth-Morris-Pratt 字串匹配

    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(mSIGMA)個條目,我們通過計算 PI 進而在預處理時間儲存 SIGMA 的係數,而非計算 DELTA。

    [1] Cormen, Leiserson, Rivest, Stein Introdcution to Algorithms, 2nd Edition, MIT Press

    [2] See finite automation theory

  20. 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

    [2] Handbook of Exact String Matching Algorithms, Thierry Lecroq, 2004 http://www-igm.univ-mlv.fr/~lecroq/string/string.pdf

    注意:由於 Boyer-Moore(BM)自右向左做匹配,有一種可能性是一個匹配分佈在不同的塊中,這種情況下是不能找到任何匹配的。

    如果你想確保這樣的事情不會發生,使用 Knuth-Pratt-Morris(KMP)演算法來替代。也就是說,根據你的設定選擇合適的字串查詢演算法。

    如果你使用文字搜尋架構來過濾、網路入侵檢測(NIDS)或者任何安全為目的,那麼選擇 KMP。如果你關乎效能,比如你在分類資料包,並應用服務質量(QoS)策略,並且你不介意可能需要在分佈在多個片段中匹配,然後就選擇 BM。

  Chromium 瀏覽器中的資料結構和演算法

  1. 伸展樹

    此樹會被分配策略引數化,這個策略負責在C的自由儲存空間和區域中分配列表,參見 zone.h

  2. Demo 中使用了 Voronoi
  3. 基於 Bresenham 演算法的標籤管理

  同時,程式碼中還包含了一些第三方的演算法和資料結構,例如:

  1. 二叉樹
  2. 紅黑樹
  3. AVL 樹
  4. 用於壓縮的 Rabin-Karp 字串匹配
  5. 計算自動機的字尾
  6. 蘋果實現的布隆過濾器
  7. 布氏演算法

  程式語言類庫

  1. C++ STL,包含的有列表、堆、棧、向量、排序、搜尋和堆操作演算法
  2. Java API 非常廣泛,包含的太多
  3. Boost C++ 類庫,包含了諸如 Boyer-Moore 和 Knuth-Morris-Pratt 字串匹配演算法等;

  分配和排程演算法

  1. 最近最少使用演算法有多種實現方式,在 Linux 核心中是基於列表實現的;
  2. 其他可能需要了解的是先入先出、最不常用和輪詢;
  3. VAX、VMS 系統中大量使用 FIFO 的變體;
  4. Richard Carr時鐘演算法被用於 Linux 中頁面幀替換;
  5. Intel i860 處理器中使用了隨機替換策略;
  6. 自適應快取替換被用於一些 IBM 的儲存控制中,由於專利原因在 PostgreSQL 只有簡單的應用;
  7. Knuth 在 TAOCP 第一卷中提到的夥伴記憶體分配演算法被用於 Linux 核心中,FreeBSD 和 Facebook 都在使用 jemalloc 併發分配器;

  *nix 系統中的核心元件

  1. grep 和 awk 都實現了使用 Thompson-McNaughton-Yamada 構建演算法實現從正規表示式中建立 NFA
  2. tsort 實現了拓撲排序
  3. fgrep 實現了 Aho-Corasick 字串匹配演算法
  4. GNU grep,據作者 Mike Haertel 所說,實現了 Boyer-Moore 演算法
  5. Unix 中的 crypt (1) 實現了啞謎機(Enigma Machine)中的加密演算法的變種;
  6. Doug Mcllroy 基於和 James 合作的原型實現的 Unix diff,比用來計算 Levenshtein 距離的標準動態規劃演算法更好,Linux 版本被用來計算最短編輯距離;

  加密演算法

  1. Merkle 樹,尤其是 Tiger Tree Hash 的變種,用於點對點的程式,例如 GTK GnutellaLimeWire;
  2. MD5用於為軟體包提供校驗碼,還用於*nix 系統(Linux 實現)中的完整性校驗,同時他還支援 Windows 和 OS X 系統;
  3. OpenSSL 實現了需要加密演算法,諸如 AES,Blowfish,DES,SHA-1,SHA-2,RSA,DES 等;

  編譯器

  1. yacc 和 bison 實現了 LALR 解析器
  2. 支配演算法用於基於 SSA 形式的最優化編譯器;
  3. lex 和 flex 將正規表示式編譯為 NFA;

  壓縮和圖片處理

  1. 為 GIF 圖片格式而出現的 Lempel-Zivsraf 演算法在圖片處理程式中經常被應用,從一個簡單的*nix 元件轉化為一個複雜的程式;

  2. 執行長度編碼被用於生成 PCX 檔案(用於 Paintbrush 這個程式中),壓縮 BMP 檔案和 TIFF 檔案;

  3. 小波壓縮(Wavelet 壓縮)是 JPEG 2000 的基礎,所以所有生成 JPEG 2000 檔案的數碼相機都是實現了這個演算法;

  4. Reed-Solomon 糾錯用於 Linux 核心、CD 驅動、條形碼讀取,並且結合卷積從航行團隊進行圖片傳輸;

  衝突驅動條款學習演算法(Conflict Driven Clause Learning)

  自 2000 年以來,在工業標準中的 SAT(布林滿足性問題)求解器的執行時間每年都在成倍減少。這一發展的一個非常重要的原因是衝突驅動條款學習演算法(Conflict Driven Clause Learning)的使用,它結合了 Davis Logemann 和 Loveland 的約束程式設計和人工智慧研究技術的原始論文中關於布林約束傳播的演算法。具體來說,工業建模中 SAT 被認為是一個簡單的問題(見討論)。對我來說,這是近代最偉大的成功故事之一,因為它結合了先進的演算法、巧妙的設計思路、實驗反饋,並以一致的共同努力來解決這個問題。Malik 和 Zhang 的 CACM 論文是一個很好的閱讀材料。許多大學都在教授這個演算法,但通常是在邏輯或形式化方法的課程中。

  微博熱議

  Databricks 大資料公司聯合創始人@hashjoin 首先並在微博上傳播了這個內容:

很多學生和軟體工程師都會好奇自己過去學習的演算法有什麼實際應用的價值。這個 StackExchange 的回答列出了各種經典演算法在幾個開源專案中的應用。http://t.cn/8kAP4yG 作者羅列出了從最基礎的 hash table 到字串匹配和加密演算法等在 Chromium 和 Linux 核心的程式碼。檢視開原始碼是學習演算法實現一個好途徑。

  大家也紛紛發表了自己的看法:

  @GeniusVczh

所謂的演算法實現就跟背書一樣,所以如果不是為了學習語法,千萬不要看那些帶程式碼的程式設計書,或者程式設計書裡面的程式碼。以學習為目的的話,東西就自己做,然後自己用,用出翔了,你就知道他為什麼不好了。

  @左耳朵耗子

說演算法沒啥用的人基本上說明他只在簡單的堆砌業務功能程式碼的井底中。

  @薛正華-中國科學院

我一直覺得在講述每一個技術前,最好先讓大家知道這個技術能幹什麼,曾經幹過什麼,將來或許能用在什麼地方。這會增加大家對技術的興趣、理解和靈活運用,會讓大家學的更好。這挺重

  原始問題連結:Core algorithms deployed

  感謝吳峰光對本文的審校。

相關文章