《從入門到放棄》數據結構和算法 1- 算法的引入和算法時間復雜度


1. 簡介 

  最近由於快過年了,不是很忙碌了,人心浮動,很多都請假了,現在終於有時間來系統學習下和惡補一下常見數據結構和算法的知識,所以,還是通過記錄筆記放在博客的方式來督促自己學習。同時和小伙伴們分享一下學習心得與體會。算法對於很多程序員都接觸不到的,何況是一個測試人員。但是面試過程中,多多少少都有算法題的面試。所以,學習算法,短期來看是為了跳槽准備,長期來看,是鍛煉一個人解決問題的思路的提升的一個途徑。

2. 算法的引入

  來看一個問題:如果 a+b+c = 1000, 且 a^2 + b^2 = c^2(勾股定理),如何求出所有a b c的組合。

2.1 問題分析:

  上面告訴兩個條件,從數學角度來說,上面有3個未知數,只有兩個表達式條件,我們第一反應是轉換成二元二次方程來解答。這里我們是計算機通過代碼來解決問題。字面意思就是 a的取值范圍是0到1000, b的取值范圍是0到1000, c的取值范圍是0到1000, 然后加上題目的兩個表達式條件,利用for嵌套循環,計算機肯定能幫我們找出a b c的取值。

2.2 代碼實現:

  根據上面的分析,python代碼實現如下:

2.3 參考代碼:

# coding=utf-8
# 1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行

# 2.注釋:包括記錄創建時間,創建人,項目名稱。
'''
Created on 2020-1-02
@author: 北京-宏哥
Project:《從入門到放棄》數據結構和算法 1- 算法的引入和算法時間復雜度
'''
# 3.導入模塊

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 + b + c == 1000 and a**2 + b**2 == c**2:
                print("a, b, c: %d, %d, %d" % (a, b, c))
end_time = time.time()
print(end_time - start_time)

2.4 運行結果:

  其實上面代碼思路也是用到了一個方法,叫枚舉法,就是一個一個列出來去嘗試,不行,換下一個值繼續去匹配。

  運行代碼后,控制台打印如下圖的結果:

  也就是差不多花費三分鍾(117秒多),找出了符合條件的a b c的四種取值組合。如果沒有計算機,人也是可以根據這個思路,一步一步去算,只不過時間更是不知道有多慢。這個時間開銷,我們很不滿意,對用戶來說,還是太慢了。有沒有什么辦法提升以下計算效率。

2.5 優化上面代碼:

  根據數學知識,我們用代碼實現二元二次方程的思路,c = 1000 - a - b; 來減少第三層嵌套for循環。

2.5.1 代碼實現:

2.5.2 參考代碼:
# coding=utf-8
# 1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行

# 2.注釋:包括記錄創建時間,創建人,項目名稱。
'''
Created on 2020-1-02
@author: 北京-宏哥
Project:《從入門到放棄》數據結構和算法 1- 算法的引入和算法時間復雜度
'''
# 3.導入模塊

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:
            print("a, b, c: %d, %d, %d" % (a, b, c))
end_time = time.time()
print(end_time - start_time)
2.5.3 運行結果:

  運行代碼后,控制台打印如下圖的結果

  這么一看,發現時間縮短了不到2秒,這個計算效率大大提升。同樣解決一個問題,由於我們第二種方法減少了一次for循環嵌套,導致計算效率提高了很多倍,這個就是算法的重要性。

3. 什么是算法

      算法是計算機處理信息的本質,因為計算機程序本質上是一個算法來告訴計算機確切的步驟來執行一個指定的任務。一般地,當算法在處理信息時,會從輸入設備或數據的存儲地址讀取數據,把結果寫入輸出設備或者某個存儲地址供以后再調用。算法是獨立存在的一種解決問題的方法和思想。對於算法而言,實現的編程語言並不重要,重要的是思想。

