复杂性与运行时实际增长不匹配?

huangapple go评论77阅读模式
英文:

Complexity does not match the actual growth of the runtime?

问题

我已经做了一段时间的家庭作业,但在我认为的渐近复杂度和运行时间结果之间存在着巨大的差异。

下面是程序运行时间的表格。

|      输入规模     |     运行时间(秒)   |
|---------------------|-------------------------|
|       10000         |      0.040533803        |
|---------------------|-------------------------|
|       20000         |      0.154712122        |
|---------------------|-------------------------|
|        30000        |      0.330814060        |    
|---------------------|-------------------------|
|        40000        |      0.603440983        |    
|---------------------|-------------------------|
|        50000        |      0.969272780        |    
|---------------------|-------------------------|
|        60000        |      1.448454467        |

复杂性与运行时实际增长不匹配?

string = "";
newLetter = "a";
for (int i = 500; i < n; i++) {
    string = string + newLetter;
}
return string

除了我可能错了之外,为什么算法的复杂度和运行时间增长之间会存在差异呢?

从运行时间的结果来看,该程序的时间复杂度似乎是 O(n^2)。将输入规模加倍似乎会使运行时间增加4倍,这会暗示二次函数?

但是从程序本身来看,我有99.9%的把握时间复杂度实际上是 O(n)。

除了我认为的错误之外,是否有其他原因导致了这种差异呢?有没有可能运行时间的结果实际上是线性的?

我对这种差异的最佳猜测是,for循环使得下一次迭代变慢(从程序的角度来看,我认为Java编译器必须迭代每个额外的newLetter),但这仍然是线性的,对吗?这并不是嵌套的for循环。

英文:

I've been doing a homework sheet for a while now, and there is a massive discrepancy between what I think the asymptomatic complexity is and what the runtime result suggests.

Below is a table for the runtime for the program.

|      Input Size     |     Runtime (Seconds)   |
|---------------------|-------------------------|
|       10000         |      0.040533803        |
|---------------------|-------------------------|
|       20000         |      0.154712122        |
|---------------------|-------------------------|
|        30000        |      0.330814060        |    
|---------------------|-------------------------|
|        40000        |      0.603440983        |    
|---------------------|-------------------------|
|        50000        |      0.969272780        |    
|---------------------|-------------------------|
|        60000        |      1.448454467        |

复杂性与运行时实际增长不匹配?

string = &quot;&quot;;
newLetter = &quot;a&quot;;
for (int i = 500; i &lt; n; i++) {
    string = string + newLetter;
}
return string

Why would there be a discrepancy between the complexity of the algorithm and the growth of the runtime apart from me being wrong?

From the runtime results, it looks like the program has a time complexity of O(n<sup>2</sup>). Doubling the input size seems to increase the runtime by a factor of 4, which would suggest a quadratic function?

But looking at the program itself, I'm 99.9% certain that the time complexity is actually O(n).

Is it possible that there's an extraneous reason for this discrepancy? Is there any possibility that the runtime results are indeed linear?

My best guess for this discrepancy is that the for loop makes the next iteration slower (which, looking at the program makes sense as I think the Java compiler would have to iterate every additional newLetter given) but that would still be linear no? It's not a nested for loop.

答案1

得分: 3

你编写的代码实际上在时间复杂度上是可以运行的,具体来说是 Θ(n2)。原因与代码的这部分有关:

string = string + newLetter;

在这里,你的意图是想说“请将一个新字符追加到这个字符串的末尾。”然而,Java 实际上执行的操作是:

  1. 计算表达式 string + newLetter。这将创建一个全新的字符串,其中包含了将 string 的全部内容复制过来,然后将 newLetter 追加到这个新字符串的末尾。
  2. 将结果赋值给 string

问题在于,步骤(1)的执行时间随着字符串的长度增加而逐渐变长,因为所有字符都必须被复制过去。特别地,如果 string 的长度为 k,那么步骤(1)需要时间 Θ(k),因为需要复制 k 个字符。由于 string 的长度与当前循环迭代索引匹配,这意味着所需的工作量为

> Θ(1 + 2 + 3 + ... + n)
>
> = Θ(n(n+1) / 2)
>
> = Θ(n2),

这就是你所看到的图形的原因。

你可以通过使用 StringBuilder 而不是 String 来加快速度,以组装更大的字符串。它在进行操作时不会创建所有中间副本,而是维护一个可以根据需要增长的内部数组。

英文:

The code you've written actually does run in time &Theta;(n<sup>2</sup>). The reason has to do with this part of the code:

string = string + newLetter;

Here, your intent is to say "please append a new character to the end of this string." However, what Java actually does is the following:

  1. Evaluate the expression string + newLetter. This makes a brand-new string formed by copying the full contents of string, then appending newLetter to the end of that new string.
  2. Assign the result to string.

The issue here is that step (1) takes longer and longer to execute the longer the string is, since all the characters have to be copied over. In particular, if string has length k, then step (1) takes time &Theta;(k) because k characters must be copied. Since the length of string matches the current loop iteration index, this means the work done is

> &Theta;(1 + 2 + 3 + ... + n)
>
> = &Theta;(n(n+1) / 2)
>
> = &Theta;(n<sup>2</sup>),

which is why you're seeing the plot you're seeing.

You can speed this up by using StringBuilder rather than String to assemble the larger string. It doesn't make all the intermediary copies as it goes, and instead maintains an internal array that can grow as needed.

huangapple
  • 本文由 发表于 2020年10月14日 00:47:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/64339629.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定