創作時間:2019-03-17

作者:周林

版權所有,轉載請聯繫作者獲取授權、並註明作者與出處

今日頭條同步鏈接

周林的簡書主頁

周林的CSDN博客主頁

周林的知乎主頁

寫在前面的話

在互聯網、大數據、人工智慧火爆的今天,「演算法」這個詞幾乎婦孺皆知,業已成為「高薪」「牛X」的代名詞。應不少朋友的邀請,特連載本系列,旨在用最通俗的方式——「講人話、無廢話、看得懂、用得上」——將位於神龕之上的演算法送進尋常百姓家。

本篇作為系列的第一篇,採用「What、Why、How」文章結構,來給大家普及一下演算法的基本概念(也糾正一些朋友的錯誤概念)。

What is Algorithm?(演算法是個什麼鬼 )

為不落入俗套,本文不會重複wiki上「演算法」的官方定義,而採用啟發式結構來闡述演算法的本質:

試想平時在遇到問題的時候,我們是如何解決的。樸素而廣泛的過程方法論如下:

1. 重新定義問題,結構化描述

2. 根據重定義,歸類問題

3. 根據問題類別,做經驗匹配

4. 根據匹配結果,分支處理:若匹配,採用經驗方法;若匹配不上,設計開發新方法

5. 迭代更新經驗庫,增強面向未來問題的能力

與演算法相關的就是上面的第3步~第5步。

簡單來說,演算法本質是:解決某類問題的方法。如果方法已經在經驗庫裏了,直接拿來主義,也就是「既有演算法」;如果不在,那麼設計開發的新方法,新方法就是「新演算法」。當然還有一種情況:雖然經驗庫裏有針對該類問題的方法了,但是設計開發了一個更有效的新方法,那麼也稱為「新演算法」。

下面來對幾個關鍵點進行闡述:

什麼是「更有效的演算法」?

「更有效」的背後邏輯其實比較的就是「代價」,或者稱為「開銷」。經濟上衡量就是成本,它分為兩個維度:時間成本和資源成本。資源成本在計算機上的體現就是硬碟、內存、CPU等一系列硬體資源開銷。對這些硬體資源開銷進一步抽象,就是空間成本。從學科分類上講,演算法其實屬於計算數學,計算數學屬於應用數學。用專業術語來描述時間成本與空間成本,就是計算複雜度,很自然地,它也有兩個維度:時間複雜度和空間複雜度。描述複雜度的數學符號是O()。後面我們會詳細介紹O()的表達。

綜上所述,所謂的「更有效」的演算法,指的就是時間複雜度或者空間複雜度更優的演算法。

為什麼要「重新定義問題,結構化描述」?

把人腦也看做一臺機器的話,很顯然這臺機器的運行方式和效率與計算機有所不同(儘管現在的機器學習在儘可能地模擬人腦的機理,但是兩者至少在現階段還有本質不同)。人腦在連續信號和非結構化場景下的處理能力是卓越的,但是計算機只能處理離散信號,並且必須最終轉化成結構化數據才能進行處理(儘管現在的機器學習可以通過自我學習來將數據結構化)。用一張圖來描述這個過程就是:

Why to use Algorithm?(演算法有什麼鬼用)

從上面對解決現實問題的過程方法論的描述中,其實已經可以看出演算法的價值就在於:經驗的重用。套用一句IT行話就是「不要重複製造輪子」。好了,既然現在你已經對演算法有了大致的感性認識,那麼接下來根據人類的學習習慣,就來看看抽象的演算法概念,在現實裏到底「長什麼模樣」。

很多人認為「演算法 = 程序或者程序」,這其實是一個狹義的理解。如前面所說的,演算法的本質是解決某類問題的方法,而程序或者代碼只是方法的一種表達形式而已。你也可以用自然語言或者偽代碼來進行表達演算法。

演算法的「模樣」(應對電燈不工作的演算法——代碼方式):

public STATUS_CODE lamp_issue_handler() {
STATUS_CODE ret_val = UNKNOWN_ISSUE;
if (!isPowerOn(this)) {
ret_val = powerOn(this) ? NOT_POWER_ON_ISSUE : POWER_ISSUE;
}
else if(!isBulbCrash(this)) {
ret_val = replaceBulb(this) ? BULB_CRASH_ISSUE : REPLACE_ISSUE;
}
else {
ret_val = fixBulb(this) ? BULB_FIXABLE_ISSUE : FIX_FAILURE_ISSUE;
}
return ret_val;
}

演算法的「模樣」(應對電燈不工作的演算法——自然語言方式):

首先檢查電源是否接好了:沒有接好,接上。

如果接上了仍然不工作,看看燈泡是否燒壞了:如果是,換個新燈泡

如果燈泡沒有燒壞,修理燈泡

演算法的「模樣」(應對電燈不工作的演算法——流程圖方式):

How to use Algorithm?(如何使用演算法)

演算法的本質就是方法,計既然是方法,就是一系列的操作;既然是操作,就必然有作用對象。在軟體程序設計中,這樣的作用對象就是「數據結構」。怎麼來理解數據結構呢?前面我們講到了,解決問題的第一步就是要將問題結構化描述。結構化描述的本質就是利用一系列便於操作的「基礎元素」來表達。

那麼怎樣的「基礎元素」是便於操作的呢?

首先我們要清楚,操作的主體是誰。從上一段的闡述來看,這個主體貌似是演算法,但是我們注意,演算法不是憑空運行的,是要在計算機上運行的,所以歸根結底,操作的主體是計算機。所以,這裡所謂的「便於操作」指的是便於計算機運行。

計算機運行有兩個維度:硬體維度和軟體維度。

從硬體維度看,學過計算機組成原理就知道,程序是在計算機的CPU高速緩存和內存中運行的。對應的存儲結構,通常都是線性的。為了充分提升線性結構的性能優勢,硬體廠商(如CPU廠商)在設計硬體時,就抽象了針對一些結構(如堆棧)的操作(如壓棧、出棧),所以很自然地,這樣的結構就應該作為數據結構。

從軟體維度看,我們編寫的應用程序一般不會直接運行在硬體之上,而是運行在操作系統、運行時或者虛擬機(如JVM)之上。所以操作系統、運行時或者虛擬機已經抽象的結構(如數組、隊列、樹、圖等),也應該作為數據結構。

上面贅述了這麼多,其實就是要表達一個觀點:演算法是要配合數據結構的,拋開數據結構談演算法就是無源之水、無根之樹。

看到這裡,我想你一定徹底明白,為什麼圖靈獎得主尼古拉斯·沃斯會提出那個著名的等式了:程序 = 演算法 + 數據結構。

寫在最後的話

看到這裡,相信你已經對演算法這個概念已經不再陌生,它對於你而言也不再高高在上。

無論在大學學習,還是在工作中,大家都幾乎被一種說法反覆洗腦:演算法非常重要,它是計算機的靈魂。在這裡,我想糾正一下這個錯誤的觀點。首先,廣義的演算法不僅僅只是軟體演算法;再次,計算機系統不僅僅只是由軟體構成,還有硬體。硬體涉及到材料科學、製造工藝等一系列技術,這些是不能簡單被演算法替代的。所以,脫離上下文、一味強調演算法的重要性是耍流氓。

本原創系列同步在以下自媒體上更新,敬請關註:

今日頭條:

【原創連載】演算法素顏(第1篇):走下神壇吧!演算法?

www.toutiao.com

簡書:

cubie_zhou - 簡書?

www.jianshu.com
圖標

CSDN:

https://blog.csdn.net/jintianyishiyeai?

blog.csdn.net


推薦閱讀:
相關文章