python演算法——大O表示法
大O表示法
大O表示法的樣子為 O(運算元)
大O表示法指出了演算法的速度有多快
它的好處在於,當我們引用別人的演算法時,瞭解了它的大O表示法會對我們大有益處。(一般情況下用處不大,但瞭解總是好的)
不同的大O執行時間
當要找出一堆數中最大的數時:
1.遍歷所有的數
2.找出最大數
def Max(n): for k in n: max_number = 0 if max_number < k: max_number = k return max_number print(Max([1, 3, 5, 7, 9]))
這裡需要把列表所有的元素都檢查一遍,因此執行時間也就是列表中所有元素檢查一遍的時間。用大O表示法表示為O(n)
當把一堆數按從小到大排列時:
1.找出最小的數,把它加入新列表並從原列表中刪除
2.有多少數執行多少次
def find_min(n): minest = n[0] minest_index = 0 for i in range(1, len(n)): if minest > n[i]: minest = n[i] minest_index = i return minest_index def array(n): NewArr = [] for i in range(len(n)): minest = find_min(n) NewArr.append(n.pop(minest)) return NewArr print(array([9, 7, 5, 3, 1]))
這裡每次都需要遍歷所有數,遍歷的次數為元素的數量。大O表示法為O(n*n),即O(n^2)
*注:即使下一次需要遍歷的數量比上一次少一,但它還是n。
大O表示法中的常量
一些常見的大O表示法
基於每秒執行10次計算的,但計算機執行速度並不是這樣。只是為了有個大概的計量,便於瞭解之間的差別。
常量c
比較一下下面兩個列印列表的函式:
1.列印一個列表
def print_number1(list):
for k in list:
print(k)
print_number1([1, 3, 5, 7, 9])
2.列印一個列表,每列印一個數字休眠1秒
from time import sleep
def print_number2(list):
for k in list:
print(k)
sleep(1)
print_number2([1, 3, 5, 7, 9])
可以看出兩個函式的大O表示法都為 O(n),因為它們都遍歷了一個列表。但是可以明顯感覺到print_number1的執行速度要比print_number2快許多。因為它沒有每次執行後休眠1秒。但從大O表示法來看二者的執行速度是一樣的,但print_number1的執行速度要快。在大O表示法中n指的其實是c*n。
這個c指的是演算法所需的固定時間,被稱為常量。print_number1指的是 10毫秒*n 。
print_number2指的是 1秒*n。
通常這個常量是不被考慮進來的,因為大O執行時間不同,這個常量無關緊要。
舉個例子:
用上圖的簡單查詢和二分查詢來說。
設簡單查詢的執行常量為10毫秒,二分查詢的執行常量為10秒。
當元素數量很多(10億)的時候
二者的執行時間為
簡單查詢:10毫秒*10億=1億秒
二分查詢:10秒*log(10億)=300秒
很明顯二分查詢要比簡單查詢快的多,所以常量基本沒什麼影響。
但有時候常量有的影響可能會很大,比如說上例中列印列表的函式。print_number1的常量比print_number2小,但它們的執行時間都為O(n)
所以print_number1的執行速度更快。實際上print_number1的執行速度確實要快。相對於糟糕情況,遇上平均情況的可能性要大的多。
平均情況與最糟情況
這裡要涉及到快速排序
在快速排序中,效能的高低取決於所選的,基準值。
假如你每次都將第一個元素作為基準值,且元素是有序的,由於快速排序不檢查陣列是否有序,因此它依舊會按照程式執行
注意這裡數列沒有被分為兩半,其中一個子序列總是為空。導致了呼叫棧很長。
如果每次的基準值都是中間元素呢
這裡呼叫棧短很多,因為每次都將陣列分為兩半,很快就到達了基線條件。因此呼叫棧短很多。
以上兩種情況就是最糟情況與最佳情況。
最糟情況下:棧的長度為O(n),每層都涉及O(n)個元素。所以執行時間為O(n*n)
最佳情況下:棧的長度為O(logn),每層涉及O(n)個元素。所以執行時間為O(logn*n)
這裡的最佳情況也就是平均情況。只要你每次選基準值都是隨機的,快速排序的執行時間都為O(logn*n)。