引言
定義:算法就是按照一系列有限的步驟正確解決問題的辦法
屬性:
- 正確:就是可以正確的求解問題
- 快速:就是時間復雜度要盡量小
- 有窮性:要在有限個步驟解決問題
- 簡潔
- 通用
漸進分析法為什么可以做到與算法運行硬件環境無關?
算法分析時往往假設輸入規模n足夠大,甚至趨近於無窮大。這樣的假設,意味着我們關注的是算法運算時間的增長率,也就是,隨着輸入規模n的增長,T(n)的增長率。當n趨向於無窮大時,決定T(n)增長率的便是T(n)中的高次項,從而可以忽略T(n)中的低次項以及高次項前的常數項。這些低次項或者高次項前的常數項,往往是機器性能、程序設計語言的性能和編譯器性能等因素產生,而這些在算法時間復雜度分析中都是需要略去的次要因素。
為什么說多項式時間復雜度的算法要優於指數時間復雜度的算法?
漸進分析與python模型
二分搜索
def binary_search(A,k):
first=0
last= len(A)-1
found=False
while first<=last and not found:
midpoint=(first+last)//2
if A[midpoint]==k:
found=True
else:
if k < A[midpoint]:
last=midpoint-1
else:
first=midpoint+1
return found
確界Θ、上界Ο、下界Ω
問題求解和代碼優化
1.動態規划算法的基本要素為最優子結構性質與重疊子問題性質
2.能采用貪心算法求最優解的問題,一般具有的重要性質為最優子結構性質與貪心選擇性質
3.使用分治法求解不需要滿足的條件是(A )。
A 子問題必須是一樣的 B 子問題不能夠重復
C 子問題的解可以合並 D 原問題和子問題使用相同的方法解
4.矩陣連乘問題的算法可由 動態規划 設計實現。
5..算法是由若干條指令組成的有窮序列,且要滿足輸入、輸出、確定性和有限性四條性質。
6.歸並排序的時間復雜度是c
(a)n (b)n^2 (c)nlgn (d)lgn
7.下列哪個問題可以用貪心算法求解( D )D.哈夫曼編碼問題
遞歸算法和遞歸函數
主分析法求時間復雜度
- 當
f(n) < nlogba
,這種情況意味着,遞歸樹上各層結點的和從根結點開始依次遞增,由於漸進表示可以去掉低次項,因此得T(n)=Θ(nlogba)。 - 當
f(n) = nlogba
,k是大於等於0的常數。這種情況意味着,遞歸樹上各層結點的和從根結點開始並沒有顯著變化,因此得T(n)=Θ( nlogba*logn)
- 當
f(n) > nlogba
,同時對於常數c<1滿足af(n/b)≤cf(n)。這種情況意味着,遞歸樹上各層結點的和從根結點開始依次遞減,因此得T(n)=Θ(f(n)。
給定正整數N,計算所有長度為N但沒有連續1的二分字符。比如,N=2時,輸
出為[00,01,10]:當N=3時,輸出為1000,001,010,100,101。
import math
N = int(input("輸入N:"))
def ss(a):
a = bin(a)[2:]
j = 6
for i in a:
if j == i == '1':
return False
j = i
return True
def printN(a, N):
a = bin(i)[2:]
while len(a) < N:
a = '0' + a
print(a)
for i in range(int(math.pow(2,N))):
if ss(i):
printN(i,N)
統計逆序數問題
#_*_coding:UTF-8_*_
import random
#逆序計算的簡單算法
def count_inversions_simple(A):
inv_count = 0
inv_list = []
for i in range(len(A)):
for j in range(i, len(A)):
if A[i] > A[j]:
inv_count += 1
inv_list.append([A[i],A[j]])
return inv_count, inv_list
#逆序計算的分治算法 邊排序邊找逆序數
#類似歸並排序,加入了逆序數計算 T(n) = 2T(n/2) + O(n) = O(nlogn)
def count_inversions_dc(A):
if len(A) <= 1: #邊界條件
return 0, A
middle = len(A) // 2
leftA = A[:middle]
rightA = A[middle:]
countLA, leftA = count_inversions_dc(leftA)
countRA, rightA = count_inversions_dc(rightA)
countLRA, mergedA = merger_and_count(leftA,rightA)
return countLA + countRA + countLRA, mergedA
#前提:輸入的A,B是有序的
def merger_and_count(A,B):
i, j, inv_count = 0, 0, 0
alist = []
while i < len(A) and j < len(B):
if A[i] < B[j]:
alist.append(A[i])
i += 1
else: # A[i] > B[j] 則B[j]與A右邊所有元素構成逆序
inv_count += len(A) - i
alist.append(B[j])
j += 1
while i < len(A):
alist.append(A[i])
i += 1
while j < len(B):
alist.append(B[j])
j += 1
return inv_count, alist
def main():
list = [random.randint(1,10) for x in range(10)]
sum, alist = count_inversions_dc(list)
print(str(list))
print("逆序數:"+str(sum)+",排序后: "+str(alist))
if __name__ == '__main__':
main()
第k小的數
方法一(課本):
#_*_coding:utf-8_*_
import random
def select_fct(array, k):
if len(array) <= 10:
array.sort()
return array[k]
pivot = get_pivot(array)
array_lt, array_gt, array_eq = patition_array(array, pivot)
if k < len(array_lt):
return select_fct(array_lt, k)
elif k < len(array_lt) + len(array_eq):
return array_eq[0]
else:
normalized_k = k - (len(array_lt) + len(array_eq))
return select_fct(array_gt, normalized_k)
def get_pivot(array):
subset_size = 5
subsets = []
num_medians = len(array) // subset_size
if (len(array) % subset_size) > 0:
num_medians += 1
for i in range(num_medians):
beg = i * subset_size
end = min(len(array), beg+subset_size)
subset = array[beg:end]
subsets.append(subset)
medians = []
for subset in subsets:
median = select_fct(subset, len(subset)//2)
medians.append(median)
pivot = select_fct(medians, len(subset)//2)
return pivot
def patition_array(array, pivot):
array_lt = []
array_gt = []
array_eq = []
for item in array:
if item < pivot:
array_lt.append(item)
elif item > pivot:
array_gt.append(item)
else:
array_eq.append(item)
return array_lt, array_gt, array_eq
def main():
num = 20
array = [random.randint(1,100) for x in range(num)]
random.shuffle(array) #random.shuffle(x[, random]):用於將一個列表中的元素打亂
random.shuffle(array)
k = 7
print(sorted(array))
kval = select_fct(array, k)
print("第八小:"+str(kval))
sorted_array = sorted(array)
assert sorted_array[k] == kval #python assert斷言是聲明其布爾值必須為真的判定,如果發生異常就說明表達示為假。
if __name__ == '__main__':
main()
方法二:
#_*_coding:utf-8_*_
#分治法解決第k小的數
import random
def partition(nums):
pi = nums[0]
low = [x for x in nums[1:] if x < pi]
high = [x for x in nums[1:] if x >= pi]
return low, pi, high
# 查找第 k 小的元素
def solve(nums, k):
low, pi,high = partition(nums) #分解
n = len(low)
if n+1 == k: #k+1表示第k小
return pi
elif n < k:
return solve(high,k-n-1) #減去小於和等於的
else:
return solve(low,k)
if __name__ == '__main__':
list = [random.randint(1,20) for x in range(20)]
print(sorted(list))
print(solve(list,3)) #第三小
print(solve(list,10)) #第十小
硬幣找零,貪心算法
#零錢找零,pay是應付金額
def coin(pay):
m = [100, 25, 10, 5, 1]
list = []
sort_m = sorted(m, reverse=True)
for i in sort_m:
coin_count = int(pay/i)
list += [i,] * coin_count
pay -= coin_count*i
if pay <= 0:
break
return list
def main():
#硬幣找零
pay = 263
print(coin(pay))
if __name__ == "__main__":
main()
digkstra算法求單源最短路徑
#_*_coding:utf-8_*_
#單源最短路徑問題
MAX_value = 999999
def dijkstra(graph, s): #s是源點,d(s) = 0
if graph is None: # 判斷圖是否為空,如果為空直接退出
return None
dist = [MAX_value,]*len(graph)
dist[s] = 0
S = []
Q = [i for i in range(len(graph))]
dist_init = [i for i in graph[s]]
while Q:
u_dist = min([d for v, d in enumerate(dist_init) if v in Q])
u = dist_init.index(u_dist)
S.append(u)
Q.remove(u)
for v, d in enumerate(graph[u]):
if 0 < d < MAX_value:
if dist[v] > dist[u]+d:
dist[v] = dist[u]+d
dist_init[v] = dist[v]
print(dist[v])
return dist #到每一個點的最短路徑距離
if __name__ == '__main__':
graph_list = [ [0, 9, MAX_value, MAX_value, MAX_value, 14, 15, MAX_value],
[9, 0, 24, MAX_value, MAX_value, MAX_value,MAX_value,MAX_value],
[MAX_value, 24, 0, 6, 2, 18,MAX_value,19],
[MAX_value, MAX_value, 6, 0, 11,MAX_value,MAX_value, 6],
[MAX_value,MAX_value, 2, 11, 0, 30,20, 16],
[14,MAX_value,18,MAX_value,30,0,5,MAX_value],
[15,MAX_value,MAX_value,MAX_value,20,5,0,44],
[MAX_value,MAX_value,19,6,16,MAX_value,44,0]]
distance = dijkstra(graph_list, 0)
print(distance)
給定n件物品的序列,以及容量為c的箱子。求將物品裝入到箱子,每一個箱子裝人的物品總重量不能超過箱子的容量。給出一個算法,要求用最小的箱子數將物品全部裝入。
比如有6個物品,其重量分別為[4,8,1,42,1],箱子容量c=10。那么最少需要2個箱子將物品全部裝入,其中一個箱子裝入[4,4,2],另一個箱子裝入[8,2]
#_*_coding:utf-8_*_
def box(list, n):
list.sort(reverse = True)
aa = [[] for x in range(len(list))]
for element in list:
for j in range(len(list)):
if sum(aa[j]) + element <= n:
aa[j].append(element)
break
aa = [x for x in aa if len(x)!=0]
return aa
list = [4,8,1,4,2,1]
print(box(list, 10))
動態規划問題
#_*_coding:utf-8_*_
#動態規划
import numpy as np
import random
#斐波那契函數
memo = {} #字典
def fib2(n):
if n in memo:
return memo[n]
else:
if n <= 1:
f = 1
else:
f = fib2(n-1) + fib2(n-2)
memo[n] = f
return f
def fib_bottom_up(n):
fib = {} #存儲結果的字典
for k in range(n+1):
if k <= 1:
f = 1
else:
f = fib[k-1] + fib[k-2] #填表
fib[k] = f
return fib[n]
#撿硬幣
#自底向上實現遞歸策略
def bottom_up_coins(row_coins):
table = [None] * (len(row_coins) + 1) #申明表格
table[0] = 0
table[1] = row_coins[0]
for i in range(2, len(row_coins)+1):
table[i] = max(table[i-2] + row_coins[i-1], table[i-1]) #填表
return table
#回溯
def trace_back_coins(row_coins, table):
select = []
i = len(row_coins) #從最后一位索引
while i >= 1:
if table[i] > table[i-1]:
select.append(row_coins[i-1])
i -= 2
else:
i -= 1
return select
#子序列和的最大值
def num_max(alist): #自底向上遞歸
table = [None] * (len(alist)+1)
table[0] = 0
for i in range(1, len(alist)+1):
table[i] = max(table[i-1] + alist[i-1], alist[i-1]) #計算重新開始的優劣
return table
def tract_back_subseq(alist, table):
select = []
ind_max = np.argmax(table) #得到最大值索引
while ind_max >= 1:
if table[ind_max] == alist[ind_max-1] + table[ind_max-1]:
select.append(alist[ind_max-1])
ind_max -= 1
else:
select.append(alist[ind_max-1])
break
return select
if __name__ == "__main__":
list = [random.randint(1,20) for x in range(10)]
print(list)
print(fib2(10))
print(fib_bottom_up(10))
table = bottom_up_coins(list)
print(trace_back_coins(list, table))
table = num_max(list)
print(tract_back_subseq(list, table))
0,1 背包問題
#_*_coding:utf-8_*_
#0 1 背包問題
#分析: k(i, x) = max(k(i-1, x), k(i-1, x-s) + v) 物品i放入背包,不放入背包
def knapSack(W, wt, val, n):
#W是容量, wt是物品容量,val是價值, n是物品數量
k = [[0 for x in range(W+1)] for x in range(n+1)]
for i in range(n+1):
for w in range(W+1):
if i == 0 or w == 0: #不滿足條件,物品或者容量為空
k[i][w] = 0
elif wt[i-1] <= w:
k[i][w] = max(val[i-1] + k[i-1][w-wt[i-1]], k[i-1][w])
else:
k[i][w] = k[i-1][w]
return k
if __name__ == "__main__":
k = knapSack(10, [1,2,3], [2,3,4], 3)
print(k)