如何知道大 O 何时是对数?

Posted

技术标签:

【中文标题】如何知道大 O 何时是对数?【英文标题】:How to know when Big O is Logarithmic? 【发布时间】:2010-10-19 11:41:25 【问题描述】:

我的问题来自"Plain English Explanation of Big O" 的帖子。我不知道对数复杂度的确切含义。我知道我可以在时间和操作次数之间进行回归并计算 X 平方值,从而确定复杂度。但是,我想知道一种在纸上快速确定它的方法。

如何确定对数复杂度?有什么好的基准吗?

【问题讨论】:

【参考方案1】:

这是另一种说法。

假设您的算法在问题大小的位数上是线性的。所以,也许你有一个新的算法来分解一个大数,你可以证明它在位数上是线性的。因此,使用您的算法计算 20 位数字所需的时间是 10 位数字的两倍。这将具有日志复杂性。 (这对发明者来说是值得的。)

二分法具有相同的行为。将区间长度缩短 1024 = 2^10 大约需要 10 个二等分步骤,但只需 20 步即可将区间长度缩短 2^20 倍。

日志复杂性并不总是意味着算法在所有问题上都很快。 O(log(n)) 前面的线性因子可能很大。所以你的算法在小问题上可能很糟糕,直到问题规模明显大到其他算法以指数(或多项式)死亡时才会变得有用。

【讨论】:

用大问题的规模很好地解释了。【参考方案2】:

不确定这是否是您的意思,但是...当您使用像平衡二叉树这样的分散数据结构时,通常会出现对数复杂性,该结构包含 1 个根节点、2 个子节点、4 个子节点孙子、8 个曾孙等。基本上在每个级别上,节点的数量都会乘以某个因子 (2),但迭代中仍然只涉及其中一个。或者作为另一个例子,一个循环,其中索引在每一步都翻倍:

for (int i = 1; i < N; i *= 2)  ... 

类似的东西是对数复杂度的特征。

【讨论】:

+1 非常有趣。我正在寻找更多类似你的例子的东西。算法对数是否为:for (int i = BIG_number; i > N; i *= 1/2) ... 1/2 在整数除法中为零,但如果您使用 "i /= 2" 代替,是的。 (如果这是您想知道的特定算法,最好将其包含在您的问题中。)【参考方案3】:

如果您只是想了解对数 Big Oh,请留意您的数据在每个重复步骤中何时减半。

这是因为如果您正在处理的数据是前一步的 1/2,那么它就是一个无限序列。

【讨论】:

通常在谈到大 O 时,log 表示对数基数 2。 @samoz,对数与底数无关。 log_a(x) = log_b(x)/log_b(a) 从基数 a 转换为基数 b。 @George 同意,但通常是基数 2。【参考方案4】:

不严格,但如果你有一个算法,基本上将每次迭代需要完成的工作分成一半,那么你就有对数复杂度。经典的例子是二分查找。

【讨论】:

不一定。我理解你试图暗示的意思,但仅仅因为你将工作分成两半并不意味着你得到对数复杂度,你甚至可以有指数时间来处理这件事。你必须注意解决方案是如何重新组合的,以及分裂的问题是如何得到解决的。在很多情况下,重组步骤占主导地位。请参阅 Master Theorem 或更好地解决没有定理的递归。一个简单的复现之下,有很多惊喜。 @unjaan:我认为你误会了我。我不只是说将工作分成两半,而是说“每次迭代都需要将工作分成一半”。我的意思是,如果与上一步相比,每一步都有一半的工作要做,那么你就有对数复杂度(对于工作,读取计算)。【参考方案5】:

Master theorem 通常有效。

【讨论】:

有点难想,但一旦掌握就很好了。

以上是关于如何知道大 O 何时是对数?的主要内容,如果未能解决你的问题,请参考以下文章

iOS - Swift 我如何知道 copyItemAtPath 何时完成?

前端学算法之算法复杂度

如何知道何时提交或回滚事务

Apollo 如何足够聪明地知道何时合并,但不知道何时添加?

如何知道所有 Promise 何时在动态“可迭代”参数中得到解决?

Wireshark for Mac版 抓包的基本用法