Iterative Loops: Loop Runtime Analysis

    Iterative Loops: Loop Runtime Analysis

      Being scary is easy. Just grab a pair of vampire fangs, some fake blood, a swatch or two of black fur, a couple of skulls, and the current statistics on average student debt, and you're basically ready to film a horror movie.

      Loops might not have as much weight as crippling educational debt, but they can be pretty scary too. Welcome to Iteration's House of Horrors. Let's see what's inside.

      WHILE true:
      	PRINT "Redrum"
      END WHILE

      Never, ever, ever make a loop like this unless you have a ton of breaking conditions. If you don't, the loop's never going to end. Ever.

      Well…technically it will end, but it'll crash the environment it's running in to end. Not so good.

      Here's another looping horror.

      FOR counter FROM 0 TO x:
      	FOR counter2 FROM 0 TO y:
      		FOR counter3 FROM 0 TO x:
      			FOR counter4 from 0 TO y:
      				PRINT "Pennywise"
      			END FOR
      		END FOR
      	END FOR
      END FOR

      Jeepers creepers. A quadruply-nested for loop is going to take forever to finish...if it even gets the chance. It might take too long and stop your (computer's) heart cold.

      Nested for loops and while loops with the condition set to true might not sound so bad in theory, but when it comes to actual computers, the processing time and power they'd need becomes terrifying. If it takes too many of your computer's resources, that loop needs to be reworked to…not take up so many resources.

      Fixing Infinite While Loops

      In the case of the while loop, unless you make the loop break or terminate the entire program another way, it'll run forever until something crashes. That being said, there are times when you might legitimately want your loop to run for a very long time. When those happen, you've got two options. First option: code the end condition in the loop's condition.

      x = 1
      WHILE x < 100:
      	PRINT "Redrum"
      	x = x + 1
      END WHILE

      Option two: include at least one break condition to exit the loop (and also make sure that break condition's reachable).

      x = 1
      WHILE true:
      	PRINT "Redrum"
      	IF x > 100:
      		BREAK loop
      	END IF
      	x = x + 1
      END WHILE

      Now the while loop should run just fine without crashing a computer. The for loops take a little more finessing, though.

      Fixing Nested For Loops

      Every instruction that a computer runs takes some amount of time. Each individual instruction takes microseconds at this point; it's seriously tiny. Talking about run time in terms of microseconds isn't very helpful, so instead we base it on the size of the input. When we talk about loops that run through a list of an arbitrary n elements (the size of the input), the time it takes to run however many lines of code can all of the sudden make a huge difference.

      A singly-nested for loop from 0 to n will only take as long to run as it takes to run the condition and the code block n times. When you add more layers to the loop, this amount of time, usually referred to as time complexity (the time spent running your program), gets multiplied by each new layer, since each new outer loop iteration means another complete run-through of the inner loop. Let's look at the example again.

      FOR counter FROM 0 TO x:
      	FOR counter2 FROM 0 TO y:
      		FOR counter3 FROM 0 TO x:
      			FOR counter4 from 0 TO y:
      				PRINT "Pennywise"
      			END FOR
      		END FOR
      	END FOR
      END FOR

      The innermost loop's going to run y times. The inner two loops of the nested for loop from above would run y × x times. Multiply that by the loop outside those two and you've got a runtime of y2 × x. Once more with the outermost loop and the final runtime's going to be y2 × x2. If x and y are the same number, say…1,000, the runtime's going to be 1,0004.

      That's at least a trillion lines of code. Yikes.

      Runtime and Big O Notation

      In computer science, runtime is usually analyzed in terms of Big O notation. Big O notation has to do with the most amount of time a program can take to run given an input size n. If a program runs the same number of lines of code no matter the size of the input (like Hello, World!) then it has a runtime of O(1), or "Big Oh of 1." On the other hand, the time it takes to run a for loop is directly dependent on the input size.

      If a for loop goes from x to n—no matter what integers x and n are—the loop's going to have a runtime of O(n), or "Big Oh of n," even if x isn't 0. In general, a single layer loop is going to have about this runtime no matter the value of x. That's because, as n gets bigger, the fact that x is keeping the loop from running exactly n times is going to make less of a difference. If x is 2 and n is 1,000, the runtime's going to be about 1,000. Computer scientists won't mind if the actual number's 998 iterations.

      With two nested loops, if both loops go from x to n, then the runtime becomes O(n2). With three nested loops ranging from x to n, the runtime will end up as O(n3). That pattern of adding one to the exponent is going to continue for as many nested loops as you have.

      This exponential scaling is exactly what makes nested loops dangerous. Let's say your computer could run 20 billion instructions per second (20,000 Mips, or 20,000 million instructions per second, which is comparable to an Intel i5 processor). It would take a 200th of a second for the nested loop to run. That might sound really flipping fast and…it is, but keep in mind that a loop through 100 items is actually pretty small for a program.

      What about with the more realistic loops around the of size 1000? That would take a 50th of a second. If the value of n gets any larger, we're going to get some serious lag time on this program. Imagine throwing this loop on a list of all the students at a public university like Ohio State or the University of Florida. If all the loops went all the way up to a billion, then the runtime would equal 1036. Even at 20 billion instructions per second, this program would take 5 × 1025 seconds to run.

      If a billion seconds is about 30 years and we have nearly a billion billion seconds. All in all, this program would take about 1,500 trillion years. And that's if your computer didn't do anything else. Without House of Cards, Fallout: New Vegas, or PewDiePie running in the background, this program's still going to take trillions of years.

      Be careful with your nested loops, Shmooper. Or cancel your plans for the next couple trillion years. It's your call.

      (Source 1, Source 2)