我們為什麼要思考演算法

2021-06-29 16:00:08 字數 2773 閱讀 7581

源頭

「演算法」的中文最早出現在中國漢代的數學名著《周髀算經》中。《周髀算經》卷上有:「數之法出於圓方。圓出於方,方出於矩。矩出於九九八十一」。意思是: 算數的方法都出於對圓、對方的計算,其中圓出於方(圓形面積=外接正方形x圓周率/4),方出於矩(正方形源自兩邊相等的矩),矩的計算出於九九八十一 (長乘寬面積的計算依自九九乘法表)。追溯回去,在春秋戰國時代,《九九乘法歌訣》已經開始流行起來。話說,自從卡梅倫被8×9等於多少問呆以後,英國教育部就開始聘請上海的小學數學老師赴英訓練小九九了……

chinese teachers bring the art of maths to english schools

大資料在西方,真正推動演算法傳播的是乙個居住在巴格達的阿拉伯人,al khwarizmi,他引進了印度更為先進的十進位制數字系統。al khawarizmi展示了加、減、乘、除,乃至平方根和圓周率π的計算步驟。這些步驟的特點是:簡單、無歧義、有效、有窮步驟、正確。數百年後,當十進位制阿拉伯數字系統在歐洲廣泛應用時,人們便創造出al khwarizmi 的拉丁化寫法「algorithm」,來描述這種有規可循的數字計算行為。

演算法的定義

究竟什麼是演算法呢,字面理解,就是計算的辦法或法則。這裡的計算不單指加減乘除等算術運算,而是廣義的做任何事情的計算。辦法和法則,則意味著使用它可以解決眼前的問題。

就拿我們喜聞樂見的世界盃比賽來說,每四年一屆比賽的目的就是選出此時世界上踢球最牛掰的那個國家。在200多個國家裡頭,如果用單迴圈聯賽賽制,也就是每個隊都必須和另外所有隊踢一場,以此決定本隊成績,假設每隔三天踢一場,最快也要600來天。怎麼樣,累覺不愛吧,還是廣場舞來的更收放自如些。對於比賽組織者來說,明智的策略當然是先在幾個大洲裡選出幾個屈指可數的球隊,然後大家聚在一起,乙個月內論出高下,這時甚至還要再分成幾個組,每組最強的幾個隊才能突出重圍,踏上冠軍之路。

道理上來講,最好的球隊,無論哪種賽制,總是會脫穎而出的;而上述這種優中選優的方式,難度和開銷就降低許多了。上述過程有乙個特定叫法:分治,也就是將乙個大問題(尋找全世界的最佳球隊),分解成多個型別相同但規模更小的問題(尋找乙個大洲的最佳球隊),如果小問題得以解決,那麼大問題就更加容易解決了(各大洲最佳球隊pk一下,就知道世界冠軍的獎盃,花落誰家了)。這只是生活中眾多演算法應用的乙個例子,那麼由事實到抽象拔高出來乙個完備的字典式定義,對應用和分析者來說,其實無太多必要。事實上,演算法的定義也因看待的角度不同而不同。

如果你是個哲學家:演算法是解決乙個問題的抽象行為序列。

如果你是個碼農:演算法是乙個計算過程,它接受一些輸入,並產生某些輸出。

如果你喜歡高大上:演算法是解決乙個精確定義的計算問題的工具。

但他們共同強調了一點:演算法的不變式,即演算法必須能夠讓人一步一步照著執行。

演算法的核心

演算法是解決問題的辦法或法則。但解決乙個問題不一定只有一種辦法,不同的辦法之間便有了好壞之分。對於解決同乙個問題的不同演算法,我們如何比較它們的好壞呢?

能夠比較的東西當然很多,如模組性、正確性、可維護性、健壯性、友好性、簡易性、可擴充套件性和可靠性等,但這些並不是演算法設計與分析中最為關心的問題。但它們更加像是人類附加在演算法上的外部屬性,因為它們通常依賴於使用或實現演算法的人員的其他方面素質:理解力、表述力、程式設計水平、資料結構的運用與設計技巧等。

