什么会导致算法具有O(log log n)复杂度?


Answers:


217

O(log log n)项可以出现在许多不同的位置,但是通常有两条主要路径会到达此运行时。

缩小平方根

如对链接问题的回答中所述,一种算法具有时间复杂度O(log n)的常见方式是,该算法通过在每次迭代中将输入的大小重复缩减一些恒定因子来工作。在这种情况下,该算法必须在O(log n)迭代后终止,因为在将O(log n)除以一个常数之后,该算法必须将问题的大小缩小到0或1。这就是为什么这样的原因,二进制搜索的复杂度为O(log n)。

有趣的是,有一种类似的方法可以缩小问题的大小,从而产生运行时间为O(log log n)的形式。而不是在每一层将输入分成两半,如果我们在每一层取大小的平方根会发生什么?

例如,让我们以65,536为例。我们必须除以2几次,直到降至1?如果我们这样做,我们得到

  • 65,536 / 2 = 32,768
  • 32,768 / 2 = 16,384
  • 16,384 / 2 = 8,192
  • 8,192 / 2 = 4,096
  • 4,096 / 2 = 2,048
  • 2,048 / 2 = 1,024
  • 1,024 / 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个步骤,并且65,536 = 2 16也是这种情况。

但是,如果我们在每个级别取平方根,

  • √65,536= 256
  • √256= 16
  • √16= 4
  • √4= 2

请注意,仅需四个步骤即可将其降至2。这是为什么呢?

首先,一个直观的解释。n和√n中有几位数?数字n中大约有log n个数字,√n中大约有log(√n)= log(n 1/2)=(1/2)log n个数字。这意味着,每次取平方根时,您将数字中的位数大约减半。因为您只能将数量k O(log k)的一半减半使其下降为常数(例如2),所以这意味着在减少数量之前只能取平方根O(log log n)的次数。到某个常数(例如2)。

