从技术上讲,这是“ Hello World”的O(1)算法吗?


117

将其归类为“ Hello,World!”的O(1)算法吗???

public class Hello1
{
   public static void Main()
   {
      DateTime TwentyYearsLater = new DateTime(2035,01,01);
      while ( DateTime.Now < TwentyYearsLater )
      { 
          System.Console.WriteLine("It's still not time to print the hello ...");
      }
      System.Console.WriteLine("Hello, World!");
   }
}

我正在考虑使用

DateTime TwentyYearsLater = new DateTime(2035,01,01);
while ( DateTime.Now < TwentyYearsLater )
{ 
   // ... 
}

代码片段是一个忙循环,当有人要求某种复杂性的算法时,这会成为一个笑话。这是正确的吗?


15
这不是O(N)复杂性O(1)
Fabjan

19
@SubparWebDev不,即使您知道启动程序与指定日期之间确切的时间差,您也不知道它将通过循环多少次。它是依赖于运行速度有多快它是计算机,还有什么是在其上运行,CPU如何调度任务,等等
Servy

131
@Fabjan N该算法没有依赖关系,因此您实际上不能说它是O(N)算法。
Servy

29
从技术上讲,没有输入,因此N甚至没有任何意义。但是,您可以考虑DateTime.Now使该输入仍然取决于结果的输入。如果您可以为假设一个实际值DateTime.Now,那么可以,该程序将循环执行恒定的时间。

43
问题陈述必须定义N是什么。
Yacoub Massad

Answers:


406

在这种情况下,Big O表示法用于描述函数输入的大小与计算该输入结果所需执行的操作数之间的关系。

您的操作没有输入可以与输出相关联,因此使用Big O表示法是没有意义的。操作所花费的时间与操作的输入无关(没有...)。由于来描述不存在的关系,输入和操作的执行,你不能使用大O数量之间没有关系


6
O(max(1, 2035 - yearTheProgramIsStarted))
Bergi 2015年

19
@Bergi [实际上没有[(stackoverflow.com/questions/34048740/…),您不能仅仅基于运行作业的时间来描述循环的迭代次数。当然,您还要结合以下事实:用户可以随时将系统时钟更改为他们想要的任何时间,依此类推。您仍然没有格式正确的输入可以准确地与许多产生输出所需的操作。哎呀,甚至输出本身也不一致。
–Servy

23
有人可能会认为系统状态(包括时钟)是程序输入的一部分。从这种意义上说,您可以将日期用作输入参数,尽管不是明确的。不过,这很奇怪。
康纳·克拉克

9
更明确地说,“隐式”输入是2035年1月1日到今天之间的增量。
康纳·克拉克 Connor Clark)

6
@Hoten但是系统时间不是固定值。此功能是一样的只是接受DateTime用于开始时间作为输入。就像我之前说的,系统时钟可以随时间变化。同样,您不能直接将您要描述的准输入映射到固定输出。对于给定的开始时间,甚至对于两个始终获得明智值的程序,都没有执行的已知数目的操作DateTime.Now,因此您不能随时间的变化而将两者相关联,因为在以下情况下您甚至无法将它们相关联:时间没有改变。
服务

88

Big-O表示大致“给定一个工作量N的运算,该算法需要多少计算时间(与N成正比)?”。例如,对大小为N的数组进行排序可以采用N ^ 2,Nlog(N)等。

这没有作用的输入数据量。所以不是O(anything)

甚至更糟; 从技术上讲,这不是算法。算法是一种用于计算数学函数值的方法-数学函数是从一个输入到输出的映射。由于这不需要输入且什么也不返回,因此从数学意义上讲它不是函数。从维基百科:

算法是一种有效的方法,可以在有限的空间和时间范围内并以定义良好的形式语言来表达,以计算函数。从初始状态和初始输入(可能为空)开始,这些指令描述了一种计算,该计算在执行时会经过有限数量的定义明确的连续状态,最终产生“输出”并终止于最终的结束状态。

从技术上讲,这是一个控制系统。来自维基百科;

控制系统是一种设备或一组设备,用于管理,命令,指导或调节其他设备或系统的行为。

对于希望更深入地回答数学函数和算法之间的区别以及计算机具有更强大的功能来完成诸如控制台输出,显示图形或控制机器人之类的副作用的人们,可以使用 阅读以下内容:强烈的教会转向假说

抽象

计算的经典观点将计算定位为输入(有理数或有限字符串)到输出的封闭式转换。根据计算的交互视图,计算是一个持续的交互过程,而不是输入到输出的基于功能的转换。具体而言,与外部世界的通信发生在计算过程中,而不是在计算之前或之后。这种方法从根本上改变了我们对什么是计算以及如何建模的理解。

交互作为一种新范式受到了强教会-图灵论文(SCT)的阻碍,该理论普遍认为图灵机(TM)可以捕获所有计算,因此比TM更具表现力的计算模型是不可能的。在本文中,我们表明SCT以图灵从未打算过的方式重新解释了原始的图灵图论论文(CTT)。它通常被认为等同于原著是神话。我们确定并分析了广泛相信SCT的历史原因。只有接受它是错误的,我们才能开始采用交互作为计算的另一种范式


不需要是序列。仅仅是一些数据输入,landau表示法描述了与该数据上的某些度量相关的运行时间,通常是与大小相关的。
Bergi 2015年

@Bergi-是的,明白你的意思!确实只是一个近似值,是的-如果您可以衡量要完成的工作量以及达到目标所需的步骤数,big-o可以反映这两种方法之间的关系。靠近一点?
史蒂夫·库珀

@kapep-这不是一个纯函数,因为它是一个void方法,但是如果我们计算控制台输出,它仍然是随机的;它可以输出{{“ Hello,World!”,“还没来得及打印你好... \ nHello,World!”,“还没来得及打印你好...呢?你好... \ n你好,世界!“,...}
史蒂夫·库珀

1
打印到标准输出不是输出吗?
rpax 2015年

4
@rpax在数学上不是,不是。函数是从输入到输出的不变转换;例如,“ square”是输入3时始终返回9的函数。如果具有相同参数的调用始终给出相同的返回值,则c#方法仅是数学函数。否则,如果它具有副作用,例如写入控制台,显示图形,分配内存,则这些都不是数学函数。(将添加指向我的答案的链接,该链接的内容非常详尽:))
史蒂夫·库珀

41

不,您的代码的时间复杂度为 O(2^|<DeltaTime>|)

对于当前时间的正确编码。
请让我先为我的英语道歉。

Big O是什么以及如何在CS中工作

Big O表示法不用于将程序的输入与其运行时间绑定在一起
大O表示法是表示两种量渐近比的一种严谨的方法。

在算法分析的情况下,这两个数量不是输入(必须首先具有“测量”功能)和运行时间。
它们是问题1实例的编码长度和关注度量。

常用的指标是

  1. 在给定的计算模型中完成算法所需的步骤数。
  2. 计算模型需要的空间(如果存在)。

隐式地将TM假定为模型,以便第一个点转换为转换2函数的应用次数,即“步骤”,第二个点转换为至少写入一次不同带单元的数目。

是否还经常隐含地假设我们可以使用多项式相关的编码来代替原始编码,例如,从头到尾搜索一个数组的函数很O(n)复杂,尽管该数组实例的编码长度应为n*b+(n-1)其中b,每个元素的符号数(常数)。这是因为它b被视为计算模型的常数,因此上面的表达式和n渐近地相同。

这也解释了为什么师之类的算法尽管本质上是一个相似的算法,但却是指数for(i=2; i<=sqr(N); i++)算法3

看到这个

这也意味着,大的O表示法可能会使用描述一个问题可能需要使用的尽可能多的参数,是否有一个k对于某些算法参数并不稀奇。

所以这不是关于“输入”或“没有输入”。

现在学习案例

Big O表示法不会质疑您的算法,它只是假设您知道自己在做什么。从本质上讲,它是一种可应用于任何地方的工具,甚至适用于可能故意棘手的算法(例如您的算法)。

为了解决您的问题,您使用了当前日期和将来的日期,因此它们必须以某种方式成为问题的一部分。简而言之:它们是问题实例的一部分。

具体来说,实例是:

<DeltaTime>

凡是<>指任何非病理性选择编码。

请参阅下面的非常重要的说明。

因此,您的O复杂度大时间为O(2^|<DeltaTime>|),因为您进行了许多迭代,具体取决于当前时间的值。放置其他数值常量没有意义,因为渐进符号很有用,因为它消除了常量(例如,使用O(10^|<DeltaTime>|*any_time_unit)无意义)。

棘手的地方在哪里

上面我们做了一个重要的假设:计算模型可以计算5倍的时间,而我指的是(真实的)物理时间。在标准计算模型中没有这样的概念,TM不知道时间,我们将时间与步数联系起来,因为这就是现实的工作方式4

在模型中,尽管时间是计算的一部分,但您可以使用功能性人员的术语,是说Main不是纯粹的,而是概念是相同的。

要理解这一点,应该注意,没有什么可以阻止框架使用比物理时间快两倍,五倍,十倍的假时间。这样,您的代码将以“时间”的“一半”,“五分之一”,“十分之一”运行。

这种反射对于选择的编码很重要<DeltaTime>,这本质上是编写<(CurrentTime,TimeInFuture)>的一种简化方式。由于时间不存在优先顺序,因此CurrentTime的编码很可能是前天的单词Now(或其他选择),可以编码为Yesterday,从而打破了编码长度增加的假设物理时间前进(DeltaTime之一减小)

为了做一些有用的事情,我们必须在计算模型中对时间进行适当的建模。

我们唯一可以做的安全选择是在物理时间向前移动时,对长度不断增加的时间戳进行编码(但仍不使用一元编码)。这是我们需要的时间的唯一真实属性,也是编码需要捕捉的时间。仅通过这种类型的编码,您的算法才可能具有时间复杂度。

你的困惑,如果有的话,从一个事实,即这个词出现的时间在短语“什么是它的时间复杂度?和“多少时间?” 意味着非常不同的事物

the,术语使用相同的词,但是您可以尝试使用“步骤复杂性”来重新询问自己的问题,希望这将帮助您理解答案确实是^ _ ^


1这也解释了渐近方法的必要性,因为每个实例具有不同但并非任意的长度。
2我希望我在这里使用正确的英语术语。
3这也是我们经常log(log(n))在数学中找到术语的原因。
4至少,步骤必须占据一定的时间间隔,但不能为空,也不能为未连接。
5这意味着计算模式是其中的物理时间知识,即可以用其术语来表示。类比是泛型在.NET框架中的工作方式。


3
“所以您的O大运行时间就是”。我确定您的意思是“ O大复杂性”吗?同样,我们仍然可以仅将'deltaTime'称为'n'..所以您所说的O(2 ^ N)类似于Fibonacci算法的复杂性。您是怎么来到“ 2 ^”的?
罗斯

@罗斯,谢谢你的意思。我有2个习惯于使用二进制数的习惯。关键是步与数字表示的长度成线性关系。实际的基础不是很重要,并且会根据特定的编码而有所不同。它是伪线性的
Yuni Mj 2015年

很抱歉,但是您能否在答案中进一步说明您得出的结论是,复杂度是O(2^n)多少?对于初学者尚不清楚。
Arturo TorresSánchez2015年

2
@YuniMj虽然你的推理是技术上没有错,我认为,坚持到测量尺寸DeltaTime,而不是它的价值,你只是添加额外的混乱。例如,但是没有最佳排序算法的推理具有时间复杂度$ O(n \ cdot log n)$。为什么?因为您只能有限地对许多可区分的对象进行排序,所以在这种情况下,您始终可以使用存储桶排序对$ O(n)$进行排序。或您的对象大小不受限制,在这种情况下,$ O(n \ cdot log n)$将不成立,因为单个比较不再具有恒定的时间...
fgp

1
FWIW O(2 ^ N)= O(10 ^ N)!stackoverflow.com/questions/19081673/...
森FD

29

尽管这里有很多很好的答案,但让我将它们全部改写一下。

存在Big-O符号来描述功能。当应用于算法分析时,这要求我们首先根据函数定义该算法的某些特征。常见的选择是考虑步数作为输入大小的函数。正如在其他答案中指出的那样,在您的情况下提出这样的功能似乎很奇怪,因为没有明确定义的“输入”。不过,我们仍然可以尝试这样做:

  • 我们可以将您的算法视为一个常数函数,它接受任何大小的任何输入,将其忽略,等待固定的时间,然后完成。在这种情况下,其运行时间为f(n)= const,并且它是O(1)时间算法。这是您期望听到的,对吗?是的,从技术上讲,它是O(1)-算法
  • 我们可以将其TwentyYearsLater视为感兴趣的类似于“ input-size”的参数。在这种情况下,运行时间为f(n)=(nx),其中x是调用时刻的“现在时间”。以这种方式看时,它是O(n)时间算法。每当您向其他人展示您的O(1)算法时,都可以期待这个反论点。
  • 哦,但是等一下,如果k =TwentyYearsLater是输入,则其大小 n实际上就是表示它的位数,即n = log(k)。因此,输入n的大小与运行时间之间的相关性为f(n)= 2 ^ n-x。好像您的算法刚刚变得指数级变慢!啊。
  • 程序的另一个输入实际上是OS对循环中的调用序列给出的答案流DateTime.Now。我们实际上可以想象,在运行程序时,将整个序列作为输入提供。然后可以认为运行时取决于该序列的属性,即直到第一个TwentyYearsLater元素的长度。在这种情况下,运行时间再次为f(n)= n,算法为O(n)

但是话又说回来,在您的问题中,您甚至没有说过您对运行时感兴趣。如果您指的是内存使用该怎么办?根据情况的建模方式,您可以说算法是O(1)内存,或者也许是O(n)内存(如果实现了DateTime.Now需要某种方式跟踪整个调用序列)。

而且,如果您的目标是提出一些荒唐的东西,那么为什么不全力以赴,说您对屏幕上以像素为单位的算法代码大小如何取决于所选的缩放级别感兴趣。这可能类似于f(zoom)= 1 / zoom,您可以自豪地将算法声明为O(1 / n)像素大小!


+1。我相信“操作系统对DateTime.Now调用序列给出的答案流是这里的真正输入。但是我认为结论不应该是O(n),而是O(k),其中k是长度直到第一TwentyYearsLater元件。
justhalf

7
到目前为止,这是最好的答案-为了使Big O有意义,您必须将数学语义/假设应用于物理实现(本质上为程序定义数学模型,并有意义地定义“输入”)。从这个意义上说,“程序”的复杂度取决于您所应用的语义-如果您假设N是随操作数线性变化的时差,则为O(n)。如果假设由于固定时间段而导致的固定操作数为O(1)。
Ant P

21

我不得不稍微不同意Servy。即使不是很明显,该程序也有输入,这是系统的时间。这可能不是您原本打算的技术,但您的TwentyYearsFromNow变量距系统现在的时间还不到二十年,而是静态分配给2035年1月1日。

因此,如果您使用此代码并在系统时间为1970年1月1日的计算机上执行该代码,则无论计算机的运行速度如何,它都将需要65年的时间才能完成(如果计算机的时钟有问题,可能会有所不同。 )。如果您采用此代码并在系统时间为2035年1月2日的计算机上执行该代码,它将几乎立即完成。

我想说您的输入nJanuary 1st, 2035 - DateTime.Now,它是O(n)。

然后还有操作数的问题。有人指出,更快的计算机将更快地进入循环,从而导致更多的操作,但这无关紧要。使用big-O表示法时,我们不会考虑处理器的速度或确切的操作数。如果您采用此算法并在计算机上运行它,然后再次运行它,但在同一台计算机上运行了10倍以上,则您期望的操作数将增长10倍。

至于:

我正在考虑使用[已编辑的代码]代码段作为忙碌的循环,当有人要求某种复杂性的算法时,这就像是在开玩笑。这是正确的吗?

不,不是。其他答案已经涵盖了这一点,因此我只想提及它。通常,您无法将执行年数与任何big-O表示法相关联。例如。没有办法说20年的执行时间= O(n ^ 87)或其他任何事情。即使在您给出的算法中,我也可以TwentyYearsFromNow将年份更改为20110、75699436或123456789,并且big-O仍为O(n)。


7
时间不是函数的输入,而是不断变化的状态,在整个方法执行过程中都会观察到状态。该功能运行时,甚至可以更改系统时钟。为了使Big O有意义,您还需要使每个输入与输出值1-1对应,以及计算该值所需的许多操作。对于此操作,输出甚至对于相同的输入也不是一致的(实际上,它的变化很大),除了执行的操作数也变化很大。
Servy 2015年

When working with big-O notation, we don't consider the speed of the processor or the exact number of operations.这是一个错误的陈述。您尝试计算Big O值的任何明智的操作几乎都不会改变基于硬件执行的操作数量,但是这一操作确实可以做到。大O只是将操作数量与输入大小相关联的一种方式。对于大多数独立于系统硬件的操作。 在这种情况下不是
–Servy

If you took this algorithm and ran it on a computer, and then ran it again but for 10x longer on the same computer, you would expect the number of operations to grow by the same factor of 10x.这也是错误的说法。环境不一定会线性地改变循环中的操作数。例如,计算机上可能存在其他程序,它们在不同的时间点使用或多或少的CPU时间,从而随着时间的推移不断更改为此应用程序分配的时间。
Servy

我在此使用@Servy,但出于稍微不同的原因。main函数不接受任何参数,也不返回任何输入。如果您愿意,它是nil => nil的函数。不管现在是什么时间,它仍然不会返回任何内容。
史蒂夫·库珀

1
如果我们使用这个定义-“在数学中,函数是一组输入和一组允许的输出之间的关系,其属性是每个输入都与一个输出正好相关。” (维基百科)-并且我们将控制台输出计为“函数的输出”,这有所不同,在速度更快的计算机上会更长,因为它将写为““现在还没有时间打印hello ...”“更多。
史蒂夫·库珀

13

Big-O分析处理涉及的处理量,因为处理的数据量无限制地增加。

在这里,您实际上只处理一个固定大小的对象。因此,进行big-O分析在很大程度上(主要是?)取决于您如何定义术语。

例如,您可能意味着一般要打印输出,并且要等待很长时间,以至于将/会在完全相同的时间段内打印任何合理数量的数据。您还必须以一些不寻常的(如果不是完全错误的)定义添加一些内容,以使内容走得更远-特别是big-O分析通常是根据执行操作所需的基本操作数定义的特定任务(但请注意,也可以从内存使用等方面来考虑复杂性,而不仅仅是CPU使用/执行的操作)。

通常,基本操作的数量与所花费的时间密切相关,因此,将两者视为同义词并不算太大。但是不幸的是,我们仍然停留在其他部分:正在处理的数据量没有限制地增加。在这种情况下,您可以强加固定的延迟,这确实有效。要将O(1)等同于O(N),您必须施加无限的延迟,以便永久打印任何固定数量的数据,就像无限数量的数据一样。


10

big-O相对于什么?

您似乎很直观,这twentyYearsLater是一个“输入”。如果确实您将函数编写为

void helloWorld(int years) {
   // ...
}

这将是O(N),其中N =年(或只是说 O(years))。

我想说的是,您的算法是O(N),相对于您偶然在以开头的代码行中写入的任何数字twentyYearsLater =。但是人们通常不会将实际源代码中的数字视为输入。他们可能会将命令行输入视为输入,或者将功能签名输入视为输入,但是很可能不是源代码本身。那就是您与朋友的争议-这是“输入”吗?您以某种方式设置代码以使其直观地看起来像是输入,并且您绝对可以就程序第6行的数字N询问其较大的O运行时间,但是如果使用这样的非默认选择作为输入,您确实需要明确说明。

但是,如果您认为输入更常用,例如命令行或函数输入,则根本没有输出,并且函数为O(1)。它需要二十年,但是由于big-O不会改变为恒定倍数,因此O(1)= O(二十年)。

类似的问题-什么是运行时:

void sortArrayOfSizeTenMillion(int[] array)

假设它按照说的去做,并且输入是有效的,并且该算法利用了快速排序或冒泡排序或任何合理的方法,则为O(1)。


对输入进行硬编码并不意味着输入消失。在任何情况下,O(1)时间复杂度的quicksort和bubbleort也都不会。bigocheatsheet.com
Theo Brinkman

@TheoBrinkman如果您想成为技术专家,请在Turing机器模型中,将您对输入的想法编码到Turing机器本身中,根据定义,它就不是输入。然后,图灵机将在恒定时间内运行,而不管其实际输入如何。从某种意义上说,它没有运行“冒泡排序”,因为它没有对任何东西进行排序,而是以自己的表示形式进行操作,但是,从非技术角度来讲,您当然可以将算法描述为冒泡排序。
djechlin

用同样的“非技术术语”,您可以将所讨论的算法描述为悬索桥。
Theo Brinkman 2015年

@TheoBrinkman不,你不能。这对任何人都没有意义。
djechlin 2015年

它像描述为O(1)冒泡排序一样有意义。
Theo Brinkman 2015年

8

该“算法”正确地描述为O(1)或恒定时间。有人争辩说,该程序没有输入,因此就Big Oh而言,没有N可分析。我不同意没有输入。将其编译为可执行文件并调用时,用户可以指定任意长度的任何输入。该输入长度为N。

程序只忽略输入(无论长度如何),因此无论输入长度如何(给定的固定环境=启动时间+硬件),所花费的时间(或执行的机器指令的数量)是相同的。 )。


但是,即使启动时间和硬件相同,操作次数也不一定是一致的。最重要的是,要声明O(1)算法,输出必须始终是恒定的,而不是恒定的,它会根据启动时间和硬件而变化很大。它也很容易是无限的,这肯定不是恒定的。有没有关系,你已经定义了输入和执行的操作的数量之间。那不是恒定的,只是不确定的。您无法命名一个有限的数字,并且知道总是会有比这更少的操作。
Servy 2015年

最长实时需要20年。如果我们将来启动它,是的,它将需要更长的时间。假设循环迭代所花费的时间有一个有限的下限,并且我们在串行硬件上运行。然后,我可以限制循环运行的次数,这意味着整个计算都可以由常量函数来限制,无论忽略的输入大小如何。
waldol1 2015年

Let's suppose that there is a finite lower bound on the amount of time a loop iteration takes这是一个错误的假设。该程序可以永远运行。我所要做的就是将系统时钟设置为从现在起50年,启动它,它将永远不会结束。或者,我可以使时钟向后移动的速度比向前移动的速度快,或者可以从过去的不确定时间开始。您根本无法假设程序运行的时间有下限。它可以永远运行。但是,即使我们将您的(假)假设视为正确,您仍然无法将执行的操作数与输入相关联。
–Servy

单循环迭代需要有限的时间。它可能执行无数次,但每个执行次数应大致恒定。我认为这个假设没有问题。
waldol1 2015年

通过这种[完全不正确]的逻辑,每个算法都总是 O(1),因为每个单独的操作总是恒定的。您只是在表明您甚至不知道Big O是什么。它是用于(在上下文中)描述输入大小与执行的相关操作数之间关系的工具。O(1)表示无论输入多少,都会执行恒定数量的操作。在这里,不管输入什么,都不会执行恒定数量的操作,可能会执行无限的操作,无限!=常量。
–Servy

6

尚未提及我感到惊讶的一件事:big-O表示法是一个上限!

每个人都注意到的问题是,没有N描述该算法的输入,因此与big-O分析无关。但是,可以通过一些基本的技巧来轻松地缓解这种情况,例如接受int n并打印“ Hello World” n时间。这样可以解决该投诉,并回到真正的问题DateTime怪兽工作。

实际上并没有保证while循环将永远终止。我们喜欢认为它必须在某个时间,但是考虑到DateTime.now返回系统日期和时间。实际上,不能保证它会单调增加。可能是有些经过病理学训练的猴子不断更改系统日期和时间,直到UTC 2015年10月21日12:00:00 UTC,直到有人给猴子一些自动装配的鞋子和气垫板。该循环实际上可以运行无限长的时间!

当您实际研究big-O表示法的数学定义时,它们是上限。他们展示了最坏的情况,无论可能性如何。这里最坏的情况是无限的运行时,因此我们不得不声明没有big-O符号来描述该算法的运行时复杂性。 它不存在,就像1/0不存在一样。

*编辑:根据我与KT的讨论,假定我们正在使用big-O表示法建模的场景并不是最坏的情况,这并不总是正确的。在大多数情况下,如果某人未能指定我们使用的是哪种情况,则他们打算探索最坏的情况。但是,您可以在最佳情况下运行时进行big-O复杂性分析。


2
从某种意义上说,O实际上是一个“上限”,但这并不意味着您只能使用O表示法来说“最坏情况的复杂性”。预期的复杂度,最佳情况下的复杂度,任何其他功能属性-都可以根据其O界限讨论它们。
KT。

@KY最佳情况下的复杂度称为little-o,预期的复杂度为big-theta。按照其数学定义,big-o始终是最坏情况下的复杂性。
Cort Ammon 2015年

不,您在这里弄错了。重新检查定义。
KT。

@KT好的,我将重新检查它们。您也要重新检查它们。zh_cn.wikipedia.org/wiki/Big_O_notation其下属巴合曼–朗道符号
Cort Ammon 2015年

我想您可以像带一个函数f并将函数声明g为与一样疯狂f,但是在受限域中仅包含f最好的情况,然后再做大哦g,但是当您这样做时,它开始听起来退化那。
Cort Ammon 2015年

5

复杂度用于衡量时间/空间上的计算“马力”。大O符号用于比较哪些问题“可计算”或“不可计算”,还用于比较哪些解决方案-算法-比其他解决方案更好。这样,您可以将任何算法分为两类:可以在多项式时间内求解的算法和不能求解的算法。

像Erathostene筛之类的问题是O(n ^ exp),因此对于n的较小值可以解决。它们是可计算的,只是不是在多项式时间(NP)中,因此当询问给定数字是否为质数时,答案取决于该数字的大小。此外,复杂性并不取决于硬件,因此拥有更快的计算机不会改变任何事情。

“ Hello World”不是一种算法,因此试图确定其复杂性毫无意义-没有。一个简单的算法可以是这样的:给定一个随机数,确定它是偶数还是奇数。现在,给定数字有500位数字是否重要?不,因为您只需要检查最后一位数字是偶数还是奇数。一种更复杂的算法是确定给定数是否被3平均除。尽管有些数字“容易”计算,而另一些则“难”,这是因为其大小:比较确定两次之间的重新表达所需的时间一个数位的数字,另一个数位的500个数字。

更复杂的情况是解码文本。您有一个明显的随机符号数组,您也知道这些符号正在为具有解密密钥的对象传递消息。假设发件人使用了左侧的钥匙,您的Hello World将显示为:Gwkki Qieks。“大锤无脑”解决方案将为这些字母生成所有组合:从Aaaa到Zzzz,然后搜索单词词典以识别哪些单词有效,并共享密码中的两个共同字母(i,k)。相同的位置。这个转换功能是Big O所测量的!


4

大多数人似乎错过了两个非常重要的事情。

  1. 该程序确实有输入。它是比较系统时间的硬编码日期/时间。输入是在运行算法的人的控制下进行的,而系统时间则不受此限制。运行该程序的人唯一可以控制的是他们硬编码到比较中的日期/时间。

  2. 程序根据输入而不是输入的大小而变化,这是big-O表示法所关注的。

因此,它是不确定的,此程序的最佳“大O”表示法可能是O(null),或者可能是O(NaN)。


1
(2)完全错误。通常考虑“输入长度”。对于固定大小的对象(如整数)的列表或数组,确实是集合的大小。要分解像1395195191600333这样的数字,它将是其二进制(或十进制等)表示形式的长度,即位数。如前所述,您在(2)中的定义禁止使用big-O讨论“ findPrimeFactors(int num)”的复杂性,大多数密码学家都会反对这种复杂性。
djechlin 2015年

4

每个人都正确地指出您没有定义N,但是在最合理的解释下答案是否定的。如果N是我们要打印的字符串的长度,则为“ hello,world!”。只是一个例子,我们可以从对它的描述中推断出它是“ for hello, world!” 算法,因此该算法为O(N),因为您可能需要花费三十,四十或五十年的时间来打印输出字符串,而您只是增加了一个固定的时间。O(kN + c)∈O(N)。

附录:

令我惊讶的是,有人对此表示怀疑。回顾大O和大Θ的定义。假设我们有一个算法,它等待一个恒定的时间c,然后在线性时间内打印出长度为N的消息。(这是对原始代码示例的概括。)让我们随意地说,我们等待二十年才能开始打印,而打印一万亿个字符又需要二十年。例如,假设c = 20和k = 10 12,但是任何正实数都可以。每个字符的比率为d = c / k(在这种情况下为2×10 11)年,因此我们的执行时间fN)渐近 dN + c年份。每当N > k时dN = c / k N > c。因此,对于所有N > kdN < dN + c = fN)<2 dN,并且fN)∈Θ(N)。 优质教育


我们有N = 13
djechlin

但是,它不仅打印“ Hello world”,还打印未知数量的“仍然不是时间”行。此外,Big O并没有真正用于将输入的大小与输出的大小进行比较,它通常用于将输入的大小与操作数或所使用的内存量进行比较。
Servy 2015年

@Servy这是恒定的内存,但是我隐式地限制了执行时间。对于任意字符串,输出的大小也为O(N):时间到时我们打印的字符串可以任意大,即使与二十年的请等待消息相比也是如此。
戴维斯洛

@Servy我已经编辑过以澄清,不,N不是输出的大小。我不确定我给人的印象如何,但是我会消除任何歧义。
戴维斯洛

1
因此,如果您假设程序接受一个输入,那么当它没有输入时,该输出可以任意大;当它不能输入时,该循环什么也不做,何时执行,并且该输出与输入,如果不是,则为是,程序是线性的。当然,这些假设中的每个假设都是完全错误的,因此您从这些假设中得出的结论并不成立。如果您能够证明自己的观点而又不做错误的假设,那将意味着某些。
Servy 2015年

4

我认为人们被抛弃是因为代码看起来不像传统算法。这是代码的翻译,格式更合理,但符合OP问题的精神。

void TrolloWorld(long currentUnixTime, long loopsPerMs){
    long laterUnixTime = 2051222400000;  //unix time of 01/01/2035, 00:00:00
    long numLoops = (laterUnixTime-currentUnixTime)*loopsPerMs;

    for (long i=0; i<numLoops; i++){
        print ("It's still not time to print the hello …");
    }
    print("Hello, World!");
}

