What would cause an algorithm to have O(log log n) complexity?
前面的问题解决了一些可能导致算法具有O(log n)复杂性的因素。
什么会导致算法具有时间复杂性O(日志日志N)?
O(log log n)术语可以出现在各种不同的地方,但通常有两条主要的路径会到达这个运行时。好的。平方根收缩
正如在对链接问题的回答中所提到的,一个算法具有时间复杂度o(log n)的常见方法是该算法通过在每次迭代中重复地将输入的大小减少一些常量因子来工作。如果是这种情况,算法必须在O(log n)迭代后终止,因为在O(logn)除以常量后,算法必须将问题大小缩小到0或1。这就是为什么二进制搜索具有复杂性O(log n)。好的。
有趣的是,有一种类似的方法可以缩小产生O(log log n)形式运行时的问题的大小。如果我们取每层大小的平方根,而不是将每层的输入分成两半,会发生什么?好的。
例如,让我们以数字65536为例。我们要用2除多少次,直到我们降到1?如果我们这样做,我们会好的。
- 65536/2=32768
- 32768/2=16384
- 16384/2=8192
- 8192/2=4096
- 4096/2=2048年
- 2048/2=1024
- 1024/2=512
- 512/2=256
- 256/2=128
- 128/2=64
- 64/2=32
- 32/2=16
- 16/2=8
- 8/2=4
- 4/2=2
- 2/2=1
这个过程需要16个步骤,65536=216也是如此。好的。
但是,如果我们在每个层次上取平方根,我们得到好的。
- RADIC;65536=256
- RADIC;256=16
- RADIC;16=4
- RADIC;4=2
注意,只需要四个步骤就可以到达2。为什么会这样?好吧,让我们用2的幂重写这个序列:好的。
- √65536=√216=(216)1/2=28=256
- √256=半径;28=(28)1/2=24=16
- √16=半径;24=(24)1/2=22=4
- √4=√22=(22)1/2=21=2
注意,我们遵循序列216→28→24→22→21。在每次迭代中,我们将两次幂的指数减半。这很有趣,因为这与我们已经知道的联系在一起——在数字k降为零之前,你只能将它除以半o(logk)次。好的。
因此,取任意数n,并将其写为n=2k。每次取n的平方根时,该方程中的指数减半。因此,只有K(log k)平方根在K下降到1或更低之前应用(在这种情况下,N下降到2或更低)。由于n=2k,这意味着k=log>2</Sub>n,因此取的平方根数为O(log k)=O(log log n)。因此,如果有一种算法通过重复地将问题减少到大小为原始问题大小平方根的子问题来工作,那么该算法将在O(log log n)步骤之后终止。好的。
其中一个真实的例子是van emde boas树(veb树)数据结构。veb树是一种专门的数据结构,用于存储范围为0…N—1。它的工作原理如下:树的根节点中有√n指针,拆分范围0…n-1放入√n个桶中,每个桶包含一系列大致√n个整数。然后,这些存储桶在内部被细分为√(√n)个存储桶,每个存储桶大致包含√(√n)个元素。要遍历树,从根开始,确定属于哪个桶,然后递归地继续在适当的子树中。由于veb树的结构,您可以在o(1)时间内确定要下降到哪个子树,因此在o(log log n)步骤之后,您将到达树的底部。因此,在VEB树中查找只需要O(日志日志N)时间。好的。
另一个例子是Hopcroft财富最近点对算法。该算法试图在二维点集合中找到两个最近的点。它通过创建一个桶网格并将点分布到这些桶中来工作。如果在算法中的任何一个点上发现一个bucket中有超过√n个点,则算法将递归地处理该bucket。因此,递归的最大深度是O(log log n),通过对递归树的分析,可以发现树中的每一层都起O(n)作用。因此,该算法的总运行时间是O(n log log n)。好的。小输入上的O(log n)算法
还有一些其他的算法通过对大小为O(log n)的对象使用二进制搜索等算法来实现O(loglogn)运行时。例如,x-fast trie数据结构对at-tree of height o(log u)的层执行二进制搜索,因此其某些操作的运行时为o(loglogu)。相关的y-fast trie通过维护每个o(log u)节点的平衡bsts来获取其一些o(loglogu)运行时,从而允许在这些树中的搜索在time o(loglogu)中运行。探戈树和相关的多播放树数据结构在其分析中以O(log log n)术语结尾,因为它们维护的树中每个都包含O(logn)项。好的。型其他示例
其他算法以其他方式实现运行时O(log log n)。插值搜索期望运行时O(log log n)在排序的数组中找到一个数字,但是分析是相当公平的。最后,分析工作表明迭代次数等于数字K,使得N2-K≤2,日志log n是正确的解决方案。一些算法,如Cheriton-Tarjan-MST算法,通过求解一个复杂的约束优化问题,到达了一个包含O(log log n)的运行时。好的。型
希望这有帮助!好的。型好啊。
在时间复杂度中看到O(对数n)因子的一种方法是像另一个答案中解释的那样通过除法,但当我们想在时间和空间/时间和近似/时间和硬度/之间进行权衡时,还有另一种方法可以看到这个因子。我们的算法上有一些人工迭代。
例如,SSSP(单源最短路径)在平面图上有一个O(N)算法,但是在这个复杂的算法之前,有一个更简单的算法(但仍然相当困难),它的运行时间为O(N日志N),算法的基础如下(只是非常粗略的描述,我将提供跳过理解这一部分和阅读其他R部分答案):
但我的观点是,这里我们选择的分区大小是O(log n/(logn))。如果我们选择其他分区,如O(log n/(logn)^2),这可能会运行得更快并带来另一个结果。我的意思是,在许多情况下(比如在近似算法或随机算法中,或者像SSSP这样的算法中),当我们迭代某个东西(子问题、可能的解,…)时,我们选择与我们所拥有的交易相对应的迭代次数(时间/空间/算法的复杂性/算法的常数因子,…)。因此,在实际的工作算法中,我们可能会看到比"log log n"更复杂的东西。