算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。
算法中的指令描述的是一个计算,当其运行时能从一个初始状态和(可能为空的)初始输入开始,经过一系列有限而清晰定义的状态,最终产生输出并停止于一个终态。一个状态到另一个状态的转移不一定是确定的。随机化算法在内的一些算法,包含了一些随机输入。
动态规划算法
1、特点:
一种思想:最优子结构--递推式 + 重叠子问题
每个子问题只求解一次,保存求解结果
之后需要此问题时,只需查找保存的结果
2、应用:
(1)斐波那契数列
def fibnacci(n):
"""斐波那契数列
速度比非递归慢的原因:子问题的重复计算
"""
if n == 1 or n == 2:
return 1
else:
return fibnacci(n-1) + fibnacci(n-2)
print(fibnacci(5))
def fibnacci_no_rec(n):
"""动态规划思想:非递归的斐波那契数列"""
f = [0, 1, 1]
if n > 2:
for i in range(n-2):
num = f[-1] + f[-2]
f.append(num)
return f[n]
print(fibnacci_no_rec(5))
(2)钢条切割问题:
def cut_rod_recurision(p, n):
"""钢条切割问题:自顶向下递归实现,时间复杂度O(2^n)
有子问题的重复计算
p:钢条长度对应的价格
n:钢条的长度
"""
if n == 0:
return 0
else:
res = p[n]
for i in range(1, n):
res = max(res, cut_rod_recurision(p, i) + cut_rod_recurision(p, n-i))
return res
def cut_rod_recurision_2(p, n):
"""钢条切割问题:自顶向下递归实现
还是有子问题的重复计算
p:钢条长度对应的价格
n:钢条的长度
"""
if n == 0:
return 0
else:
res = p[n]
for i in range(1, n+1):
res = max(res, p[i] + cut_rod_recurision_2(p, n-i))
return res
p = [0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30]
print(cut_rod_recurision(p, 9)) # 25
print(cut_rod_recurision_2(p, 9)) # 25
def cut_rod_dp(p, n):
"""动态规划:钢条切割问题,自底向上
无子问题的重复计算,时间复杂度O(n^2)
p:钢条长度对应的价格
n:钢条的长度
"""
# 对于每个子问题,保存求解结果
r = [0]
# 先求r1,r2,r3...
for i in range(1, n+1):
res = 0
# 从i种方案里,比较原来的res和现在方案,取最大
for j in range(1, i+1):
res = max(res, p[j] + r[i - j])
r.append(res)
return r[n]
p = [0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30]
print(cut_rod_dp(p, 9))
def cut_rod_extend(p, n):
"""动态规划:钢条切割问题:自底向上,重构解
从钢条的左边割下长度为i的一段,只对右边剩下的一段继续进行切割
无子问题的重复计算
p:钢条长度对应的价格
n:钢条的长度
"""
# 对于每个子问题,保存求解结果
r = [0]
# 对于每个子问题,保存坐标不切割的那一段的长度
s = [0]
# 自底向上,先求r1,r2,r3...
for i in range(1, n+1):
res_r = 0 # 记录价格的最大值
res_s = 0 # 记录价格最大值对应方案的左边不切割长度
# 从i种方案里,比较原来的res和现在方案,取最大
for j in range(1, i+1):
if p[j]+r[i - j] > res_r:
res_r = p[j] + r[i - j]
res_s = j
r.append(res_r)
s.append(res_s)
return r[n], s
def cut_rod_solution(p, n):
"""输出最优方案"""
r, s = cut_rod_extend(p, n)
ans = [] # 存放最优方案
while n > 0:
ans.append(s[n])
n -= s[n]
return ans
p = [0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30]
print(cut_rod_solution(p, 9))
(3)最长公共子序列
(4)欧几里得算法:最大公约数
def gcd(a, b):
"""欧几里得算法,最大公约数"""
if b == 0:
return a
else:
return gcd(b, a % b)
def gcd_no_rec(a, b):
while b > 0:
r = a % b
a = b
b = r
return a
print(gcd(12,16)) # 4
利用欧几里得算法实现的分数类,支持分数的四则运算:
class Fraction:
"""利用欧几里得算法实现的分数类,支持分数的四则运算
a:分子
b:分母
"""
def __init__(self, a, b):
self.a = a
self.b = b
x = self.gcd(a, b)
self.a /= x
self.b /= x
def gcd(self, a, b):
"""最大公约数"""
while b > 0:
r = a % b
a = b
b = r
return a
def zgs(self, a, b):
"""最小公倍数"""
# 12 16 --->4 最小公倍数为3*4*4 = 48
x = self.gcd(a, b)
return a * b / x
def __add__(self, other):
"""分数的加法运算"""
# 1/12 + 1/20
a = self.a
b = self.b
c = other.a
d = other.b
fenmu = self.zgs(b, d)
fenzi = a * fenmu / b + c * fenmu / d
return Fraction(fenzi, fenmu)
def __str__(self):
return "%d/%d" % (self.a, self.b)
a = Fraction(1,3)
b = Fraction(1,2)
print(a + b) # 5/6