【《演算法基礎》筆記02_Ch01演算法:效率、分析和階】重難點摘錄及習題選做

Salas發表於2017-10-01

感謝貓冬同學給予我這個再次學習演算法的機會,本系列筆記主要以章節形式劃分,內容以我認為的重難點及部分習題的探討為主,希望能給各位帶來啟發,在此表示感謝!(更新較慢,但我希望我能堅持寫完整本書的筆記整理,請見諒!)

引言

我學過資料結構的基礎課,記得一開始老師就講時間複雜度,還需要計算一些程式碼中的時間複雜度。當時沒怎麼搞清楚平均、最壞情況時間複雜度等,也就是會數一下迴圈,算一下階,具體要計算平均時間複雜度是完全靠腦洞做的,閱讀了這本書後才明確原來就是數學期望……

書中具體的例子我不會描述得特別詳細,因為我最近也比較忙……請見諒,不過我會把我的想法加進來,不一定正確,但希望能給您一些小小的啟發。

另外,比較有意思的一件事是我的資料結構的老師說過,所有的n^2的演算法都可以優化為nlog n,我覺得可能和遞迴有關係,但是老師沒有明確給出依據,如果您對這個問題有了解或者感興趣,歡迎和我討論,謝謝!

1.1_演算法

  1. [P1-P2]引數(parameter)是一個問題中可能在其描述中包含的一些未指定取值的變數。因為問題中包含引數,所以它代表著一類問題,每對引數進行一次賦值,就得到其中一個問題。每次特定賦值就稱為該問題的一個例項(instance)。
  2. [P4]當演算法的名字看起來與其返回值很吻合時,我們就將演算法寫為函式(function)。否則,就將其寫為過程(procedure,C++中的void函式),並使用引用引數(reference parameter,也就是按地址傳遞的引數)來返回值。如果引數不是陣列,在宣告它時,會在資料型別名的末尾新增一個&符號。它在這裡的意思是:這個引數包含演算法的一個返回值。因為陣列在C++中是自動按引用傳遞的,而且在傳遞陣列時,C++中也沒有使用&符號,所以我們沒有使用&來表示陣列中包含演算法的返回值。相反,由於C++中使用保留字const來防止對所傳遞陣列進行修改,所以我們使用const表示陣列中不包含演算法的返回值。

1.2_開發高效演算法的重要性

1.2.1_順序查詢與二分查詢的對比

1.2.2_斐波那契序列

  1. [P9演算法1.7]若使用迭代來計算斐波那契序列的第n項,則在計算一個值時,將其儲存在一個陣列中,後面再需要它時,就不用再重新計算了。
  2. [P10]演算法1.7是動態規劃方法(dynamic programming approach)的一個例子。

1.3_演算法分析

1.3.1_複雜度分析

  1. [P11]注意“輸入規模”不是“輸入”。根據情況選擇輸入規模的合理度量。
  2. [P11]在確定了輸入規模後,我們選擇某條指令或某組指令,使演算法完成的總工作量大體與這條指令或這組指令的執行次數成正比。我們將這條指令或這組指令稱為演算法的基本運算。
  3. [P11]基本運算的選擇沒有一成不變的規則,大多由個人判斷力和經驗決定。
  4. [P11]有時可能希望考慮兩種不同的基本運算。例如,在通過比較鍵來完成排序的演算法中,經常希望將比較指令和賦值指令分別看作基本運算。
  5. [P11]在某些情況下,基本運算的執行次數不僅取決於輸入規模,還與輸入值(比如順序之於順序查詢)有關。
  6. [P11]T(n)稱為演算法的所有情況時間複雜度(every-case time complexity),對T(n)的計算稱為所有情況時間複雜度分析。T(n)定義為:對於輸入規模n的一個具體取值,該演算法執行基本運算的次數。
  7. [P12]W(n)稱為最差情況時間複雜度(worst-case time complexity)。W(n)定義為:當輸入規模為n時,該演算法執行其基本運算的最大次數。如果存在T(n),則W(n)=T(n)。
  8. [P12]A(n)稱為演算法的平均情況時間複雜度(average-case time complexity)。A(n)定義為:對於輸入規模n,該演算法執行基本運算的平均次數,也就是均值(期望)。為了計算A(n),需要為所有規模為n的不同輸入指定相應概率。和W(n)一樣,如果存在T(n),則A(n)=T(n)。
  9. [P13]只有當實際情景不會與“平均值”偏離太多時(也就是說標準偏差很小時),才能說這個平均值是“典型值”。
  10. [P13]B(n)成為演算法的最佳情況時間複雜度(best-case time complexity)。B(n)的定義為對於輸入規模n,該演算法執行基本運算的最小次數。和W(n)、A(n)的情景一樣,如果T(n)存在,則B(n)=T(n)。
  11. [P14]對於不存在T(n)的演算法,我們執行W(n)和A(n)分析的頻率遠高於B(n)的分析。
  12. [P14]一般情況下,複雜度函式(complexity function)可以使任何一個將正整數對映為非負實數的函式。

1.3.2_理論應用

1.3.3_正確性分析

1.4_階

1.4.1_階的直觀介紹

  1. [P16]與僅知道階數相比,若準確地知道時間複雜度,則可掌握更多資訊。比如對於特定的例項,階數高的演算法也可能效率高於階數低的演算法(函式圖象交點)。

1.4.2_階數的嚴謹介紹

  1. 關於大O、Ω、小o、Θ等,請參考微積分中的有關分析即可。證明只需嚴格對照定義。
  2. [P19]O(n^2)的複雜度函式並非一定要有二次項,只要它的曲線最終低於某個純二次函式即可。
  3. [P20]例1.17是一個不錯的反證法的例子。
  4. [P21定義]“大O”是指必然存在“某個”正實常數c,使此上限成立。而“小o”的定義說該上限必須對於“每個”正實常數c成立。
  5. [P21]定理1.2。[P22]例1.20,o(f(n))和O(f(n))-Ω(f(n))不一定是同一個集合。
  6. [P22]階的性質:
    1. g(n)∈O(f(n)), 當且僅當f(n)∈Ω(g(n))
    2. g(n)∈Θ(f(n)), 當且僅當f(n)∈Θ(g(n))
    3. 若b>1, a>1, 則log_{a}n∈Θ(log_{b}n), 即所有對數時間複雜度都屬於同意複雜度類別, 我們用Θ(lgn)代表這一類別
    4. 若b>a>0, 則a^n∈o(b^n), 即所有指數複雜度函式都不在同一複雜度類別中
    5. 對於所有a>0, 有a^n∈o(n!), 即n!比任何指數複雜度函式都要差
    6. 複雜度類別的順序:
      Θ(lgn) Θ(n) Θ(nlgn) Θ(n^2) Θ(n^j) Θ(n^k) Θ(a^n) Θ(b^n) Θ(n!) 其中k>j>2, b>a>1. 若複雜度函式g(n)所在類別位於f(n)所在類別的左側,則g(n)∈o(f(n))
    7. [線性組合]若c≥0, d>0, g(n)∈O(f(n)), 且h(n)∈Θ(f(n)),則c×g(n)+d×h(n)∈Θ(f(n))

1.4.3_利用極限計算階

  1. [P24]例1.26的證明。

1.5_本書概要

1.6_習題

所有的題目我都看過了,比較有意義的是下列題目,推薦大家完成。未來我將補上以下習題的我的答案:

  1. [1.1節]#3,6-7
  2. [1.3節]#10-11,14
  3. [1.4節]#22-27
  4. [補充習題]#29-32,34-44

相關文章