Skip to content
Marlbridge

AP® Computer Science A · Unit 2 · Topic 2.12

Informal Run-Time Analysis: Study Guide (Computer Science A 2.12)

Learn statement execution counts and informal run-time comparison, with a first-principles explanation, traced Java examples, a diagram, two worked examples and the mistakes to avoid.

Counting work, not seconds

How long does a program take? The honest answer depends on the machine, so computer scientists first ask a machine-independent question: how many statements are executed? A statement execution count indicates the number of times a statement is executed by the program. Statement execution counts are often calculated informally, through tracing and analysis of the iterative statements, and then used to compare how much work two pieces of code do.

You already traced loops in Topics 2.7 to 2.11. This topic turns tracing into a habit of counting.

Counting a single loop

For a for loop whose body runs n times, every part of the loop has a count.

Part of for (int i = 0; i < n; i++) How many times
Initialization int i = 0 1
Boolean expression i < n n + 1 (n true results, then one false)
Body n
Update i++ n

You can check these claims by instrumenting a loop with counters. The helper methods below only measure; the static variables are explained properly in Topic 3.7.

public class HeaderCounts {
    static int tests = 0;
    static int updates = 0;

    static boolean less(int a, int b) {
        tests++;
        return a < b;
    }

    static int next(int i) {
        updates++;
        return i + 1;
    }

    public static void main(String[] args) {
        int body = 0;
        for (int i = 0; less(i, 5); i = next(i)) {
            body++;
        }
        System.out.println("tests " + tests + ", body " + body + ", updates " + updates);
    }
}

Output:

tests 6, body 5, updates 5

With n = 5: the test is evaluated 6 times, the body runs 5 times, and the update runs 5 times. When people ask “how many times does this statement execute?” they usually mean the body statement, which is the iteration count.

Different steps

The count depends on how the variable moves.

Loop Values of i Body count
for (int i = 0; i < n; i++) 0 to n - 1 n
for (int i = 1; i <= n; i++) 1 to n n
for (int i = 0; i < n; i += 2) 0, 2, 4, … about n / 2 (exactly n / 2 rounded up)
for (int i = 1; i < n; i *= 2) 1, 2, 4, 8, … about log2 of n, much smaller

The last row is a loop that doubles its variable. For n = 16 the values are 1, 2, 4, 8, so 4 iterations; for n = 64 the values are 1, 2, 4, 8, 16, 32, so 6 iterations. A loop that halves works the same way in the other direction.

int n = 1000;
int steps = 0;
while (n > 1) {
    n = n / 2;
    steps++;
}
System.out.println(steps);

Output:

9

The values are 1000, 500, 250, 125, 62, 31, 15, 7, 3, 1, which gives 9 divisions. Even for a starting value of a million, a halving loop runs only about 20 times, so these loops stay small as n grows.

Counting nested loops

Use the rules from Topic 2.11: multiply when the inner count does not depend on the outer variable, and add row by row when it does.

Statement execution counts as n grows A line chart with n from 0 to 10 on the horizontal axis and number of executions from 0 to 100 on the vertical axis. The straight line for a single loop reaches 10 at n equals 10. The curve for a half-triangle nested loop, n times n minus 1 over 2, reaches 45. The steepest curve for a full nested loop, n squared, reaches 100. The lines use different dash patterns and are labelled directly at their right ends. 0 2 4 6 8 10 0 25 50 75 100 n (size of the loop bounds) number of executions n * n n(n-1)/2 n
Figure 1. How many times a statement runs as n grows, for a single loop (n), a triangular nested loop (n(n-1)/2) and a full nested loop (n x n). The gaps widen quickly.
Code shape (n outer passes) Innermost statement runs n = 5 n = 10
Single loop n 5 10
Inner loop independent of i (n passes) n x n 25 100
Inner loop j < i 0 + 1 + … + (n - 1) = n(n - 1) / 2 10 45
Inner loop with fixed 3 passes 3n 15 30

When n doubles from 5 to 10, the single loop does twice the work, the fixed-inner loop does twice the work, the triangle does 4.5 times the work, and the full nested loop does 4 times the work.

Early exits: it depends on the data

Some loops can stop before they have visited everything, for example a return inside a loop, or a condition that becomes false early. Their count then depends on the data as well as on n.

  • Best case: the least work, such as finding what you want straight away.
  • Worst case: the most work, such as finding nothing and checking every candidate.

A prime test is a good example. The methods in the next section return as soon as they find a divisor, so a number with a small divisor is decided at once, whereas a prime must be tested against every candidate.

Comparing two algorithms

Informal run-time comparison asks: for the same task, which code executes fewer statements, and how does the gap change when n grows? Take two ways to test whether n is prime. Method A tries every d from 2 to n - 1. Method B stops when d * d exceeds n, because if n has a divisor larger than its square root, it also has a smaller one. Each method here returns the number of divisibility tests it made.

public class PrimeWork {
    public static int testsA(int n) {
        int tests = 0;
        for (int d = 2; d < n; d++) {
            tests++;
            if (n % d == 0) {
                return tests;
            }
        }
        return tests;
    }

    public static int testsB(int n) {
        int tests = 0;
        for (int d = 2; d * d <= n; d++) {
            tests++;
            if (n % d == 0) {
                return tests;
            }
        }
        return tests;
    }

