微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

什么是增长顺序?如何计算? 确切值是 n 吗? Rn不是恒定的吗?增长顺序有阶乘和斐波那契数列的例子

如何解决什么是增长顺序?如何计算? 确切值是 n 吗? Rn不是恒定的吗?增长顺序有阶乘和斐波那契数列的例子

为什么我要问这个问题

我最近开始阅读Sicp,并且我已经工作了 一直到Section 1.2.3。 我无法理解成长顺序的一些细节。请忍受我和 我的问题太长了。另请注意,我以前从未处理过算法分析。

我在Sicp中阅读的内容以及对此的想法

以下是Sicp的部分文字

确切值是 n 吗?

n 为衡量问题大小的参数。在前面的示例中,我们将n设为给定数字 函数是要计算的,但还有其他可能性。例如, 如果我们的目标是计算数字的平方根的近似值, 我们可能将n设为所需的位数精度。

采用Section 1.7中针对牛顿平方根法给出的以下步骤:

(define (sqrt x)
  (sqrt-iter 1.0 x))

这是(sqrt-iter)

(define (sqrt-iter guess x)
  (if (good-enough? guess x)
      guess
      (sqrt-iter (improve guess x)
                 x)))

good-enough?在哪里检查guess是否足够好近似值

(define (good-enough? guess x)
  (< (abs (- (square guess) x)) 0.001))

现在,根据Sicp的说法,0.001应该是n,但是不应该在n中输入(sqrt x)吗? 迭代次数根据输入x而变化,即所需的迭代次数 会根据数字的大小而改变。

这是我的python等效证明:

In [33]: sqrt(2)
1
1.5
1.4166666666666665
achieved 1.4142156862745097 in 3 iterations

In [34]: sqrt(4)
1
2.5
2.05
2.000609756097561
achieved 2.0000000929222947 in 4 iterations

In [35]: sqrt(1024)
1
512.5
257.2490243902439
130.61480157022683
69.22732405448895
42.00958563100827
33.19248741685438
32.02142090500024
achieved 32.0000071648159 in 8 iterations

因此,0.001不应为 k1 k2 ,因为它是一个恒定且独立于 n (此处是我们输入到sqrt的值)?

R(n)不是恒定的吗?

R(n)为该过程为大小为 n 的问题所需的资源量。 R(n)可能 测量使用的内部存储寄存器的数量,执行的基本机器操作的数量,等等。在电脑上 一次仅执行固定次数的操作,所需时间为 与执行的基本机器操作数量成比例。

在这里,Sicp说 R(n)可能是执行的基本运算的数量,但是 操作系统之间执行的操作数量是否有所不同?作为Linux机器可能 它有一定的步骤集,另一个是FreeBSD机器,另一个是Windows机器? 您会明白为什么我会这么说。

增长顺序

我们说 R(n)具有增长顺序Θ(f(n)),写为 R(n)=Θ(f(n ))(发音为“ f(n)的theta”),如果存在独立于n的正常数 k1 k2 ,从而 k1 f (n)≤R(n)≤k2 f(n) 的n (换句话说,对于大的 n ,值 R(n)被夹在 k1 f(n) k2 f(n)。)

现在,据我了解,我们通过计算代数后计算出的资源增长量 从函数R(n)f(n)接收值( R 函数吗?和 f 我在想什么?我不知道! )

有阶乘和斐波那契数列的例子

例如,使用线性递归过程来计算阶乘 在1.2.1节中描述,步数与 输入因此,该过程所需的步骤随着Θ(n)的增长而增加。我们也 看到所需的空间随着Θ(n)的增长而增加。对于迭代阶乘, 步数仍为Θ(n),但空间为Θ(1),即常数。的 树递归斐波那契计算需要Θ(ϕn)步骤和空间Θ(n), ϕ是第1.2.2节中所述的黄金分割率。

在这对我来说毫无意义-我认为该怎么做? 我是否考虑使用替代模型的替代数量? 还是我考虑评估的表达式数量?还是我考虑在那里 算术评估?

我了解在递归过程和迭代Θ(1)中,空间为Θ(n)(因为解释器每次递归还需要记住1件事)(解释器带有x的某个值并继续。)我不了解斐波那契计算(树递归)如何取Θ(n)。

我也不知道早期的 n 与步数之间的关系。

我的问题

这是我与增长顺序有关的所有问题的清单:

  1. 算法中的哪个精确值是n?

  2. R(n)中会发生什么? (假设它当然是函数 n 是否将值相除/相乘?

  3. 我认为应该采取什么步骤?

  4. 如何计算台阶和空间的增长顺序。

简而言之:

什么是增长顺序,如何计算?

解决方法

您要问的话题很长。我会尽力回答。但是,这是计算机科学的基本概念,在任何算法书籍的开头,您都会找到有关增长顺序(又称为Big-O,Big-Omega和Big-theta表示法)的章节。如果您有兴趣,我强烈推荐这本书:https://www.amazon.ca/Introduction-Algorithms-Thomas-H-Cormen/dp/0262033844

要回答您的问题,科学家无法比较他们的代码,因为原子操作在不同的机器上花费的时间不同。大量因素会影响计算机上代码的运行时间,例如OS负载,OS调度,在CPU上执行的原子操作等。因此,它们为增长顺序提供了理论定义。

一件事肯定会影响运行时间,这就是代码输入的大小,通常用n表示。由于n可能非常大,因此这些符号也称为渐近符号。因此,当我们谈论增长顺序时,我们假设n任意大(我们不在乎小的输入量)。简单来说,增长的顺序就是程序执行的原子步骤(也称为基本步骤)的数量。什么是原子?任何需要1或2或恒定数量CPU操作的操作(按常数,我的意思是它不依赖于n(输入大小))。让我举一个例子。 这段代码:

c = a + b

有一个原子步,它有一个求和和一个赋值。 另一个例子:

for i in 1..n:
   print(i)

此代码有一个原子步骤print(i),并重复了n次(让n为输入大小)。因此,我们说该程序具有n个原子操作(即,其增长顺序为O(n))。

所以,我希望到目前为止您了解什么是原子操作以及什么增长顺序(原子步数)。但是,通常,计算增长顺序并不容易,并且涉及大量数学运算。例如,此代码:

for i in 1..n:
   for j in i..n:
      print(i+j)

在此代码中,由于j取决于i,因此我们将进行n + (n-1) + ... + 1 = n*(n+1)/2原子操作。如果您计算该公式,则为n^2 + ...。由于n^2在结果中的指数最大,因此我们只关心该术语。在很大的输入量中,该术语占主导地位,我们说其增长顺序为O(n^2)(我知道很多细节都遗漏了)。 因此,当我们说程序A的增长顺序为O(n),而程序B的增长顺序为O(n^2)时,我们可以确定,对于较大的输入量,程序B的运行速度会慢得多比程序A(我们最终无需在计算机上运行代码就可以比较代码)。

总而言之,当输入量很大时,增长顺序是原子操作的数量,我们不关心小操作,我们只关心最大的操作块。

我的话可能会冒犯科学家和工程师。对我的科学家朋友:在这里解释增长顺序时,我在数学上并不准确(如果您关心确切的数学定义,请阅读我提到的书)。对我的工程师朋友说:是的,实际上,科学家在计算Big-o时忽略的那些小步骤实际上很重要,但是科学家需要简化基础,然后再讨论细节。

,

如果希望能够编写有效的代码,则必须了解算法的增长顺序。互联网上有关此主题的资源非常丰富,有些在数学上比其他资源更准确,但这是我自己在数学上不准确的解释:

假设您有一些数据,例如数据库中有100行,您想对其进行处理。现在,您实际上可以使用数据做什么?

也许您只想打印第一行。那有多难?好吧,不是很难。您只需要执行一个操作即可执行print(first_row)。如果您有1000行,您仍然可以仅执行一次操作就打印第一行,这种情况意味着您要处理多少数据都没有关系,它仍将花费相同(恒定)的时间, O(1)

然后,我们有另一个非常重要的增长顺序 O(log(n))。对于在排序数据中搜索的算法,这是典型的。如果您有100行排序的名称,并且您正在寻找某个名称,则只需进行几次比较即可。通过每次比较,您将数据切成两半,并在6次操作中以最坏的情况找到了名称。如果要查看1000个排序的行,则大约需要9-10个比较。这在搜索大数据时非常有效,您不必担心在排序数据的大数据集中搜索。总是很快。

现在,如果您想例如打印所有数据,则必须在每一行上调用print。对于100行,您将必须执行100次操作,而对于1000行,则必须进行1000次操作。这意味着您需要的工作量与正在处理的数据量成正比。这将是 O(n)

O(n * n)是指例如尝试在数据中查找重复的行。为此,您需要将每一行与其他每一行进行比较。因此,您必须对100行进行10000个比较,对1000行进行1000 000个比较!在这种情况下,您可以看到,所需的操作数量开始迅速增长,并且已经在增长,这可能会限制您可以在几秒钟之内在现代计算机上处​​理的数据量(通常是您想要的)。这就是为什么当您看到彼此有两个for循环时,必须开始小心的原因。这也称为多项式增长。

O(e ^ n)是指数增长,并且增长非常非常非常非常快。考虑一种情况,您有100行带有随机数。如果要找到这些数字的所有子集,总和为50,则将需要进行1267650600228229401401496703205376运算。对于1000行,则它将是一个300个零的数字。对于您来说,这对于程序员来说意味着,如果您试图像这样处理1000个数字,就永远不会得到结果。

O(n!)是阶乘复杂度,它增长得甚至更快,并且您不会使用这种算法处理任何大数据集。一个典型的例子是用蛮力搜索解决旅行商问题。这意味着如果有100行包含100个城市,并且您想知道应该按什么顺序访问它们,那么您将采用最短的路径来计算每种可能的旅行组合所需的距离,这意味着要执行多个带有157个零的运算,而对于1000个城市,该数字将具有数千个零。这是一个您甚至无法想象的疯狂数字。考虑一下宇宙中大约有10 ^ 78到10 ^ 82个原子!换句话说,您可以针对计算机上的少数几个城市优化销售员的路径。

您应该能够根据增长的速度对增长顺序进行排序。将其绘制成图表并始终在您要编写某种算法并且发现自己需要投入资金时考虑一下这张图片会很有帮助。一次又一次地循环。

Paul Ray

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。