【问题标题】:How do I memoize this LIS python2.7 algorithm properly?如何正确记忆这个 LIS python2.7 算法?
【发布时间】:2017-02-20 22:54:54
【问题描述】:

我正在练习动态编程,正在编写最长递增子序列问题。

我有 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]) 对于记忆版本失败。

>>> 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]

我做错了什么?

【问题讨论】:

  • 我只是粗略地看了一下你的代码,但你之前有没有在解释器会话中调用过记忆化的版本?可变的默认参数保留状态。这可能会搞砸你。
  • 不!这两个结果都来自新的会议@juanpa.arrivillaga

标签: python algorithm dynamic-programming memoization


【解决方案1】:

您的状态取决于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 问题。

【讨论】:

  • 哦,我更喜欢列表的索引,谢谢!
  • 其实有一个案例破坏了我刚刚发现的这个记忆版本,请看编辑!
  • @ylun.ca 编辑了答案。
【解决方案2】:

所以我刚刚发现 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))]

这适用于我迄今为止测试过的所有情况。

【讨论】:

  • 我已经更新了答案。我应该警告你,使用tuple(lst)tuple(lis) 作为动态状态会大大增加程序的复杂性。
  • 太棒了,是的,我意识到这一点,内存使用量在大输入时变得相当高。稍后我可能会尝试使用索引来实现这一点
猜你喜欢
  • 2018-01-11
  • 2019-11-30
  • 1970-01-01
  • 2013-06-23
  • 2011-03-14
  • 1970-01-01
  • 1970-01-01
  • 2016-09-18
相关资源
最近更新 更多