第九讲 效率和增加量级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:
哈希的思想以下。给定一个键,好比,它指向了一个字典的一个元素。一个哈希函数会将这个键转换为一个整数,而后它用这个整数来检索列表。哈希函数可让咱们查找东西,这个查找过程的用时几乎独立于字典的规模。