Introduction to Informal Run-Time Analysis

In the world of programming, there is usually more than one way to solve a problem. But how do we decide which way is "better"? While a program might produce the right answer, one version might be much faster than another. Informal Run-Time Analysis is the process of estimating how much "work" a computer does to run a piece of code.

In this chapter, we aren't using fancy math or complex formulas. Instead, we are going to act like "code accountants" by counting statement executions to see which algorithms are more efficient. Don't worry if this seems tricky at first; once you learn to spot the patterns in loops, it becomes a very straightforward skill!


The Core Metric: Counting Executions

On the AP Computer Science A exam, you are expected to compare algorithms informally. This means you will determine which code segment is faster by looking at how many times the statements inside a loop actually run.

Important Note: You do not need to use formal "Big-O" notation (like \(O(n)\) or \(O(n^2)\)) for this course. You simply need to be able to say, "Algorithm A executes its loop body \(n\) times, while Algorithm B executes it \(2n\) times."

1. Simple Loops (Linear Growth)

When we look at a single loop, the number of executions is usually tied directly to the limit of the loop variable.

Consider this loop:
for (int i = 0; i < n; i++) {
    System.out.println(i);
}

In this case, if \(n = 10\), the print statement executes \(10\) times. If \(n = 100\), it executes \(100\) times. We say the number of executions is proportional to \(n\).

2. Changing the Step Size

The "work" changes if we change how the loop variable increases. Look at this example:
for (int i = 0; i < n; i += 2) {
    // some code
}

Because we are jumping by \(2\) each time, the loop body only executes approximately \(\frac{n}{2}\) times. This is "less work" than the first example!

Quick Tip: Always check the update statement (the i++ or i += 2 part). It tells you how fast the loop is "consuming" the distance to the finish line.


Analyzing Nested Iteration

Things get interesting when we put one loop inside another. This is called Nested Iteration (which you learned about in Topic 2.11). When loops are nested, the total number of executions is the product of the number of times the outer loop runs and the number of times the inner loop runs.

The "Multiplication" Rule

Imagine you are packing \(5\) boxes, and inside each box, you put \(3\) toys. You have handled a total of \(5 \times 3 = 15\) toys. Nested loops work the same way.

for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
        // execution happens here
    }
}

In the code above, the inner statement executes \(n \times m\) times. If both loops go up to \(n\), the total work is \(n \times n\), or \(n^{2}\).

Example: Comparing Efficiencies
  • Algorithm A: Two separate loops, one after the other.
    Total executions: \(n + n = 2n\).
  • Algorithm B: Two nested loops.
    Total executions: \(n \times n = n^{2}\).

If \(n = 100\), Algorithm A does \(200\) units of work. Algorithm B does \(10,000\) units of work! Clearly, Algorithm A is more efficient for large values of \(n\).


Common Patterns to Recognize

To succeed in informal run-time analysis, keep an eye out for these common scenarios:

  1. Constant Time: If a code segment has no loops, it does the same amount of work regardless of how big the input is. We call this "constant."
  2. Logarithmic Style (Halving): If a loop variable is divided by \(2\) each time (e.g., for (int i = n; i > 0; i /= 2)), the loop runs very few times. This is much faster than a loop that subtracts \(1\).
  3. Inner Loop Dependent on Outer Loop: Sometimes the inner loop starts at the outer loop's index:
    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) { ... }
    }

    This runs roughly \(\frac{n^{2}}{2}\) times. While it is less work than a full \(n^{2}\) loop, it still grows much faster than a single loop.

Common Pitfalls & Mistakes

1. The "Off-by-One" Error: Be careful with < vs <=. While it usually doesn't change the informal comparison significantly, it can lead to an incorrect count of exact executions.

2. Ignoring the Update: Students often assume every loop runs \(n\) times. Always look at the i++ part. If it says i = i * 2, the loop is actually much faster!

3. Miscounting Nested Loops: Don't add the loop counts; multiply them. A loop of \(10\) inside a loop of \(10\) is \(100\), not \(20\).


Key Takeaways

Summary Checklist:

  • Analyze code by counting how many times the innermost statement executes.
  • Single loops are generally proportional to the number of iterations (\(n\)).
  • Nested loops require multiplication of the iterations (\(n \times m\)).
  • Algorithms that "halve" the work (like dividing a variable by \(2\)) are more efficient than those that "step" through one by one.
  • Informal analysis is about comparing which code does more work as the input size (\(n\)) gets larger.

Remember: You don't need a calculator or complex math for this! Just trace a few steps with a small number (like \(n = 4\)) to see the pattern, then apply that logic to larger numbers.