    public static void main(String[] args) {
        System.out.println(testsA(97) + " " + testsB(97));
        System.out.println(testsA(1009) + " " + testsB(1009));
        System.out.println(testsA(100) + " " + testsB(100));
    }
}

Output:

95 8
1007 30
1 1

For the prime 97, Method A makes 95 tests, and Method B makes 8. For the prime 1009, A makes 1007 and B makes 30: B’s count grows far more slowly. For 100, both stop at the first test because 2 divides 100; the data decided the count, and the two methods are equal in this best case. Which method is better is a statement about how the counts change as n grows and about the worst case, not about one particular input.

Worked example 1: counting two statements in a nested loop

Question. For n = 6, how many times do a++ and b++ execute? Give the general formulas, then check them for n = 10.

public class TwoCounts {
    public static void main(String[] args) {
        int n = 6;
        int a = 0;
        int b = 0;
        for (int i = 1; i <= n; i++) {
            a++;
            for (int j = 1; j <= i; j++) {
                b++;
            }
        }
        System.out.println(a + " " + b);
    }
}

Output:

6 21

Solution.

  • a++ is in the outer loop’s body only, so it runs once per outer pass: n = 6 times.
  • b++ is in the inner loop. The inner loop runs i times on outer pass i, so the counts are 1, 2, 3, 4, 5, 6, which sum to 21. In general, 1 + 2 + … + n = n(n + 1) / 2, which for n = 6 is 6 x 7 / 2 = 21.
i 1 2 3 4 5 6 total
a++ runs 1 1 1 1 1 1 6
b++ runs 1 2 3 4 5 6 21

Check for n = 10. a++ runs 10 times and b++ runs 10 x 11 / 2 = 55 times. Doubling n from 5 to 10 changes b++ from 15 to 55, which is a factor of about 3.7, approaching 4 as n grows.

Worked example 2: which loop does more work?

Question. Three code segments each use n = 100. How many times does count++ run in each? Which does the most work?

public class Compare {
    public static void main(String[] args) {
        int n = 100;
        int c1 = 0;
        for (int i = 0; i < n; i += 5) {
            c1++;
        }
        int c2 = 0;
        for (int i = 1; i < n; i *= 2) {
            c2++;
        }
        int c3 = 0;
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 10; j++) {
                c3++;
            }
        }
        System.out.println(c1 + " " + c2 + " " + c3);
    }
}

Output:

20 7 100

Solution.

  1. i takes the values 0, 5, 10, …, 95: that is 100 / 5 = 20 iterations.
  2. i takes the values 1, 2, 4, 8, 16, 32, 64, and the next value, 128, is not less than 100: 7 iterations.
  3. The nested loops are independent, so 10 x 10 = 100 iterations.

The third segment does the most work, even though its loop bounds (10) look small, because the two loops multiply. The second does the least, because its variable doubles.

Check. If n were doubled to 200, segment 1 would run 40 times (twice as many), segment 2 would run 8 times (one more), and the nested segment would still be 100, since its bounds are fixed numbers. Counts respond to n only through the bounds that use n.

Common misconceptions

  • “A loop condition is tested as many times as the body runs.” It is tested one more time.
  • “Fewer lines of code means a faster program.” Speed in this sense depends on how often statements execute, not on how many lines are written. A short nested loop can do far more work than a long, straight sequence.
  • “Nested loops always give n x n.” Only if both bounds depend on n and the inner bound does not depend on the outer variable.
  • “Doubling n always doubles the work.” Only for a single loop. A nested loop does about four times as much; a halving loop does just one more pass.
  • “Run-time analysis means timing the program.” Here you count statement executions, which does not depend on the machine.
  • “The count is fixed by n.” A loop with an early exit also depends on the data.
  • “A bigger count is always worse.” The comparison is about the work needed for the same task; a loop that does more work to get a different result is not comparable.

Where this leads

Counting executions is how you will compare searching and sorting algorithms in Topics 4.14 and 4.15 and recursion in Topic 4.16. In the multiple-choice section it is a standard question type: “how many times is this statement executed?”. Try the practice questions, recap with the revision notes and tick off the checklist. Then take the Unit 2 diagnostic and finish with the Unit 2 review. For nested loop counting, revisit the Topic 2.11 study guide.

Quick questions

What is a statement execution count?

The number of times a particular statement is executed when the code runs. For a statement in a loop body it is the number of iterations; for a statement in a nested loop it is the number of iterations of the inner loop summed over the outer loop.

How many times is the loop test evaluated?

One more time than the body runs, because the final test is the false one. A for loop whose body runs n times evaluates its Boolean expression n + 1 times.

Do I need Big-O notation?

No. This topic is about counting how many times statements execute and comparing counts informally, such as 'twice as many when n doubles' or 'four times as many'. You do not need to name the growth rates.

Why can two loops with the same output take different amounts of work?

They may execute different numbers of statements. A search that stops as soon as it finds an answer does less work than one that always checks everything, and a loop that tests fewer candidates does less work than one that tests more.

AP® and Advanced Placement® are trademarks registered by the College Board, which is not affiliated with, and does not endorse, this website. Marlbridge is an independent tuition provider. It is not an authorized AP course provider, an AP school or an exam centre, and these resources are not official College Board materials. Reading them or doing simulations does not meet the laboratory requirement of an AP science course.