2485

我正在学习 Big O Notation 运行时间和摊销时间。我理解O(n)线性时间的概念,这意味着输入的大小会成比例地影响算法的增长......例如,二次时间O(n 2 )等也是如此......甚至算法,例如置换生成器,具有O(n!)次,按阶乘增长。

例如,以下函数是O(n),因为算法的增长与其输入n成比例:

f(int n) {
  int i;
  for (i = 0; i < n; ++i)
    printf("%d", i);
}

类似地,如果有一个嵌套循环,时间将是 O(n 2 )。

但是O(log n)到底是什么?例如,完全二叉树的高度为O(log n)是什么意思?

我确实知道(也许不是很详细)对数是什么,从某种意义上说:log 10 100 = 2,但我不明白如何识别具有对数时间的函数。

4

31 回答 31

3133

我无法理解如何识别具有日志时间的功能。

对数运行时间函数最常见的属性是:

  • 选择执行某些操作的下一个元素是几种可能性之一,并且
  • 只需要选择一个。

或者

  • 执行动作的元素是 n 的数字

这就是为什么,例如,在电话簿中查找人员是 O(log n)。您无需检查电话簿中的每个人才能找到合适的人;取而代之的是,您可以通过根据他们的姓名按字母顺序查找的位置来简单地分而治之,并且在每个部分中,您只需要探索每个部分的子集,然后才能最终找到某人的电话号码。

当然,更大的电话簿仍然会花费您更长的时间,但它不会像额外大小的比例增加那样快速增长。


我们可以扩展电话簿示例来比较其他类型的操作及其运行时间。我们将假设我们的电话簿有具有唯一名称的企业(“黄页”)和可能没有唯一名称的人员(“白页”)。一个电话号码最多分配给一个人或一个企业。我们还将假设翻到特定页面需要恒定的时间。

以下是我们可能在电话簿上执行的一些操作的运行时间,从最快到最慢:

  • O(1)(在最坏的情况下):给定商家名称所在的页面和商家名称,找到电话号码。

  • O(1)(一般情况下):给定一个人的名字所在的页面和他们的名字,找到电话号码。

  • O(log n):给定一个人的名字,通过在你还没有搜索到的书的一半左右选择一个随机点来找到电话号码,然后检查这个人的名字是否在那个点。然后重复这个过程大约一半的人的名字所在的书的部分。(这是对人名的二分搜索。)

  • O(n):找出所有电话号码中包含数字“5”的人。

  • O(n):给定一个电话号码,找到那个号码的人或企业。

  • O(n log n):打印机办公室发生了混淆,我们的电话簿的所有页面都以随机顺序插入。通过查看每一页上的名字,然后将该页放在新的空电话簿中的适当位置,修正排序,使其正确。

对于以下示例,我们现在在打印机办公室。电话簿正在等待邮寄给每个居民或企业,每个电话簿上都有一个标签,标识应该邮寄到哪里。每个人或企业都有一本电话簿。

  • O(n log n):我们想要个性化电话簿,所以我们要在他们指定的副本中找到每个人或企业的名字,然后在电话簿中圈出他们的名字,并写一个简短的感谢信感谢他们的惠顾.

  • O(n 2 ):办公室发生错误,每个电话簿中的每个条目在电话号码的末尾都有一个额外的“0”。取一些白色并删除每个零。

  • O(n · n!):我们已准备好将电话簿装载到装运码头上。不幸的是,本应装载书籍的机器人出现了故障:它正在以随机顺序将书籍放到卡车上!更糟糕的是,它将所有书籍装载到卡车上,然后检查它们的顺序是否正确,如果不正确,它会卸载它们并重新开始。(这是可怕的bogo 排序。)

  • O(n n ):您修复了机器人,以便它正确加载东西。第二天,您的一位同事对您进行恶作剧,并将装卸平台机器人连接到自动打印系统。每次机器人去加载原始电话簿时,工厂打印机都会重复运行所有电话簿!幸运的是,机器人的错误检测系统足够复杂,当遇到要加载的复制书时,机器人不会尝试打印更多的副本,但它仍然必须加载每本已打印的原始和复制书。

于 2010-02-21T20:14:59.797 回答
822

O(log N)基本上意味着时间呈线性增长,而n呈指数增长。因此,如果1计算元素需要几秒钟,计算元素10需要2几秒钟,计算100元素需要几3秒钟1000,等等。

