如何将 O(2^(logN)) 简化为 O(N)

How to simplify O(2^(logN)) to O(N)

在 Cracking the Coding Interview 中有一个示例,其中计算二叉搜索树中节点的递归算法的运行时间为 O(2^(logN))。这本书解释了我们如何简化得到 O(N) 这样...

2^p = Q 
logQ = P 
Let P = 2^(logN).

但是当他们说 Let P = 2^(logN) 时我迷路了。我不明白我们怎么知道如何将这两个设置为彼此相等,我也不明白下一步......(尽管他们告诉我他们通过对数基数 2 的定义来做到这一点)

logP = logN 
P = N 
2^(logN) = N

因此代码的运行时间为 O(N)

假设logN为log2N

这一行:

Let P = 2^(logN).

只是假设 P 等于 2^(logN)。您还不知道 N,您只是定义 PN 之间的关系。

稍后,您可以对等式两边应用log函数。而由于 log(2^(logN))logN,下一步是:

logP = logN

而且,显然,当 logP = logN 时,则:

P = N

并且之前您假设 P = 2^(logN),然后:

2^(logN) = N

此外,所有这些都可以通过 log 函数的定义简化为 2^logN = N

对数的定义是“需要将底数提高多少次方才能得到这个值”所以如果对数的底数是 2,那么将 2 提高到那个次方就可以得到原始值。

示例:N 是 256。如果我们取它的以 2 为底的对数,我们得到 8。如果我们将 2 提高到 8 的次方,我们得到 256。所以它是线性的,我们可以使它只是 N .

如果对数的基数不同,例如10,则转换只需要将指数除以一个常数,使更准确的形式成为N = 2^(log N / log 2),可以改为N / 2^(1 / log 2) = 2^log N。这里左边 N 的除数是一个常数,所以我们在讨论复杂性时可以忘记它,然后再次来到 N = 2^log N

你也可以亲手测试一下。 256 的 Log2 为 8。128 的 Log2 为 7。8/7 约为 1.14。 256 的 Log10 是 2.4。 128 的 Log10 是 2.1。 2.4/2.1 约为 1.14。所以基数并不重要,你得到的值是不一样的,但它是线性的。所以在数学上 N 不等于 2^Log10 N,但在复杂性方面它确实如此。

简短的回答是,原始问题可能隐含地假设对数应该以 2 为底,因此根据 [= 的定义,2^(log_2(N)) 就是 N 57=](x) 作为 2^y 的反函数。

但是,如果对数是基于不同的底数,则更仔细地检查一下会很有趣。标准结果允许我们以 b 为底写对数,如下所示:

其中 ln(x) 是自然对数(使用基数 e)。类似地,可以重写 2^x 如下:

然后我们可以重写原来的order-expression如下:

可以简化为:

所以,如果我们的对数的底 b 是 2,那么这显然就是 N。但是,如果基数不同,那么我们会得到 N 的幂。例如,如果 b=10 我们得到 N 的 0.301 次方,这绝对是一个比 O(N).

更慢的递增函数

我们可以使用以下 Python 脚本直接检查:

import numpy
import matplotlib.pyplot as plt

N = numpy.arange(1, 100)

plt.figure()
plt.plot(N, 2**(numpy.log2(N)))
plt.xlabel('N')
plt.ylabel(r'^{\log_2 N}$')

plt.figure()
plt.plot(N, 2**(numpy.log10(N)))
plt.xlabel('N')
plt.ylabel(r'^{\log_{10} N}$')

plt.show()

当我们假设对数以二为底时生成的图形:

与取对数以十为底时的图形非常不同: