三、猴子吃桃
猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾就多吃了一个。第二天早上又将剩下的桃子吃了一半,还是不过瘾又吃了一个。以后每天都吃前一天剩下的一半再加一个。到第10天刚好剩一个。问猴子第一天摘了多少个桃子?
递归版本
# encoding: utf-8
def peach(day, number):
if day == 10:
return number
return peach(day + 1, 2 * (number + 1))
print(peach(1, 1))
非递归版本
# encoding: utf-8
x2 = 1
for day in range(9, 0, -1):
x1 = (x2 + 1) * 2
x2 = x1
print(x1)
四、最长公共子序列
先要理清一个概念,序列是不用连续的,连续的是子串,是KMP算法的范畴
最长公共子序列(Longest Common Subsequence)是典型的动态规划问题,如果不是学习原理,我们可以使用mlpy库中的mlpy.lcs_std(x, y)
函数来方便的实现,不用自己重复造轮子
动态规划的基本思想和分治法类似,是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。二者又有区别,动态规划求解的问题,经分解得到的子问题往往不是互相独立的。若用分治法来解这类问题,则分解得到的部分子问题可能被重复计算了多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。
动态规划和分治法最显著的区别就是子问题“高度重叠”,动态规划可以显著的降低重复计算带来的影响,如果是一般的递归,我们也可以容易看出有高度重叠的重复计算部分会令递归的效率变得非常低,我们先看一个递归版本的
# encoding: utf-8
def lcs(xstr, ystr):
if not xstr or not ystr:
return ""
x, xs, y, ys = xstr[0], xstr[1:], ystr[0], ystr[1:]
if x == y:
return x + lcs(xs, ys)
else:
return max(lcs(xstr, ys), lcs(xs, ystr), key=len)
print(lcs('thisisatest', 'testing123testing'))
结果是tsitest
我们可以明显的感觉到要等待运算的结果了,但是递归非常容易理解,这里利用了Python的切片和序列解包,程序非常的简介。
接下来我们要看动态规划的解法:
# encoding: utf-8
def lcs(a, b):
lengths = [[0 for j in range(len(b)+1)] for i in range(len(a)+1)]
# 初始化整个矩阵,两个串构成边的矩阵
for i, x in enumerate(a):
# enumerate可以同时获得索引和值,第一个返回索引,第二个返回值
for j, y in enumerate(b):
if x == y:
lengths[i+1][j+1] = lengths[i][j] + 1
else:
lengths[i+1][j+1] = max(lengths[i+1][j], lengths[i][j+1])
# 已经得到整个矩阵,从矩阵的右下角开始向左搜索,如果左侧的数字比现在的数字要小,就向上一层,否则就继续向左搜索
result = ""
x, y = len(a), len(b)
while x != 0 and y != 0:
if lengths[x][y] == lengths[x-1][y]:
x -= 1
elif lengths[x][y] == lengths[x][y-1]:
y -= 1
else:
assert a[x-1] == b[y-1]
result = a[x-1] + result
x -= 1
y -= 1
return result[::-1]
print(lcs('thisisatest', 'testing123testing'))
我们可以明显的感觉到得出结果几乎就是在瞬间完成,没有一个停顿的过程
五、编辑距离
编辑距离,又称Levenshtein距离,是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。通常许可的编辑操作包括:
1、将一个字符替换成另一个字符
2、插入一个字符
3、删除一个字符
# encoding: utf-8
def minimumEditDistance(s1, s2):
if len(s1) > len(s2):
s1, s2 = s2, s1
distances = range(len(s1) + 1)
for index2, char2 in enumerate(s2):
newDistances = [index2+1]
for index1, char1 in enumerate(s1):
if char1 == char2:
newDistances.append(distances[index1])
else:
newDistances.append(1 + min((distances[index1],
distances[index1+1],
newDistances[-1])))
distances = newDistances
return distances[-1]
print(minimumEditDistance("kitten", "sitting"))
print(minimumEditDistance("rosettacode", "raisethysword"))
类似的,我们也采用动态规划来进行计算,结果为3和8