算法的五大特性:
1:輸入:算法具有0個或多個輸入
2:輸出:算法至少有一個或多個輸出
3:有窮性:算法在有限的步驟之后會自動結束而不會無限循環,並且每一個步驟可以在可接受的時間內完成
4:確定性:算法內的每一步都有確定的含義,不會出現二義性。
5:可行性:算法的每一步都是可行的,也就是說每一步都能執行有限的次數完成。

4. 時間復雜度和大O表示法

  上面我們通過兩個方法來求出a b c的取值組合,第二個方法比第一個方法,從時間效果來看,快很多,所以我們很容易得出結論,第二個算法比第一個算法效率要高。那么算法是通過時間來衡量,確實最直觀地,我們從時間上來看到算法和算法之間的效率不同。但是,單靠時間是不可靠的,例如,同一個算法,在一個I7的CPU上運行和拿到一個1995年之前的個人PC電腦上運行,這種時間來比較就有點不合適了。所以,我們一般從算法的執行計算數量這個維度去考察算法的效率。執行數量可以這么理解,上面3個for循環嵌套的代碼,每一行代碼都有確定的執行步驟數量,所有代碼行的執行步驟數量相加,就得到了這個算法的執行步驟數量。因為每台機器要執行這么多步驟數量大體相同,所以這個執行步驟數量就拿來衡量算法的效率。

  我們假定計算機執行算法每一個基本操作的時間是固定的一個時間單位,那么有多少個基本操作就代表會花費多少時間單位。對於不同機器而言,確切的單位時間是不同的,但是對於算法進行多少個基本操作,在規模數量級上說卻是相同的。由此可以忽略機器環境影響而客觀的反應算法的時間效率。

對於算法的時間效率,我們可以用“大O記法”來表示。
“大O記法”:對於單調的整數函數f,如果存在一個整數函數g和實常數c>0,使得對於充分大得n,總有
f(n)<=c*g(n),就說函數g是f得一個漸進函數(忽略常數),記作為f(n)=O(g(n)),也就是說在趨向無窮得
極限意義下,函數f的增長速度收到函數g的約束,亦函數f與函數g的特征相似。

時間復雜度:假設存在函數g,使得算法A處理規模為n的問題實例所用時間為T(n)=O(g(n)),則稱O(g(n))為算法A
的漸進時間復雜度,簡稱時間復雜度,記為T(n)

5. 如何理解“大O記法”

  我們通過“大O記法”的定義,我們來計算下上面 a b c這題的第一種代碼實現方式的時間復雜度的計算過程。

  我們根據上面這個圖代碼對應行來分析(第4到8行代碼),先分析每行代碼執行步驟數目。

  分析過程:

  第4行:a的取值范圍是0到1000,所以這個for循環要執行1000次

  第5行:b的取值范圍是0到1000,所以這個for循環要執行1000次

  第6行:c的取值范圍是0到1000,所以這個for循環要執行1000次

  第7行:如果不細分步驟,第7和第八兩行當作2個步驟,如果細分,a + b + c是一個步驟, 判斷a + b + c ==1000是一個步驟,a**2是一個步驟,所以細分,第七行存在需要執行 8個步驟數目。

  這樣,我們把每一行代碼需要執行步驟次數計算出來是

  T = 1000 * 1000 * 1000 * 8
  簡寫成 T = 8*1000^3
  如果,這里把1000改成n, 把這個問題規模擴大,這個算法的時間復雜度可以寫成

  T(n)= 8*n^3
  我們在計算時間復雜度的時候,只關注大頭部分,會去掉旁支末節部分,一般我們可以這樣認為 n^3和1000*n^3是等價,所以我們上面文章開頭寫的第一種枚舉法的時間復雜度是 O(n^3)。

  根據這個時間復雜度計算原則,我們計算第二種算法的時間復雜度為O(n^2),這個比第一個效率要高,當然如果時間復雜度為n^1,那么這個算法效率就更高。

6.小結

  好了,今天的分享就到這里吧!!!謝謝各位的耐心閱讀。有問題加群交流討論!!!

您的肯定就是我進步的動力。如果你感覺還不錯,就請鼓勵一下吧!記得隨手點波  推薦  不要忘記哦!!!

別忘了點 推薦 留下您來過的痕跡

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM