算法系列教程04 - 算法相关的基础概念
本系列前面兩篇講的都是一些背景知識(shí),從這一篇開始我們正式講算法,從算法的一些基本概念講起。
什么是算法
通過上一篇對(duì)圖靈機(jī)原理的講解,我們知道,一個(gè)計(jì)算問題描述的是輸入/輸出之間的關(guān)系,如果根據(jù)給定的輸入能設(shè)計(jì)一個(gè)程序計(jì)算出期望的輸出,就認(rèn)為這個(gè)問題可解。這個(gè)程序的計(jì)算過程就是用算法來描述的,通過算法這個(gè)工具我們就容易設(shè)計(jì)出這樣的一個(gè)程序。
確切地說,算法是有限步驟的計(jì)算過程,該過程取某個(gè)值或集合作為輸入,并產(chǎn)生某個(gè)值或集合作為輸出。
算法的正確與錯(cuò)誤
如果算法對(duì)于每個(gè)輸入都可以正確的停機(jī),則稱該算法是正確的,并稱正確的算法解決了給定的計(jì)算問題。一個(gè)不正確的算法對(duì)于某個(gè)輸入可能根本不停機(jī),也可能以不正確的結(jié)果停機(jī),比如一個(gè)排序問題:
輸入:一個(gè)長(zhǎng)度為n的數(shù)組(a1,a2,a3...) 輸出:輸入數(shù)組排序后的一個(gè)數(shù)組(b1,b2,b3....),滿足:b1≤b2≤b3如果根據(jù)這個(gè)問題設(shè)計(jì)出來的算法交給圖靈機(jī)運(yùn)行輸出的結(jié)果與人們的期望相反(比如輸出:b3,b2,b1…),那么這個(gè)算法就是錯(cuò)誤的。
隨機(jī)訪問機(jī)模型
針對(duì)同一計(jì)算問題,可以設(shè)計(jì)出多種算法,其中有好有壞,我們可以通過預(yù)測(cè)算法需要的資源來篩選出好的算法。雖然有時(shí)我們關(guān)心內(nèi)存、帶寬這類硬件資源,但通常我們度量的是運(yùn)行時(shí)間。
度量算法的運(yùn)行時(shí)間,人們通常用的是隨機(jī)訪問機(jī)模型(Random-Access Machine, RAM)。在 RAM 模型中:
- 指令一條接著一條執(zhí)行的,沒有并發(fā)操作。
- 指令包含了真實(shí)計(jì)算機(jī)的常見指令:算數(shù)指令(加法,減法,乘法,除法,取余等)、數(shù)據(jù)移入指令(裝入,存儲(chǔ),復(fù)制)和控制命令(條件與無條件轉(zhuǎn)移、子程序調(diào)用與返回);
- 每條指令所用的時(shí)間均為常量。
RAM 模型假定的觀點(diǎn)是,運(yùn)行每行偽代碼所需的時(shí)間是一個(gè)常量時(shí)間,雖然真實(shí)計(jì)算機(jī)執(zhí)行一行代碼與另一行代碼需要不同的常量時(shí)間。依此,一個(gè)算法在特定輸入上的運(yùn)行時(shí)間不是指現(xiàn)實(shí)意義的時(shí)間,而是執(zhí)行指令的次數(shù)。
比如下面這段 foo 函數(shù)的代碼:
// prettier-ignorefunction foo(n) {
for (let i = 0; i < n; i++) { // 2n+1 次
console.log('Hello, World!') // n 次
}
return 0 // 1 次
}
上面的代碼需要執(zhí)行 2n + 1 + n + 1 = 3n + 2 次指令,也就是說執(zhí)行時(shí)間是 3n + 2,如果用一個(gè)時(shí)間函數(shù)來表示,就是 T(n) = 3n + 2。
算法的時(shí)間復(fù)雜度
根據(jù) RAM 模型,一個(gè)算法可以在給定的輸入規(guī)模 n 下分析出一個(gè)運(yùn)行時(shí)間的函數(shù) T(n)。研究 T(n) 常用的一種策略是分析輸入規(guī)模 n 增大的情況下 T(n) 的變化(如線性增長(zhǎng)、指數(shù)增長(zhǎng)等)。如果用 f(n) 來表示 T(n) 的增長(zhǎng)速度,那么 f(n) 和 T(n) 的關(guān)系我們約定用一個(gè)大 O 來表示,即:
T(n) = O(f(n))
這就是 大 O 表示法。由于輸入規(guī)模 n 的增長(zhǎng)率與 f(n) 的增長(zhǎng)率是正相關(guān)的,所以稱作 漸近時(shí)間復(fù)雜度(Asymptotic Time Complexity),簡(jiǎn)稱 時(shí)間復(fù)雜度。相對(duì)應(yīng)的,還有空間復(fù)雜度,這里我們不作討論。
當(dāng) n 足夠大時(shí)或趨于無窮大時(shí),T(n) 的常數(shù)部分就變得不重要,我們真正關(guān)心的是運(yùn)行時(shí)間的 增長(zhǎng)量級(jí) 或 增長(zhǎng)率。如果用 f(n) 來表示增長(zhǎng)數(shù)度,上面 foo 示例代碼的增長(zhǎng)速度可以表示為 f(n) = n,把它代入到 T(n) = O(f(n)) 就是:
T(n) = O(n)
這時(shí),我們稱 foo 的時(shí)間復(fù)雜度為 O(n)。
常見的時(shí)間復(fù)雜度有:
- 常數(shù)階 O(1),
- 對(duì)數(shù)階 O(log2^n),
- 線性階 O(n),
- 線性對(duì)數(shù)階 O(nlog2^n),
- 平方階 O(n^2),
- 立方階 O(n^3),
- k 次方階 O(n^k),
- 指數(shù)階 O(2^n)。
隨著問題規(guī)模 n 的不斷增大,上述時(shí)間復(fù)雜度不斷增大,算法的執(zhí)行效率也越低。大 O 表示法只是一種估算,當(dāng)輸入規(guī)模足夠大的時(shí)候才有意義。
注意,大 O 表示法考慮的是最壞的情況。比如,從一個(gè)長(zhǎng)度為 n 的數(shù)組中找一個(gè)值等于 10 的元素,開始遍歷掃描這個(gè)數(shù)組,有可能第 1 次就掃到了,也有可能是第 n 次才掃到。這里最壞的情況是 n 次,所以時(shí)間復(fù)雜度就是 O(n)。
大部分情況下你用直覺就可以知道一個(gè)算法的大 O 表示。比如,如果用一個(gè)循環(huán)遍歷輸入的每個(gè)元素,那么這個(gè)算法就是 O(n);如果是用循環(huán)套循環(huán),那就是 O(n^2),以此類推。
參考:《算法導(dǎo)論,第三版》
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的算法系列教程04 - 算法相关的基础概念的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《巫师 3:狂猎》4.04 版本推出,S
- 下一篇: 给页面减减肥!