英文:
Big O Notation O(n^2) what does it mean?
问题
例如,它说在1秒内使用选择排序可以排序3000个数字。我们如何预测在10秒内将排序多少个数字?
我查了一下,选择排序需要O(n^2),但我不明白如何计算在10秒内将排序多少个数字。
英文:
For example, it says that in 1 sec 3000 number are sorted with selection sort. How can we predict how many numbers are gonna be sorted in 10 sec ?
I checked that selection sort needs O(n^2) but I dont understand how I am gonna calculate how many numbers are gonna be sorted in 10 sec.
答案1
得分: 4
我们不能使用大O来可靠地推断实际运行时间或输入大小(无论哪个是未知的)。
想象一下相同的代码在两台不同的机器A和B上运行,它们有不同的解析器、编译器、硬件、操作系统、数组实现等等。
假设它们都可以解析和运行以下代码:
procedure sort(reference A)
declare i, j, x
i ← 1
n ← length(A)
while i < n
x ← A[i]
j ← i - 1
while j >= 0 and A[j] > x
A[j+1] ← A[j]
j ← j - 1
end while
A[j+1] ← x[3]
i ← i + 1
end while
end procedure
现在,系统A在循环开始之前花费0.40秒在初始化部分上,不管A是什么,因为在该配置上,函数执行上下文的初始化,包括变量的分配,是一个非常昂贵的操作。当它到达过程的末尾时,它还需要花费0.40秒来释放已声明的变量和调用堆栈帧,因为在该配置上,内存管理是非常昂贵的。此外,length
函数也很昂贵,需要0.19秒。这总共是0.99秒的开销。
在系统B上,内存分配和释放廉价,只需1微秒。此外,length
函数速度快,只需1微秒。总开销为2微秒。
然而,与系统B相比,系统A在其他语句上速度要快得多。
这两种实现恰好需要1秒来对一个包含3000个值的数组A进行排序。
如果我们现在根据1秒的结果来预测可以在10秒内排序的数组大小,我们会说:
𝑛 = 3000,持续时间为1秒,对应于𝑛^2 = 9,000,000次操作。因此,如果9,000,000次操作对应于1秒,那么9,000,000次操作对应于10秒,𝑛 = √(𝑛^2) ≈ 9,487(可以在10秒内排序的数组大小)。
然而,如果我们按照相同的推理,仅考虑完成外部循环所需的时间(不包括初始化开销),这也是O(𝑛^2),因此可以进行相同的推理:
𝑛 = 3000,在系统A中持续时间为0.01秒,对应于𝑛^2 = 9,000,000次操作。因此,如果9,000,000次操作可以在0.01秒内执行,那么在10 - 0.99秒内(减去了开销)我们可以执行9.01 / 0.01次操作,即 𝑛^2 = 8,109,000,000次操作,现在 𝑛 = √(𝑛^2) ≈ 90,040。
问题是,使用大O相同的推理,预测的结果相差约10倍!
我们可能会认为这只是恒定开销的“问题”,但外部循环中的操作也可以类似地说明。例如,可能有一些原因导致在某些系统上“x ← A[i]”具有相对较高的成本。这些因素在大O标记中没有显示,它只保留了最重要的因素,省略了起作用的线性和恒定因素。
实际的运行时间取决于一个更复杂的函数,可能接近多项式,如 𝑛^2 + 𝜆𝑛 + 𝜇。这些系数 𝜆 和 𝜇 将需要使更合理的预测成为可能。甚至可能存在非多项式的函数组件,如 𝑛^2 + 𝜆𝑛 + 𝜇 + 𝜈√𝑛... 这可能看起来不太可能,但代码运行的系统可能在代码运行时进行各种优化,这可能对实际运行时间产生这种或类似的影响。
结论是,这种类型的推理不能保证预测与现实接近 - 如果没有关于实际代码、运行它的系统等的更多信息,它只是一个猜测。大O是一种用于渐进行为的度量。
英文:
We cannot use big O to reliably extrapolate actual running times or input sizes (whichever is the unknown).
Imagine the same code running on two machines A and B, different parsers, compilers, hardware, operating system, array implementation, ...etc.
Let's say they both can parse and run the following code:
procedure sort(reference A)
declare i, j, x
i ← 1
n ← length(A)
while i < n
x ← A[i]
j ← i - 1
while j >= 0 and A[j] > x
A[j+1] ← A[j]
j ← j - 1
end while
A[j+1] ← x[3]
i ← i + 1
end while
end procedure
Now system A spends 0.40 seconds on the initialisation part before the loop starts, independent on what A is, because on that configuration the initiation of the function's execution context including the allocation of the variables is a very, very expensive operation. It also needs to spend 0.40 seconds on the de-allocation of the declared variables and the call stack frame when it arrives at the end of the procedure, again because on that configuration the memory management is very expensive. Furthermore, the length
function is costly as well, and takes 0.19 seconds. That's a total overhead of 0.99 seconds
On system B this memory allocation and de-allocation is cheap and takes 1 microsecond. Also the length
function is fast and needs 1 microsecond. That's a total overhead that is 2 microseconds.
System A is however much faster on the rest of the statements in comparison with system B.
Both implementations happen to need 1 second to sort an array A having 3000 values.
If we now take the reasoning that we could predict the array size that can be sorted in 10 seconds based on the results for 1 second, we would say:
> 𝑛 = 3000, and the duration is 1 second which corresponds to 𝑛² = 9 000 000 operations. So if 9 000 000 operations corresponds to 1 second, then 90 000 000 operations correspond to 10 seconds, and 𝑛 = √(𝑛²) ~= 9 487 (the size of the array that can be sorted in 10 seconds).
However, if we follow the same reasoning, we can look at the time needed for completing the outer loop only (without the initialisation overhead), which also is O(𝑛²) and thus the same reasoning can be followed:
> 𝑛 = 3000, and the duration in system A is 0.01 second which corresponds to 𝑛² = 9 000 000 operations. So if 9 000 000 operations can be executed in 0.01 second then in 10 - 0.99 seconds (overhead is subtracted) we can execute 9.01 / 0.01 operations, i.e 𝑛² = 8 109 000 000 operations, and now 𝑛 = √(𝑛²) ~= 90 040.
The problem is that using the same reasoning on big O, the predicted outcomes differ by a factor of about 10!
We may be tempted to think that this is now only a "problem" of constant overhead, but similar things can be said about operations in the outer loop. For instance it might be that x ← A[i]
has a relatively high cost for some reason on some system. These are factors that are not revealed in the big O notation, which only retains the most significant factor, omitting linear and constant factors that play a role.
The actual running time for an actual input size is dependent on a more complex function that is likely close to polynomial, like 𝑛² + 𝑎𝑛 + 𝑏. These coefficients 𝑎, and 𝑏 would be needed to make a more reasonable prediction possible. There might even be function components that are non-polynomial, like 𝑛² + 𝑎𝑛 + 𝑏 + 𝑐√𝑛... This may seem unlikely, but systems on which the code runs may do all kinds of optimisations while code runs which may have such or similar effect on actual running time.
The conclusion is that this type of reasoning gives no guarantee that the prediction is anywhere near the reality -- without more information about the actual code, system on which it runs,... etc, it is nothing more than a guess. Big O is a measure for asymptotic behaviour.
答案2
得分: 0
根据评论所述,大O符号与具体的时间测量无关;然而,这个问题仍然有意义,因为大O符号在时间计算中完全可以用作相对因素。
大O符号告诉我们,算法执行的基本操作数量如何随要处理的项目数量的变化而变化。
简单的算法每项执行固定数量的操作,但在更复杂的算法中,需要执行的操作数量随项目数量的变化而变化。排序算法是这种复杂算法的典型示例。
大O符号的好处在于它属于科学领域,而不是技术领域,因为它完全独立于您的硬件以及您的硬件执行单个操作的速度。
然而,这个问题告诉我们某个假设的硬件处理一定数量的项目需要多长时间,因此我们知道这个硬件执行单个操作所需的时间,因此我们可以根据这个推理。
如果在1秒内对3000个数字进行排序,而算法的操作时间复杂度为O(N^2)
,这意味着算法在那一秒内执行了3000^2 = 9,000,000次操作。
如果给定10秒的时间,算法将在那段时间内执行这么多的操作,即90,000,000次操作。
由于算法的时间复杂度为O(N^2)
,这意味着在执行了90,000,000次操作之后,它将排序Sqrt(90,000,000)
= 9,486个数字。
为了验证:在1秒内执行9,000,000次操作意味着每次操作需要1.11e-7秒。由于算法的时间复杂度为O(N^2)
,这意味着处理9,486个数字需要9,486^2次操作,大约等于90,000,000次操作。以1.11e-7秒每次操作,90,000,000次操作将在大约10秒内完成,因此我们通过不同的途径得到了相同的结果。
如果您真正追求计算机科学或编程,我建议您了解大O符号,因为它非常重要,并且是一个非常庞大的主题,无法在StackOverflow的问题和答案中完全覆盖。
英文:
As the comments say, big-oh notation has nothing to do with specific time measurements; however, the question still makes sense, because the big-oh notation is perfectly usable as a relative factor in time calculations.
Big-oh notation gives us an indication of how the number of elementary operations performed by an algorithm varies as the number of items to process varies.
Simple algorithms perform a fixed number of operations per item, but in more complicated algorithms the number of operations that need to be performed per item varies as the number of items varies. Sorting algorithms are a typical example of such complicated algorithms.
The great thing about big-oh notation is that it belongs to the realm of science, rather than technology, because it is completely independent of your hardware, and of the speed at which your hardware is capable of performing a single operation.
However, the question tells us exactly how much time it took for some hypothetical hardware to process a certain number of items, so we have an idea of how much time that hardware takes to perform a single operation, so we can reason based on this.
If 3000 numbers are sorted in 1 second, and the algorithm operates with O( N ^ 2 )
, this means that the algorithm performed 3000 ^ 2 = 9,000,000 operations within that second.
If given 10 seconds to work, the algorithm will perform ten times that many operations within that time, which is 90,000,000 operations.
Since the algorithm works in O( N ^ 2 )
time, this means that after 90,000,000 operations it will have sorted Sqrt( 90,000,000 )
= 9,486 numbers.
To verify: 9,000,000 operations within a second means 1.11e-7 seconds per operation. Since the algorithm works at O( N ^ 2 )
, this means that to process 9,486 numbers it will require 9,486 ^ 2 operations, which is roughly equal to 90,000,000 operations. At 1.11e-7 seconds per operation, 90,000,000 operations will be done in roughly 10 seconds, so we are arriving at the same result via a different avenue.
If you are seriously pursuing computer science or programming I would recommend reading up on big-oh notation, because it is a) very important and b) a very big subject which cannot be covered in stackoverflow questions and answers.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论