现在,让我们做一些数学运算以使其变得更加严格。用两个的幂重写上面的序列:

  • √65,536=√2 16 =(2 161/2 = 2 8 = 256
  • √256=√2 8 =(2 81/2 = 2 4 = 16
  • √16=√2 4 =(2 41/2 = 2 2 = 4
  • √4=√2 2 =(2 21/2 = 2 1 = 2

注意,我们遵循序列2 16 →2 8 →2 4 →2 2 →2 1。在每次迭代中,我们将指数的二分之一减半。这很有趣,因为这与我们已经知道的联系在一起-您只能在将k降为零之前将其除以O(log k)的一半。

因此,取任意数字n并将其写为n = 2 k。每次取n的平方根,都会使该方程式的指数减半。因此,在k降至1或更低(在这种情况下n降至2或更低)之前只能应用O(log k)平方根。由于n = 2 k,这意味着k = log 2 n,因此取平方根的数目为O(log k)= O(log log n)。因此,如果有一种算法可以通过反复地将问题缩小为一个子问题,该子问题的大小是原始问题大小的平方根,则该算法将在O(log log n)个步骤后终止。

van Emde Boas树就是其中一个真实的例子(vEB-tree)数据结构。vEB树是一种专用的数据结构,用于存储范围为0 ... N-1的整数。它的工作方式如下:树的根节点中有√N个指针,将范围划分为0 ... N- 1个到√N个存储桶中,每个存储区域包含大约√N个整数。然后将这些存储桶在内部每个细分为√(√N)个存储桶,每个存储有大约√(√N)个元素。要遍历该树,请从根开始,确定您属于哪个存储桶,然后在适当的子树中递归继续。由于vEB树的结构方式,您可以在O(1)时间中确定要进入哪个子树,因此在执行O(log log N)步骤之后,您将到达树的底部。因此,在vEB树中的查找仅花费时间O(log log N)。

另一个例子是Hopcroft-Fortune最接近点对算法。该算法尝试在2D点的集合中找到两个最接近的点。它通过创建存储桶网格并将点分布到这些存储桶中来工作。如果在算法中的任何一点上发现一个存储桶,其中有超过√N个点,则该算法将对该存储桶进行递归处理。因此,递归的最大深度为O(log log n),并且通过对递归树的分析,可以证明树中的每一层都可以执行O(n)。因此,该算法的总运行时间为O(n log log n)。

小输入的O(log n)算法

还有其他一些算法可以通过使用算法来实现O(log log n)运行时,例如对大小为O(log n)的对象进行二进制搜索。例如,x-fast trie数据结构在高度为O(log U)的树的层上执行二进制搜索,因此其某些操作的运行时为O(log log U)。相关的y-fast trie通过维护每个O(log U)节点的平衡BST来获取其O(log log U)运行时的某些时间,从而允许在这些树中进行搜索以在O(log log U)的时间运行。在探戈树和相关multisplay树的数据结构,结束了一个O(log日志N)长期在他们的分析,因为他们认为,含有O(log n)的项目每个树。

其他例子

其他算法以其他方式实现运行时O(log log n)。 插值搜索期望运行时O(log log n)在排序数组中找到一个数字,但是分析相当复杂。最终,该分析通过显示迭代次数等于次数k使得n 2 -k≤2来进行工作,其中log log n是正确的解决方案。诸如Cheriton-Tarjan MST算法之类的某些算法通过解决复杂的约束优化问题而到达包含O(log log n)的运行时。

希望这可以帮助!


5
@ JonathonReinhart-这恰好是我认为(a)非常非常酷,并且(b)不为人所知的东西。我总是很高兴分享这样的事情!:-)
templatetypedef

1
@ Mahesha999 Stack Overflow 积极鼓励用户回答自己的问题。:-)
templatetypedef

只是猜测“问问题”页面底部的“回答您自己的问题”可能还有其他含义,或者只是启用了问题页面上的答案文本框。
Mahesha999 '16

1
重要提示:因此,如果有一种算法可以通过反复地将问题缩小为一个子问题,该子问题的大小是原始问题大小的平方根,则该算法将在O(log log n)个步骤后终止。
Gun2sh

3

在时间复杂度中观察O(log log n)因子的一种方法是像其他答案中所解释的那样进行除法,但是当我们想在时间和空间/时间之间进行交易时,还有另一种方法可以观察到此因子。以及算法的近似/时间和硬度/ ...,我们对算法进行了一些人工迭代。

例如,SSSP(单源最短路径)在平面图上具有O(n)算法,但是在该复杂算法之前,运行时间为O(n log log n)的算法要简单得多(但仍然相当困难),因此,算法的基础如下(仅是非常粗略的描述,我将跳过这一部分,而阅读答案的另一部分):

  1. 将图分成大小为O(log n /(log log n))的部分,但有一些限制。
  2. 假设提到的每个部分都是新图形G'中的节点,然后在时间O(| G'| * log | G'|)==>中为G'计算SSSP,因为| G'| = O(| G | * log log n / log n)我们可以看到(log log n)因子。
  3. 为每个部分计算SSSP:再次因为我们有O(| G'|)部分,所以我们可以在时间| n / logn |中计算所有部分的SSSP。* | log n / log logn *日志(logn / log log n)。
  4. 更新权重,这部分可以在O(n)中完成。有关更多详细信息,本讲义很好。

但是我的意思是,在这里我们选择除法为O(log n /(log log n))。如果我们选择O(log n /(log log n)^ 2)之类的其他除法,则该除法可能会运行得更快并带来另一个结果。我的意思是,在许多情况下(例如近似算法或随机算法,或如上所述的SSSP之类的算法),当我们迭代某些事物(子问题,可能的解决方案...)时,我们选择与该事物的交易相对应的迭代次数我们有(时间/空间/算法的复杂性/算法的常数因子,...)。因此,在实际的工作算法中,我们可能会看到比“ log log n”更复杂的东西。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.