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 = "";
newLetter = "a";
for (int i = 500; i < 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(n2). 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.
The code you've written actually does run in time Θ(n2). 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:
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.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 Θ(k) because k characters must be copied. Since the length of string
matches the current loop iteration index, this means the work done is
Θ(1 + 2 + 3 + ... + n)
= Θ(n(n+1) / 2)
= Θ(n2),
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.