那麼演算法的核心或靈魂是什麼呢?也許您已經可以猜到:速度。也就是其解決問題的速度。因為速度往往是區分可行和不可行方案的分水嶺。例如,乙個讓人等上很多年才能執行結束的演算法,就是再正確,也不會令我們滿意。從實際意義上看,這種演算法的正確和不正確並無太大的本質區別。

如果乙個演算法在你的改進下,效率提高了成百上千倍,則當你坐在顯示屏前,所獲得的快感不會亞於很多其他的事情。

堆機器還是拼演算法

說到提公升速度,真壕們會不約而同的移步華強北,血拼記憶體處理器。然而,計算機速度的增長可以多大程度上解決一些簡單問題呢?我們來看乙個經典的例子吧。

我們有乙個描述兔子增長數量的模型:

原點:一對雌雄兔子

如果定義在第n個月的兔子數量為f(n),那麼f(n)個兔子中包含這個月新生的兔子(也就是(第n-1月)兔子總數f(n-1)),和上個月的新生兔子數目(因為上個月的老兔子們這個月已經死掉了),即f(n-2)。所以f(n)=f(n-1)+f(n-2),f(n)的序列叫做斐波那契序列。

那麼我們就開始算f(n)吧,比如說你想知道第30個月時的兔子數量,那麼f(30)=f(29)+f(28),那麼f(29)=f(28)+f(27),我們一直分解下去,最後變成數數一共多少個f(0)了。我們寫段**,執行它,約朋友出去打個撞球,回來也許可以得到答案。若你眼光長遠,想知道第300個月時的兔子數量,那麼你必須要尋找其它演算法了。因為f(0)的數量太龐大了。這種天真遞迴解法,事實上要對f(0)進行指數級次的運算。聰明的你會進一步發現斐波那契數列是乙個二階遞推數列,於是最後可以用對數級次運算搞定f(300),這裡細節省略,感興趣的話我們會在後續詳細討論。

問題是,在硬體效能愈加強悍的今天,大規模運算或者大資料的實踐者們,時常認為更快的演算法也許沒有必要。那麼我們來看斐波那契數的天真遞迴解法,它的時間複雜度為1.6的n次方,即計算f(n+1)的時間約是計算f(n)的1.6倍。按照摩爾定律計算效能每18個月翻倍的速度,每過一年,我們只能多計算乙個未知的斐波那契數。

我們說ibm的roadrunner 超級計算機效能為nec的earth simulator的30倍,但這也僅僅意味著roadrunner比後者在同樣的時間下以指數級複雜度多算7個數。但如果使用log(n)複雜度的演算法,那麼roadrunner可多算10的30次方個斐波那契數。

所以,演算法書其實不全是用來墊咖啡杯的……改進演算法比起提公升硬體速度的效果,還是很顯著的,不是嗎。

結語對演算法效率的追求,在任何場景中,都可以給你帶來意想不到的驚喜。對於產品的突破、開銷的降低、技術氣氛的凝聚,還有什麼方式比思考與重視演算法來的更加有效與唯美呢?

系統思考 我們為什麼要學習系統思考?

我們一直接受的教育是線性思考,a影響了b,b影響c。jason最近一直在寫的是系統思考,a影響b,b也同時影響b,是乙個迴圈。那系統思考對於我們的意義到底在 對於個人,對於組織,對於企業,我們為什麼要學習系統思考?我想系統思考對於個人而言,不管是工作還是生活,我覺了一些案例 個人學習 從本質上講,個...

為什麼我們要放棄Subversion

subversion 曾經是我們親密無間的戰友,但自 從一年前部分團隊成員去了美國,我們和subversion的關係就開始出現了裂痕,首先是將subversion伺服器架設在美國後,中國開發人員頻繁 進行的一些操作變得非常緩慢,本來通過追溯 歷史便可找出原因的問題,卻因為網速緩慢,導致開發者將大量的...

我們為什麼要擁抱Linq?

linq 源起 net的設計者在類庫中定義了一系列的擴充套件方法,方便使用者操作集合物件,這些擴充套件方法構成了linq的查詢操作符。是什麼?linq,語言整合查詢 language integrated query 是一組用於c 和visual basic語言的擴充套件。它允許編寫c 或者visu...