第九講 效率和增加量級python
咱們的首要目標就是讓代碼正常運行,可以計算出正確的答案。儘管如此,咱們仍是但願代碼可以達到第二個目標,即高效地進行計算。一般而言,第一個目標更加劇要,但有時候第二個目標也十分重要,同時咱們須要平衡計算的複雜度和理解代碼的複雜度。git
複雜度 :算法
咱們假設一個基本步驟就是一個操做,而這個操做的用時老是同樣的,以後咱們只需專一於數清楚計算函數時所執行的基本步驟便可。測量複雜度咱們只關心增幅最快的項,而且忽略係數。app
例:函數
def f(x): for i in range(1000): ans = i for i in range(x): ans += 1 for i in range(x): for j in range(x): ans += 1 #這一段代碼的步驟總數爲1000+2x+2x^2,對於其複雜度咱們只關心增加最快的項即2x^2, #同時咱們不關心繫數就獲得該函數的複雜度x^2。對於複雜度咱們使用大寫的O表示, #則該函數的複雜度表示爲O(n^2)
常見的複雜度(耗時,由低到高):code
O(1) 常數時間算法,運算時間不隨運算量的增長而增長排序
O(log n) 對數時間算法索引
O(n) 線性時間算法內存
O(n * log n) 對數-線性時間算法element
O(n^c) 多項式時間算法
O(c^n) 指數時間算法
# 對數時間算法 def intToStr(i): digits = '0123456789' if i == 0: return '0' result = '' while i > 0: result = digits[i%10] + result i = i/10 return result # 運算步驟 log10(i) 複雜度 O(log(i))
# 線性時間算法 def addDigits(s): val = 0 for c in s: val += int(c) return val #總步驟 len(s) 複雜度O(len(s)) def fact(n): if n == 1: return 1 else: return n*fact(n-1) #總步驟 n 複雜度O(n)
# 多項式時間算法 def isSubset(L1, L2): for e1 in L1:? matched = False for e2 in L2: if e1 == e2: matched = True break if not matched: return False return True # 總步驟爲 len(L1)*len(L2) 這裏咱們只考慮最壞的狀況即 len(L1) == len(L2) # 因此這個算法的複雜度爲 O(len(L1)^2) def intersect(L1, L2): tmp = [] for e1 in L1: for e2 in L2: if e1 == e2: tmp.append(e1) res = [] for e in tmp: if not(e in res): res.append(e) return res # 總步驟爲 len(L1)*len(L2) + len(L1) 咱們只關心增加最快的項, # 因此這個算法的複雜度爲O(len(L1)^2)
# 指數時間算法 def genSubsets(L): res = [] if len(L) == 0: return [[]] #list of empty list smaller = genSubsets(L[:-1]) # get all subsets without last element extra = L[-1:] # create a list of just last element new = [] for small in smaller: new.append(small+extra) # for all smaller solutions, add one with last element return smaller+new # combine those with last element and those without # 總步驟爲 2^(n-1)+..+...+2^0, 因此該算法的複雜度爲O(2^n)
第十講 內存和查找
咱們能夠同過間接索引的辦法來查找須要的數據。
通常的對於無序列表的查找,時間複雜度爲O(n),而有序列表可經過二分查找將時間複雜度縮短爲O(log(n)),因此咱們能夠經過對列表排序,使其變成有序的,再使用二分查找,只要咱們找到一種排序算法使得 sort(L) + log(len(L)) < len(L) 則運算效率將會更高,並且對於屢次查找,效率的提高就更加明顯了。
# 選擇排序 def selSort(L): for i in range(len(L) - 1): minIndx = i minVal= L[i] j = i + 1 while j < len(L): if minVal > L[j]: minIndx = j minVal= L[j] j += 1 temp = L[i] L[i] = L[minIndx] L[minIndx] = temp # 這裏選擇排序的時間複雜度爲 O(len(L)^2),顯然選擇排序的效率並不高, # 咱們須要更好的排序方法
# 歸併排序 # 將列表分爲兩部分,分別排序,而後將兩個列表合併 def merge(left, right, compare): result = [] i,j = 0, 0 while i < len(left) and j < len(right): if compare(left[i], right[j]): result.append(left[i]) i += 1 else: result.append(right[j]) j += 1 while (i < len(left)): result.append(left[i]) i += 1 while (j < len(right)): result.append(right[j]) j += 1 return result import operator def mergeSort(L, compare = operator.lt): if len(L) < 2: return L[:] else: middle = int(len(L)/2) left = mergeSort(L[:middle], compare) right = mergeSort(L[middle:], compare) return merge(left, right, compare) # merge的複雜度爲 O(len(L)) mergesort 的複雜度爲 O(len(L) * log(len(L))) # 簡化爲 O(n*log(n)) 歸併排序相比於選擇排序效率提升了不少,這樣的效率咱們能夠接受
hash:
哈希的思想以下。給定一個鍵,好比,它指向了一個字典的一個元素。一個哈希函數會將這個鍵轉換爲一個整數,而後它用這個整數來檢索列表。哈希函數可讓咱們查找東西,這個查找過程的用時幾乎獨立於字典的規模。