这是O(log n)当我们进行分而治之的算法类型时,例如二进制搜索。另一个例子是快速排序,每次我们将数组分成两部分,每次都需要O(N)时间来找到一个枢轴元素。因此它 N O(log N)

于 2010-02-21T20:18:24.213 回答
622

这个问题已经发布了许多好的答案,但我相信我们确实错过了一个重要的答案——即图示的答案。

完全二叉树的高度为 O(log n) 是什么意思?

下图描绘了一个二叉树。请注意,每个级别包含的节点数量是上面级别的两倍(因此是binary):

二叉树

二分搜索是一个复杂的例子O(log n)。假设图 1 中树的底层节点表示某个排序集合中的项目。二分搜索是一种分而治之的算法,该图显示了我们将如何(最多)需要 4 次比较来找到我们在这个 16 项数据集中搜索的记录。

假设我们有一个包含 32 个元素的数据集。继续上图,发现我们现在需要 5 次比较才能找到我们要搜索的内容,因为当我们乘以数据量时,树只增长了一层。因此,算法的复杂度可以描述为对数阶。

在一张普通的纸上绘图log(n),将产生一个曲线的上升随着n增加而减速的图形:

O(log n)

于 2010-02-21T22:22:25.757 回答
280

概述

其他人给出了很好的图表示例,例如树形图。我没有看到任何简单的代码示例。所以除了我的解释之外,我会提供一些带有简单打印语句的算法来说明不同算法类别的复杂性。

首先,您需要对对数有一个大致的了解,可以从https://en.wikipedia.org/wiki/Logarithm获得。自然科学使用e和自然日志。工程弟子会使用 log_10(以 10 为底的对数),而计算机科学家将大量使用 log_2(以 2 为底的对数),因为计算机是基于二进制的。有时你会看到 natural log as 的缩写ln(),工程师通常将 _10 关闭而只使用log()log_2 缩写为lg()。所有类型的对数都以相似的方式增长,这就是它们共享相同类别的原因log(n)

当您查看下面的代码示例时,我建议您查看 O(1),然后是 O(n),然后是 O(n^2)。在你对这些很好之后,再看看其他的。我已经包含了干净的示例和变体,以展示细微的变化仍然可以导致相同的分类。

您可以将 O(1)、O(n)、O(logn) 等视为增长的类别或类别。有些类别比其他类别需要更多时间。这些类别有助于为我们提供一种排序算法性能的方法。随着输入 n 的增长,一些增长得更快。下表以数字方式说明了所述增长。在下表中,将 log(n) 视为 log_2 的上限。

在此处输入图像描述

各种大 O 类别的简单代码示例:

O(1) - 恒定时间示例:

  • 算法1:

算法 1 打印 hello 一次,它不依赖于 n,所以它总是在恒定时间内运行,所以它是O(1).

print "hello";
  • 算法2:

算法 2 打印 hello 3 次,但它不依赖于输入大小。即使随着 n 的增长,这个算法也只会打印 hello 3 次。话虽如此 3, 是一个常数,所以这个算法也是O(1).

print "hello";
print "hello";
print "hello";

O(log(n)) - 对数示例:

  • 算法 3 - 这就像“log_2”

算法 3 演示了在 log_2(n) 中运行的算法。请注意 for 循环的后操作将 i 的当前值乘以 2,因此i从 1 到 2 到 4 到 8 到 16 到 32 ...

for(int i = 1; i <= n; i = i * 2)
  print "hello";
  • 算法 4 - 这就像“log_3”

算法 4 演示了 log_3。通知i从 1 到 3 到 9 到 27...

for(int i = 1; i <= n; i = i * 3)
  print "hello";
  • 算法 5 - 这就像“log_1.02”

算法 5 很重要,因为它有助于表明,只要数字大于 1 并且结果与自身重复相乘,就表明您正在查看对数算法。

for(double i = 1; i < n; i = i * 1.02)
  print "hello";

O(n) - 线性时间示例:

  • 算法 6

这个算法很简单,打印 hello n 次。

for(int i = 0; i < n; i++)
  print "hello";
  • 算法 7

该算法显示了一个变体,它将打印 hello n/2 次。n/2 = 1/2 * n。我们忽略 1/2 常数,看到这个算法是 O(n)。

