如何正确记忆这个 LIS python2.7 算法?
How do I memoize this LIS python2.7 algorithm properly?
我正在练习动态规划,我正在写最长递增子序列问题。
我有DP解决方案:
def longest_subsequence(lst, lis=[], mem={}):
if not lst:
return lis
if tuple(lst) not in mem.keys():
if not lis or lst[0] > lis[-1]:
mem[tuple(lst)] = max([longest_subsequence(lst[1:], lis+[lst[0]], mem), longest_subsequence(lst[1:], lis, mem)], key=len)
else:
mem[tuple(lst)] = longest_subsequence(lst[1:], lis, mem)
return mem[tuple(lst)]
还有一个非记忆版本
def longest_subsequence(lst, lis=[]):
if not lst:
return lis
if not lis or lst[0] > lis[-1]:
result = max([longest_subsequence(lst[1:], lis+[lst[0]]), longest_subsequence(lst[1:], lis)], key=len)
else:
result = longest_subsequence(lst[1:], lis)
return result
但是,这两个函数具有不同的行为。例如,测试用例 longest_subsequence([10,9,2,5,3,7,101,18])
对于 memoized 版本失败。
>>> longest_subsequence([10,9,2,5,3,7,101,18])
[10, 101]
然而,非记忆版本是完全正确的(虽然慢得多)。
>>> longest_subsequence([10,9,2,5,3,7,101,18])
[2, 5, 7, 101]
我做错了什么?
您的状态取决于 lst
和您选择的上一个项目。但是你只考虑了lst
。这就是您得到不正确结果的原因。要修复它,您只需将上一个项目添加到您的动态状态。
def longest_subsequence(lst, prev=None, mem={}):
if not lst:
return []
if (tuple(lst),prev) not in mem:
if not prev or lst[0] > prev:
mem[(tuple(lst),prev)] = max([[lst[0]]+longest_subsequence(lst[1:], lst[0]), longest_subsequence(lst[1:], prev)], key=len)
else:
mem[(tuple(lst),prev)] = longest_subsequence(lst[1:], prev)
return mem[(tuple(lst),prev)]
print longest_subsequence([3,5,6,2,5,4,19,5,6,7,12])
请注意,使用 tuple(list)
作为动态状态并不是一个好主意。您可以简单地使用您正在检查的 list
中的项目索引而不是整个列表:
def longest_subsequence(lst, index=0, prev=None, mem={}):
if index>=len(lst):
return []
if (index,prev) not in mem:
if not prev or lst[index] > prev:
mem[(index,prev)] = max([[lst[index]]+longest_subsequence(lst, index+1, lst[index]), longest_subsequence(lst, index+1, prev)], key=len)
else:
mem[(index,prev)] = longest_subsequence(lst,index+1, prev)
return mem[(index,prev)]
print longest_subsequence([3,5,6,2,5,4,19,5,6,7,12])
如需更有效的方法,您可以查看 this 问题。
所以我刚刚发现 Tempux 的答案并不适用于所有情况。
我回过头来将整个状态封装到记忆字典中,因此将 tuple(lis)
添加为密钥的一部分。此外,lst
索引技巧可能不那么容易实现,因为我通过递归改变 lst
,因此我使用 tuple()
作为我的键。
我这样做的原因是多个 lis
可能具有相同的 [-1]
值。因此,对于这个新状态,代码是:
def longest_subsequence(lst, lis=[],mem={}):
if not lst:
return lis
if (tuple(lst),tuple(lis)) not in mem:
if not lis or lst[0] > lis[-1]:
mem[(tuple(lst),tuple(lis))] = max([longest_subsequence(lst[1:], lis+[lst[0]]), longest_subsequence(lst[1:], lis)], key=len)
else:
mem[(tuple(lst),tuple(lis))] = longest_subsequence(lst[1:], lis)
return mem[(tuple(lst),tuple(lis))]
这适用于我目前测试过的所有情况。
我正在练习动态规划,我正在写最长递增子序列问题。
我有DP解决方案:
def longest_subsequence(lst, lis=[], mem={}):
if not lst:
return lis
if tuple(lst) not in mem.keys():
if not lis or lst[0] > lis[-1]:
mem[tuple(lst)] = max([longest_subsequence(lst[1:], lis+[lst[0]], mem), longest_subsequence(lst[1:], lis, mem)], key=len)
else:
mem[tuple(lst)] = longest_subsequence(lst[1:], lis, mem)
return mem[tuple(lst)]
还有一个非记忆版本
def longest_subsequence(lst, lis=[]):
if not lst:
return lis
if not lis or lst[0] > lis[-1]:
result = max([longest_subsequence(lst[1:], lis+[lst[0]]), longest_subsequence(lst[1:], lis)], key=len)
else:
result = longest_subsequence(lst[1:], lis)
return result
但是,这两个函数具有不同的行为。例如,测试用例 longest_subsequence([10,9,2,5,3,7,101,18])
对于 memoized 版本失败。
>>> longest_subsequence([10,9,2,5,3,7,101,18])
[10, 101]
然而,非记忆版本是完全正确的(虽然慢得多)。
>>> longest_subsequence([10,9,2,5,3,7,101,18])
[2, 5, 7, 101]
我做错了什么?
您的状态取决于 lst
和您选择的上一个项目。但是你只考虑了lst
。这就是您得到不正确结果的原因。要修复它,您只需将上一个项目添加到您的动态状态。
def longest_subsequence(lst, prev=None, mem={}):
if not lst:
return []
if (tuple(lst),prev) not in mem:
if not prev or lst[0] > prev:
mem[(tuple(lst),prev)] = max([[lst[0]]+longest_subsequence(lst[1:], lst[0]), longest_subsequence(lst[1:], prev)], key=len)
else:
mem[(tuple(lst),prev)] = longest_subsequence(lst[1:], prev)
return mem[(tuple(lst),prev)]
print longest_subsequence([3,5,6,2,5,4,19,5,6,7,12])
请注意,使用 tuple(list)
作为动态状态并不是一个好主意。您可以简单地使用您正在检查的 list
中的项目索引而不是整个列表:
def longest_subsequence(lst, index=0, prev=None, mem={}):
if index>=len(lst):
return []
if (index,prev) not in mem:
if not prev or lst[index] > prev:
mem[(index,prev)] = max([[lst[index]]+longest_subsequence(lst, index+1, lst[index]), longest_subsequence(lst, index+1, prev)], key=len)
else:
mem[(index,prev)] = longest_subsequence(lst,index+1, prev)
return mem[(index,prev)]
print longest_subsequence([3,5,6,2,5,4,19,5,6,7,12])
如需更有效的方法,您可以查看 this 问题。
所以我刚刚发现 Tempux 的答案并不适用于所有情况。
我回过头来将整个状态封装到记忆字典中,因此将 tuple(lis)
添加为密钥的一部分。此外,lst
索引技巧可能不那么容易实现,因为我通过递归改变 lst
,因此我使用 tuple()
作为我的键。
我这样做的原因是多个 lis
可能具有相同的 [-1]
值。因此,对于这个新状态,代码是:
def longest_subsequence(lst, lis=[],mem={}):
if not lst:
return lis
if (tuple(lst),tuple(lis)) not in mem:
if not lis or lst[0] > lis[-1]:
mem[(tuple(lst),tuple(lis))] = max([longest_subsequence(lst[1:], lis+[lst[0]]), longest_subsequence(lst[1:], lis)], key=len)
else:
mem[(tuple(lst),tuple(lis))] = longest_subsequence(lst[1:], lis)
return mem[(tuple(lst),tuple(lis))]
这适用于我目前测试过的所有情况。