【问题标题】:Is this Longest Common Subsequence Correct?这个最长公共子序列正确吗?
【发布时间】:2013-04-13 19:10:38
【问题描述】:

我刚刚编写了这个实现来使用动态编程找出longest increasing subsequence 的长度。因此,对于输入为 [10, 22, 9, 33, 21, 50, 41, 60, 80],LIS 为 6,其中一组为 [10, 22, 33, 50, 60, 80]。

当我运行以下代码时,我得到的正确答案为 6,复杂度为 O(n)。这是正确的吗?

def lis(a):
    dp_lis     = []
    curr_index = 0
    prev_index = 0

    for i in range(len(a)):
        prev_index = curr_index
        curr_index = i

        print 'if: %d < %d and %d < %d' % (prev_index, curr_index, a[prev_index], a[curr_index])
        if prev_index < curr_index and a[prev_index] < a[curr_index]:
            print '\tadd ELEMENT: ', a[curr_index]
            new_lis = 1 + max(dp_lis)
            dp_lis.append(new_lis)
        else:
            print '\telse ELEMENT: ', a[curr_index]
            dp_lis.append(1)

    print "DP LIST: ", dp_lis
    return max(dp_lis)

if __name__ == '__main__':
    a = [10, 22, 9, 33, 21, 50, 41, 60, 80]
    print lis(a)

【问题讨论】:

  • [10, 100, 20, 30, 40, 50, 60, 70, 80] 会发生什么?
  • 它正确地说 LIS 的长度为 8,这是正确的 [10,20,30,40,50,60,70,80]
  • max(dp_lis) 是否适用于 O(1) 复杂度?
  • 在 python 中 max 需要 O(n) 时间。我到处读到最长增加子序列需要O(n^2),优化版本需要O(nlogn),但我上面的实现是在O(n) 时间完成的。我错过了什么?
  • 我试过[10, 100, 200, 30, 40, 50, 60, 70, 80],它错误地说LIS的长度是8,应该是7 [10, 30, 40, 50, 60, 70, 80]

标签: python algorithm dynamic-programming


【解决方案1】:

使用这种正确、经过验证但效率低下的算法实现来检查您的结果 - 这是标准的递归解决方案,它不使用动态编程:

def lis(nums):
    def max_length(i):
        if i == -1:
            return 0
        maxLen, curLen = 0, 0
        for j in xrange(i-1, -1, -1):
            if nums[j] < nums[i]:
                curLen = max_length(j)
                if curLen > maxLen:
                    maxLen = curLen
        return 1 + maxLen
    if not nums:
        return 0
    return max(max_length(x) for x in xrange(len(nums)))

检查your_lis(nums) == my_lis(nums) 是否有尽可能多的带有数字的不同大小的输入列表,它们应该相等。在某些时候,对于长列表,我的实现会比你的慢得多。

作为进一步的比较点,这是我自己优化的动态编程解决方案。它在O(n log k) 时间和O(n) 空间中运行,返回沿途发现的实际最长递增子序列:

def an_lis(nums):
    table, lis = lis_table(nums), []
    for i in xrange(len(table)):
        lis.append(nums[table[i]])
    return lis

def lis_table(nums):
    if not nums:
        return []
    table, preds = [0], [0] * len(nums)
    for i in xrange(1, len(nums)):
        if nums[table[-1]] < nums[i]:
            preds[i] = table[-1]
            table.append(i)
            continue
        minIdx, maxIdx = 0, len(table)-1
        while minIdx < maxIdx:
            mid = (minIdx + maxIdx) / 2
            if nums[table[mid]] < nums[i]:
                minIdx = mid + 1
            else:
                maxIdx = mid
        if nums[i] < nums[table[minIdx]]:
            if minIdx > 0:
                preds[i] = table[minIdx-1]
            table[minIdx] = i
    current, i = table[-1], len(table)
    while i:
        i -= 1
        table[i], current = current, preds[current]
    return table

【讨论】:

    【解决方案2】:

    我经常实现动态编程算法。

    我发现检查正确性的最佳方法是编写算法的蛮力版本,并将输出与小示例的动态编程实现进行比较。

    如果两个版本的输出一致,那么你有合理的正确性信心。

    【讨论】:

      猜你喜欢
      • 2011-03-01
      • 1970-01-01
      • 2011-08-10
      • 1970-01-01
      • 2011-02-25
      • 2013-02-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多