输入是显式的,而在输入之前是通过启动代码的时间以及运行代码的硬件的速度隐式给出的。该代码是确定性的,并具有给定输入的明确定义的输出。

由于我们可以提供的输入受到限制,因此将要执行的操作数量有一个上限,因此该算法实际上为O(1)。


2

在这个时间点,是的

该算法具有隐式输入,即程序开始的时间。执行时间将根据启动时间而线性变化1。在2035年及之后的时间里,while循环立即退出,并且程序在不断运行2之后终止。因此可以说运行时间是O(max(2035 - start year, 1))3。但是,由于我们的开始年份具有最小值,因此该算法的执行时间不会超过20年(即恒定值)。

您可以通过定义4使算法更符合您的意图DateTime TwentyYearsLater = DateTime.Now + new TimeSpan(365*20,0,0,0);

1这对于以操作数衡量的执行时间更具技术意义,因为每个时间单位有最大操作数。
2假设获取DateTime.Now是一个恒定的操作,这是合理的。
3我在这里有点滥用大的O表示法,因为这是相对而言的递减函数start year,但是我们可以通过用表示来轻松地纠正它years prior to 2035
4然后,算法不再依赖于开始时间的隐式输入,但这无关紧要。


1

我认为这是O(n)。使用http://www.cforcoding.com/2009/07/plain-english-explanation-of-big-o.html作为参考。

什么是大O?

Big O符号试图通过在关键因素趋于无穷大时将增长率降低到关键因素来描述算法的相对复杂性。

我能想到的Big-O最好的例子是算术。我们在学校学到的基本算术运算是:

加成; 减法 乘法; 和分裂。这些都是操作或问题。解决这些问题的方法称为算法。

例如,

给定输入n = 20(以年为单位)。

该算法是一个数学函数f()。其中f()恰好等待了n年,中间有'debug'字符串。比例因子为1。可以通过更改此比例因子来减小/或增加f()。

对于这种情况,输出也为20(更改输入会线性更改输出)。

本质上功能是

f(n) = n*1 = n
    if  n = 20, then 
f(20) = 20 
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.