为什么我的算法超时?

时间:2018-08-02 02:52:12

标签: python dynamic-programming memoization

问题出在这里(来自Leetcode):

Given an unsorted array of integers, find the length of longest increasing subsequence.

Example:

Input: [10,9,2,5,3,7,101,18]
Output: 4 
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4. 
Note:

There may be more than one LIS combination, it is only necessary for you to return the length.
Your algorithm should run in O(n2) complexity.

这是我的解决方法:

memo = {} 
def lis_calc(lower_bound, offset):
    if memo.get((lower_bound, offset), None):
        return memo[(lower_bound, offset)]
    if offset >= len(nums):
        return 0
    if nums[offset] > lower_bound:
        res = max(1 + lis_calc(nums[offset], offset + 1), lis_calc(lower_bound, offset + 1))
    else:
        res = lis_calc(lower_bound, offset + 1)

    memo[(lower_bound, offset)] = res 
    return memo[(lower_bound, offset)]

在最坏的情况下(列表已经按升序排序),我们将有NxN个唯一的函数调用(成对的每个arg N个值)。但是,我的算法正在为非常大的输入而超时,这表明我的算法没有最坏情况下的O(NxN)时间成本。我在这里做错了什么吗?似乎是DP +备忘的直接实现。超时的测试输入为list(range(1,2501))

我通过lis_calc(float('-inf'), 0)

调用该函数

1 个答案:

答案 0 :(得分:1)

您的算法可能不是平方的,而是指数的。

看下面的代码:

if nums[offset] > lower_bound:
    res = max(1 + lis_calc(nums[offset], offset + 1), lis_calc(lower_bound, offset + 1))

在每个步骤中,在最坏的情况下,您都会打两个电话。在最坏的情况下,这两个电话中的每个电话都会打两个电话。这四个电话中的每个电话都会打两个电话,依此类推。

如果满足以下两个条件之一,则您的算法仍可以是多项式:

  • 如果这些新呼叫中至少有一半是备忘录存储的,或者
  • 如果可以保证最坏的情况将以最差的log N步骤减少到下界情况(变为线性)。

但是据我所知,这两个都不是真的。因此,在最坏的情况下,您的算法需要执行O(2**N)个步骤。这就是为什么它太慢的原因。


或者……也许不对,也许只是用一个额外的常数乘以二次时间,而2500恰好在他们期望您的代码能够舒适地工作的边缘附近,而您只是没有完全通过? >

每次将通话加倍时,您将不会缓存其中的一半,但应缓存其中N-1的一半。因此,如果一切正常,则您的总步骤应为N * (N+1) + 1,但如果您稍稍出错,则可能将其减少4倍……尽管确实如此,但我认为这不会一个很好的测试,即使以他们测试的最大数字为常数4足以使结果有所不同。