for(int i = 0; i < n; i = i + 2)
  print "hello";

O(n*log(n)) - nlog(n) 示例:

  • 算法 8

将此视为 和 的O(log(n))组合O(n)。for 循环的嵌套帮助我们获得O(n*log(n))

for(int i = 0; i < n; i++)
  for(int j = 1; j < n; j = j * 2)
    print "hello";
  • 算法 9

算法 9 类似于算法 8,但每个循环都允许变化,这仍然导致最终结果为O(n*log(n))

for(int i = 0; i < n; i = i + 2)
  for(int j = 1; j < n; j = j * 3)
    print "hello";

O(n^2) - n 平方示例:

  • 算法 10

O(n^2)通过嵌套标准 for 循环很容易获得。

for(int i = 0; i < n; i++)
  for(int j = 0; j < n; j++)
    print "hello";
  • 算法 11

与算法 10 类似,但有一些变化。

for(int i = 0; i < n; i++)
  for(int j = 0; j < n; j = j + 2)
    print "hello";

O(n^3) - n 立方示例:

  • 算法 12

这类似于算法 10,但使用 3 个循环而不是 2 个循环。

for(int i = 0; i < n; i++)
  for(int j = 0; j < n; j++)
    for(int k = 0; k < n; k++)
      print "hello";
  • 算法 13

与算法 12 类似,但有一些变化仍会产生O(n^3).

for(int i = 0; i < n; i++)
  for(int j = 0; j < n + 5; j = j + 2)
    for(int k = 0; k < n; k = k + 3)
      print "hello";

概括

上面给出了几个直接的例子和变化,以帮助演示可以引入哪些细微的变化,而这些变化实际上不会改变分析。希望它能给你足够的洞察力。

于 2016-04-26T22:50:50.647 回答
277

下面的解释是使用完全平衡二叉树的情况来帮助您理解我们如何获得对数时间复杂度。

二叉树是这样一种情况,其中一个大小为 n 的问题被划分为大小为 n/2 的子问题,直到我们遇到大小为 1 的问题:

二叉树的高度

这就是您获得 O(log n) 的方式,这是需要在上述树上完成的工作量才能达到解决方案。

具有 O(log n) 时间复杂度的常见算法是二分搜索,其递归关系为 T(n/2) + O(1),即在树的每个后续级别,您将问题分成两半并做恒定数量的额外工作。

于 2012-10-26T19:33:54.980 回答
157

如果你有一个函数需要:

1 millisecond to complete if you have 2 elements.
2 milliseconds to complete if you have 4 elements.
3 milliseconds to complete if you have 8 elements.
4 milliseconds to complete if you have 16 elements.
...
n milliseconds to complete if you have 2^n elements.

然后它需要 log 2 (n) 时间。大O 表示法,松散地说,意味着该关系只需要对大 n 为真,并且可以忽略常数因子和较小的项。

于 2010-02-21T20:11:56.137 回答
128

对数

好的,让我们尝试完全理解对数实际上是什么。

想象一下,我们有一根绳子,我们把它绑在一匹马上。如果绳子直接系在马身上,马需要拉开(例如,从男人身上)的力直接为 1。

现在想象绳子绕在一根杆子上。要逃跑的马现在必须用力拉很多倍。次数取决于绳子的粗糙度和杆子的大小,但我们假设它将一个人的力量乘以 10(当绳子转一圈时)。

现在,如果绳子绕了一次,马需要用力拉 10 倍。如果人类决定让马很难过,他可能会再次将绳子绕在一根杆子上,使其强度增加 10 倍。第三个循环将再次将强度增加 10 倍。

在此处输入图像描述

我们可以看到,对于每个循环,该值增加 10。获得任何数字所需的圈数称为该数字的对数,即我们需要 3 个帖子将您的力量乘以 1000 倍,6 个帖子将您的力量乘以1,000,000。

3 是 1,000 的对数,6 是 1,000,000(以 10 为底)的对数。

那么 O(log n) 实际上是什么意思呢?

在我们上面的例子中,我们的“增长率”是O(log n)。每增加一个环,我们的绳索可以承受的力就会增加 10 倍:

Turns | Max Force
  0   |   1
  1   |   10
  2   |   100
  3   |   1000
  4   |   10000
  n   |   10^n

