从指数时间到线性时间的改进
到目前为止给出的所有答案都在 指数时间 O(2<sup>n</sup>)。但是,可以通过更改一行代码使其在 O(k) 中运行。
解释:
指数运行时间的原因是每次递归都用这行代码将问题分成重叠的子问题(see Ideone here):
def choose(n, k):
...
return choose(n-1, k-1) + choose(n-1, k)
要了解为什么这会如此糟糕,请考虑choose(500, 2) 的示例。 500 choose 2的数值为500*499/2;但是,使用上面的递归需要250499 递归调用来计算它。显然,这太过分了,因为只需要 3 次操作。
要将其改进为线性时间,您需要做的就是选择一个不同的递归,它不会分成两个子问题(wikipedia 上有很多子问题)。
例如下面的递归是等价的,但只使用3递归调用来计算choose(500,2)(see Ideone here):
def choose(n,k):
...
return ((n + 1 - k)/k)*choose(n, k-1)
改进的原因是每个递归只有一个子问题,每次调用都会将k 减少1。这意味着我们保证此递归只会采用k + 1 递归或O(k)。这对于更改一行代码来说是一个巨大的改进!
如果您想更进一步,您可以利用“n 选择 k”中的对称性来确保 k <= n/2 (see Ideone here):
def choose(n,k):
...
k = k if k <= n/2 else n - k # if k > n/2 it's equivalent to k - n
return ((n + 1 - k)/k)*choose(n, k-1)