一. 什麼是算法分析?html
- 案例引入:剛接觸編程的學生常常會將本身編寫的程序和別人的程序作比對,獲取在比對的過程當中會發現雙方編寫的程序很類似但又各不相同。那麼就會出現一個有趣的現象:兩組程序都是用來解決同一個問題的,可是兩組程序看起來又各不相同,那麼哪一組程序更好呢?例以下述代碼:python
def sumOfN(n): theSum = 0 for i in range(1,n+1): theSum = theSum + i return theSum print(sumOfN(10))
def foo(tom): fred = 0 for bill in range(1,tom+1): barney = bill fred = fred + barney return fred print(foo(10))
分析:算法
- 哪一個函數更好,答案取決於你的標準。若是你關注可讀性,函數 sumOfN 確定比 foo 好。事實上,你可能已經在介紹編程的課程中看到過不少例子,他們的目標之一就是幫助你編寫易於閱讀和理解的程序。然而,在本課程中,咱們對算法自己的表示更感興趣(固然咱們但願你繼續努力編寫可讀的,易於理解的代碼)。編程
- 算法分析是基於每種算法使用的計算資源量來比較算法。咱們比較兩個算法,說一個比另外一個算法好的緣由在於它在使用資源方面更有效率,或者僅僅使用的資源更少。從這個角度來看,上面兩個函數看起來很類似。它們都使用基本相同的算法來解決求和問題。數據結構
- 問題:a+b+c = 1000 a**2 + b**2 = c**2 (a,b,c均爲天然數),求出a,b,c可能的組合?函數
for a in range(0,1001): for b in range(0,1001): for c in range(0,1001): if a**2+b**2 == c**2 and a+b+c==1000: print(a,b,c)
for a in range(0,1001): for b in range(0,1001): c = 1000 - a - b if a**2+b**2 == c**2 and a+b+c==1000: print(a,b,c)
分析:很明顯上述兩中問題的解決方案是不一樣的。那如何斷定上述兩種解決方案(算法)的優劣呢?有同窗會說,觀察計算兩種算法使用耗費計算機資源的大小和它們的執行效率呀!可是我想說的是,一些較爲複雜的算法,它耗費計算機資源的大小和執行效率咱們很難可以直觀的從算法的編碼上分析出來。因此咱們必須採用某種量化的方式求出不一樣算法的資源耗費和執行效率的具體值來斷定算法之間的優劣。問題來了,如何進行量化計算呢?方法有兩種:編碼
- 方法1:計算算法執行的耗時(不推薦)。spa
import time start_time = time.time() for a in range(0,1001): for b in range(0,1001): for c in range(0,1001): if a**2+b**2 == c**2 and a+b+c==1000: print(a,b,c) end_time = time.time() print(end_time-start_time) #執行結果爲: 0 500 500 200 375 425 375 200 425 500 0 500 1221.7778379917145
import time start_time = time.time() for a in range(0,1001): for b in range(0,1001): c = 1000 - a - b if a**2+b**2 == c**2 and a+b+c==1000: print(a,b,c) end_time = time.time() print(end_time-start_time) #執行結果爲: 0 500 500 200 375 425 375 200 425 500 0 500 1.410386085510254
注意:單靠執行時間能夠反應算法的效率嗎?不能。跟機器(執行環境)存在很大關係。設計
方法2:計算算法的時間複雜度(推薦)code
- 時間複雜度:量化算法須要的操做或者執行步驟的數量。
四.時間複雜度
- 咱們試圖經過算法的執行時間來斷定算法的優劣。可是僅僅根據執行時間斷定算法優劣有些片面,由於算法是獨立於計算機的。重要的是量化算法須要的操做或者步驟的數量。選擇適當的基本計算單位是個複雜的問題,而且將取決於如何實現算法。對於先前的求和算法,一個比較好的基本計算單位是對執行語句進行計數。
- 在 sumOfN 中,賦值語句的計數爲 1(theSum = 0) 加上 n 的值(咱們執行 theSum=theSum+i 的次數)。咱們經過函數 T 表示 T(n)=1+n。參數 n 一般稱爲「問題的規模」,咱們稱做 「T(n) 是解決問題大小爲 n 所花費的時間,即 1+n 步長」。在上面的求和函數中,使用 n 來表示問題大小是有意義的。咱們能夠說,100,000 個整數和比 1000 個問題規模大。所以,所需時間也更長。咱們的目標是表示出算法的執行時間是如何相對問題規模大小而改變的。
- 計算機科學家更喜歡將這種分析技術進一步擴展。事實證實,操做步驟數量不如肯定 T(n) 最主要的部分來的重要。換句話說,當問題規模變大時,T(n) 函數某些部分的份量會超過其餘部分。函數的數量級表示了隨着 n 的值增長而增長最快的那些部分。
- 數量級一般稱爲大O符號,寫爲 O(f(n))。它表示對計算中的實際步數的近似。函數 f(n) 提供了 T(n) 最主要部分的表示方法。T(n)的最主要部分是:在上述示例中,T(n)=1+n。當 n 變大時,常數 1 對於最終結果變得愈來愈不重要。若是咱們找的是 T(n) 的近似值,咱們能夠刪除 1。所以T(n)中最主要的部分爲n,所以f(n)=n。所以在sumOfN函數對應的算法的時間複雜度可即爲O(f(n)),即爲O(n)。
- 另一個示例,假設對於一些算法,肯定的步數是 T(n)=5n^2+27n+1005。當 n 很小時, 例如 1 或 2 ,常數 1005 彷佛是函數的主要部分。然而,隨着 n 變大,n^2 這項變得愈來愈重要。事實上,當 n 真的很大時,其餘兩項在它們肯定最終結果中所起的做用變得不重要。當 n 變大時,爲了近似 T(n),咱們能夠忽略其餘項,只關注 5n^2 。係數 5 也變得不重要。咱們說,T(n) 具備的數量級爲 f(n)=n^2,或者 O( n^2 )。
分析:前三行賦值語句執行操做步驟爲3,5-8行的執行操做步驟爲3n,結合第四行的外部循環,整個內外循環的執行步驟爲3n^2。9-11行爲2n,12行爲1。最終得出T(n)=3+3n^2+2n+1,簡化操做後T(n)=3n^2+2n+4。經過查看指數,咱們能夠看到 n^2 項是最重要的,所以這個代碼段是 O(n^ 2)。當 n 增大時,全部其餘項以及主項上的係數均可以忽略。
四.數據結構:
- 對於數據(基本類型的數據(int,float,char))的組織方式就被稱做爲數據結構。數據結構解決的就是一組數據如何進行保存,保存形式是怎樣的。
- 案例:須要存儲一些學生的學生信息(name,score),那麼這些數據應該如何組織呢?查詢某一個具體學生的時間複雜度是什麼呢?
- 組織形式1:
- 組織形式2:
- 組織形式3:
- 三種組織形式基於查詢的時間複雜度分別爲:O(n),O(n),O(1)
- 發現:python中的字典,列表,元組自己就是已經被封裝好的一種數據結構啦。使用不一樣的數據結構進行數據的存儲,所致使的時間複雜度是不同。所以認爲算法是爲了解決實際問題而設計的,數據結構是算法須要處理問題的載體。