现在上面的例子确实使用了以 10 为底,但幸运的是,当我们谈论大 o 表示法时,对数的底是微不足道的。

现在让我们假设您正在尝试猜测 1-100 之间的数字。

Your Friend: Guess my number between 1-100! 
Your Guess: 50
Your Friend: Lower!
Your Guess: 25
Your Friend: Lower!
Your Guess: 13
Your Friend: Higher!
Your Guess: 19
Your Friend: Higher!
Your Friend: 22
Your Guess: Lower!
Your Guess: 20
Your Friend: Higher!
Your Guess: 21
Your Friend: YOU GOT IT!  

现在你需要 7 次猜测才能做到这一点。但这里的关系是什么?从每个额外的猜测中,你能猜到的最多项目是多少?

Guesses | Items
  1     |   2
  2     |   4
  3     |   8
  4     |   16
  5     |   32
  6     |   64
  7     |   128
  10    |   1024

使用该图,我们可以看到,如果我们使用二分搜索来猜测 1-100 之间的数字,最多需要7 次尝试。如果我们有 128 个数字,我们也可以在 7 次尝试中猜测该数字,但 129 个数字最多需要8 次尝试(相对于对数,这里我们需要 7 次猜测 128 值范围,10 次猜测 1024 值范围. 7 是 128 的对数,10 是 1024 的对数(以 2 为底)。

请注意,我将“最多”加粗。Big-O 表示法总是指最坏的情况。如果你幸运的话,你可以一次猜出这个数字,所以最好的情况是 O(1),但那是另一回事了。

我们可以看到,对于每一次猜测,我们的数据集都在缩小。识别算法是否具有对数时间的一个好的经验法则是查看每次迭代后数据集是否按特定顺序收缩

O(n log n)呢?

你最终会遇到一个线性时间O(n log(n))算法。上面的经验法则再次适用,但这次对数函数必须运行 n 次,例如减少列表的大小n 次,这发生在诸如合并排序之类的算法中。

您可以轻松识别算法时间是否为 n log n。寻找一个遍历列表 (O(n)) 的外循环。然后看看有没有内循环。如果内部循环在每次迭代中切割/减少数据集,则该循环为 (O(log n)),因此整体算法为 = O(n log n)

免责声明:绳索对数示例取自W.Sawyer 的优秀数学家的喜悦书

于 2016-10-08T14:27:54.077 回答
106

运行时间(O(log n)_最多需要,那么它看起来像一个时间复杂度。x2x4xO(log n)

于 2010-02-21T20:10:14.143 回答
64

首先,我建议您阅读以下书籍;

算法(第 4 版)

这是一些功能及其预期的复杂性。数字表示语句执行频率

这是一些功能及其预期的复杂性

以下Big-O 复杂性图表也取自bigocheatsheet 大 O 复杂度图

最后一个非常简单的展示,展示了它是如何计算的;

程序语句执行频率的剖析。

分析程序的运行时间(示例)。

分析程序的运行时间

于 2017-07-02T20:35:42.553 回答
59

您可以通过说时间与 N 中的位数成正比来直观地想到 O(log N)。

如果一个操作对输入的每个数字或位执行恒定时间的工作,则整个操作所花费的时间将与输入中的数字或位的数量成正比,而不是输入的大小;因此,O(log N) 而不是 O(N)。

如果一个操作做出一系列恒定时间决策,每一个决策都将要考虑的输入大小减半(减少 3、4、5..),那么整体所花费的时间将与以 2 为底的对数(以 3 为底, base 4, base 5...) 的输入大小为 N,而不是 O(N)。

等等。

于 2010-02-21T20:13:49.823 回答
56

什么是 log b (n)?

它是在达到大小为 1 的部分之前,您可以将长度为 n 的对数重复切割成 b 等份的次数。

于 2010-03-19T19:28:43.917 回答
54

我一直不得不在脑海中想象一个在 O(log n) 中运行的算法的最佳方法如下:

如果您将问题大小增加一个乘数(即,将其大小乘以 10),则工作只会增加一个相加量。

将此应用于您的二叉树问题,以便您有一个很好的应用程序:如果您将二叉树中的节点数加倍,则高度仅增加 1(添加量)。如果你再次加倍,它仍然只增加了 1。(显然我假设它保持平衡等等)。这样,当问题规模成倍增加时,您的工作量不会增加一倍,而您只需要做更多的工作。这就是 O(log n) 算法很棒的原因。

于 2010-02-22T19:51:33.887 回答
19

分而治之的算法通常logn对运行时间有影响。这来自输入的重复减半。

在二分搜索的情况下,每次迭代都会丢弃一半的输入。需要注意的是,在 Big-O 表示法中,log 是 log base 2。

编辑:如前所述,对数基数无关紧要,但是在推导算法的 Big-O 性能时,对数因子将来自减半,因此我将其视为基数 2。

于 2010-02-21T20:11:43.790 回答
15

O(log n) 有点误导,更准确地说是 O(log 2 n),即(以 2 为底的对数)。

平衡二叉树的高度为 O(log 2 n),因为每个节点都有两个(注意 log 2 n 中的“二”)子节点。因此,具有 n 个节点的树的高度为 log 2 n。

另一个例子是二分搜索,它的运行时间为 O(log 2 n),因为在每一步都将搜索空间除以 2。

于 2010-02-21T20:12:09.240 回答
15

但是 O(log n) 到底是什么?例如,>完全二叉树的高度为 O(log n) 是什么意思?

我将其改写为“完整二叉树的高度为 log n”。如果您逐步向下遍历,则计算完整二叉树的高度将为 O(log n)。

我无法理解如何识别具有对数时间的函数。

对数本质上是取幂的倒数。因此,如果您的函数的每个“步骤”都在从原始项目集中消除一个元素,那就是对时间算法。

对于树示例,您可以很容易地看到,随着您继续遍历,降低节点级别会减少指数数量的元素。查看按姓名排序的电话簿的流行示例本质上相当于向下遍历二叉搜索树(中间页面是根元素,您可以在每一步推断是向左还是向右)。

于 2013-05-26T08:35:37.380 回答
12

这两种情况将花费 O(log n) 时间

case 1: f(int n) {
      int i;
      for (i = 1; i < n; i=i*2)
        printf("%d", i);
    }


 case 2  : f(int n) {
      int i;
      for (i = n; i>=1 ; i=i/2)
        printf("%d", i);
    }
于 2013-07-05T03:43:07.553 回答
11

它只是意味着此任务所需的时间随着 log(n) 的增长而增长(例如:n = 10 时需要 2 秒,n = 100 时需要 4 秒,...)。阅读有关二进制搜索算法大 O 表示法的 Wikipedia 文章以获得更多精度。

于 2010-02-21T20:10:43.643 回答
11

O(log n)指在与对数成比例的时间内工作的函数(或算法,或算法中的步骤)(在大多数情况下通常以 2 为底,但并非总是如此,无论如何这对于 big-O 表示法来说是微不足道的*)输入的大小。

对数函数是指数函数的倒数。换句话说,如果你的输入呈指数增长(而不是你通常认为的线性增长),你的函数就会线性增长。

O(log n)运行时间在任何类型的分治应用程序中都很常见,因为您(理想情况下)每次都将工作减半。如果在每个划分或征服步骤中,您都在做恒定时间的工作(或不是恒定时间的工作,但时间增长比 慢O(log n)),那么您的整个功能就是O(log n)。每一步都需要输入线性时间是相当普遍的。这将达到总时间复杂度O(n log n)

二分查找的运行时间复杂度就是一个例子O(log n)。这是因为在二分搜索中,您总是在后面的每个步骤中忽略一半的输入,方法是将数组分成两半,并且每一步只关注一半。每个步骤都是固定时间的,因为在二分搜索中,您只需将一个元素与您的键进行比较,以便确定下一步该做什么,而不管您正在考虑的数组在任何时候有多大。因此,您大约执行 log(n)/log(2) 步骤。

归并排序的运行时间复杂度就是一个例子O(n log n)。这是因为您在每一步都将数组分成两半,导致总共大约 log(n)/log(2) 步。但是,在每个步骤中,您需要对所有元素执行合并操作(无论是对 n/2 个元素的两个子列表的一个合并操作,还是对 n/4 个元素的四个子列表的两个合并操作,都是无关紧要的,因为它增加了必须在每个步骤中对 n 个元素执行此操作)。因此,总复杂度为O(n log n)

*请记住,根据定义,大 O 表示法,常量无关紧要。同样通过对数基数规则的改变,不同基数的对数之间的唯一区别是一个常数因子。

于 2010-02-21T20:19:05.680 回答
11

简而言之:在算法的每一步,您都可以将工作量减半。(渐近等价于第三,第四,...)

于 2010-02-22T16:41:25.520 回答
8

如果您在图形计算器或类似工具上绘制对数函数,您会发现它的上升非常缓慢——甚至比线性函数还要慢。

这就是为什么具有对数时间复杂度的算法受到高度追捧的原因:即使对于非常大的 n(例如,假设 n = 10^8),它们的性能也超出了可接受的范围。

于 2010-02-21T20:11:07.807 回答
7

我可以添加一些有趣的东西,这是我很久以前在 Kormen 等人的书中读到的。现在,想象一个问题,我们必须在问题空间中找到解决方案。这个问题空间应该是有限的。

现在,如果你能证明,在你的算法的每次迭代中,你都会切断这个空间的一小部分,这不小于某个限制,这意味着你的算法在 O(logN) 时间内运行。

我应该指出,我们在这里谈论的是相对分数限制,而不是绝对限制。二分搜索是一个经典的例子。在每一步,我们都会丢弃 1/2 的问题空间。但二分查找并不是唯一这样的例子。假设,你以某种方式证明,在每一步你至少丢掉 1/128 的问题空间。这意味着,您的程序仍然在 O(logN) 时间运行,尽管比二进制搜索慢得多。这是分析递归算法的一个很好的提示。通常可以证明,在每个步骤中,递归不会使用多个变体,这会导致问题空间中某些部分的截断。

于 2014-02-04T12:52:03.167 回答
7

实际上,如果您有一个包含 n 个元素的列表,并从该列表创建一棵二叉树(就像在分治算法中一样),您将继续除以 2,直到您到达大小为 1 的列表(叶子)。

在第一步,你除以 2。然后你有 2 个列表 (2^1),你将每个列表除以 2,所以你有 4 个列表 (2^2),你再除,你有 8 个列表 (2^3 ) 依此类推,直到您的列表大小为 1

这给了你等式:

n/(2^steps)=1 <=> n=2^steps <=> lg(n)=steps

(你取每一边的 lg,lg 是对数基数 2)

于 2016-11-25T18:50:16.133 回答
7

每次我们编写算法或代码时,我们都会尝试分析其渐近复杂度。它不同于它的时间复杂度

渐近复杂度是算法执行时间的行为,而时间复杂度是实际执行时间。但是有些人可以互换使用这些术语。

因为时间复杂度取决于各种参数,即。
1. 物理系统
2. 编程语言
3. 编码风格
4. 还有更多......

实际执行时间不是一个很好的分析度量。


相反,我们将输入大小作为参数,因为无论代码是什么,输入都是相同的。 所以执行时间是输入大小的函数。

以下是线性时间算法的示例


线性搜索
给定 n 个输入元素,要在数组中搜索一个元素,您最多需要 'n' 次比较。换句话说,无论你使用什么编程语言,喜欢什么编码风格,在什么系统上执行它。在最坏的情况下,它只需要 n 次比较。执行时间与输入大小成线性比例。

它不仅仅是搜索,无论工作(增量、比较或任何操作)是输入大小的函数。

因此,当您说任何算法都是 O(log n) 时,这意味着执行时间是 log 乘以输入大小 n。

随着输入大小的增加,完成的工作(这里是执行时间)增加。(因此成比例)

      n      Work
      2     1 units of work
      4     2 units of work
      8     3 units of work

随着输入大小的增加,完成的工作也会增加,并且它独立于任何机器。如果你试图找出工作单位的价值它实际上取决于上面指定的参数。它会根据系统和所有的变化。

于 2018-02-09T05:59:46.207 回答
6

我可以举一个 for 循环的例子,也许一旦掌握了这个概念,在不同的上下文中可能会更容易理解。

这意味着在循环中,步长呈指数增长。例如

for (i=1; i<=n; i=i*2) {;}

该程序的 O 表示法的复杂度为 O(log(n))。让我们尝试手动遍历它(n 介于 512 和 1023 之间(不包括 1024):

step: 1   2   3   4   5    6    7    8     9     10
   i: 1   2   4   8   16   32   64   128   256   512

尽管 n 介于 512 和 1023 之间,但只发生了 10 次迭代。这是因为循环中的步骤呈指数增长,因此只需 10 次迭代即可到达终止。

x 的对数(以 a 为底)是 a^x 的反函数。

这就像说对数是指数的倒数。

现在尝试以这种方式看待它,如果指数增长非常快,那么对数增长(反向)非常缓慢。

O(n) 和 O(log(n)) 之间的差异很大,类似于 O(n) 和 O(a^n) 之间的差异(a 是一个常数)。

于 2015-05-16T04:27:13.037 回答
6

O(logn) 是衡量任何代码运行时性能的多项式时间复杂度之一。

我希望你已经听说过二分搜索算法。

假设您必须在大小为 N 的数组中找到一个元素。

基本上,代码执行就像 N N/2 N/4 N/8....等

如果将每个级别完成的所有工作相加,您最终将得到 n(1+1/2+1/4....) 并且等于 O(logn)

于 2019-05-30T12:05:21.977 回答
5

完整的二进制示例是 O(ln n),因为搜索看起来像这样:

1 2 3 4 5 6 7 8 9 10 11 12

搜索 4 会产生 3 个命中:6、3 然后是 4。并且 log2 12 = 3,这很好地近似于需要多少命中。

于 2010-02-21T20:11:01.630 回答
5

树

log x to base b = y是的倒数b^y = x

如果你有一个深度为 d、大小为 n 的 M-ary 树,那么:

  • 遍历整棵树 ~ O(M^d) = O(n)

  • 在树中走一条路径 ~ O(d) = O(log n to base M)

于 2013-05-28T07:07:39.657 回答
5

在信息技术中,它意味着:

  f(n)=O(g(n)) If there is suitable constant C and N0 independent on N, 
  such that
  for all N>N0  "C*g(n) > f(n) > 0" is true.

蚂蚁似乎这种符号主要来自数学。

在这篇文章中有一句话: DE Knuth,“BIG OMICRON AND BIG OMEGA AND BIG THETA”,1976 年

基于此处讨论的问题,我建议 SIGACT 的成员以及计算机科学和数学期刊的编辑采用上述定义的符号,除非可以很快找到更好的替代方案

今天是 2016 年,但我们今天仍在使用它。


在数学分析中,它意味着:

  lim (f(n)/g(n))=Constant; where n goes to +infinity

但即使在数学分析中,有时这个符号也被用于表示“C*g(n) > f(n) > 0”。

据我在大学里知道,这个符号是由德国数学家朗道 (1877-1938) 引入的

于 2014-04-02T11:37:43.997 回答
3

如果您正在寻找基于直觉的答案,我想为您提供两种解释。

  1. 想象一个非常高的山丘,基础也非常宽阔。到达山顶有两种方式:一种是专门的小路,绕着山顶盘旋而上,另一种是:像雕刻一样的小露台切出一个楼梯。现在,如果第一种方法是在线性时间 O(n) 内达到,则第二种方法是 O(log n)。

  2. 想象一个算法,它接受一个整数n作为输入,并在与时间成正比的时间内完成,那么它是 O(n) 或theta n(n),但如果它以与number of digits or the number of bits in the binary representation on number(log n) 时间。

于 2013-05-26T16:56:31.350 回答
2

分而治之范式中的算法的复杂度为 O(logn)。这里有一个例子,计算你自己的幂函数,

int power(int x, unsigned int y)
{
    int temp;
    if( y == 0)
        return 1;
    temp = power(x, y/2);
    if (y%2 == 0)
        return temp*temp;
    else
        return x*temp*temp;
}

来自http://www.geeksforgeeks.org/write-ac-program-to-calculate-powxn/

于 2015-06-27T08:20:17.303 回答
1

我想补充一点,树的高度是从根到叶子的最长路径的长度,而节点的高度是从该节点到叶子的最长路径的长度。路径表示我们在遍历两个节点之间的树时遇到的节点数。为了达到 O(log n) 的时间复杂度,树应该是平衡的,这意味着任何节点的孩子之间的高度差应该小于或等于 1。因此,树并不总是保证时间复杂度O(log n),除非它们是平衡的。实际上在某些情况下,在最坏的情况下,在树中搜索的时间复杂度可能是 O(n)。

您可以查看平衡树,例如AVL tree. 这一项在插入数据时平衡树,以便在树中搜索时保持 (log n) 的时间复杂度。

于 2013-12-01T23:52:14.037 回答