12A. Scan Algorithms

The idea of a scan algorithm

Consider a function f  whose parameter is a sequence of values x1, x2, …, xn. A scan algorithm for f  computes f (x1, x2, …, xn) by walking through sequence x1, x2, …, xn once. While walking through the sequence, the algorithm keeps track of information about the part of the sequence that it has looked at so far. A key property of a scan algorithm is:

After looking at prefix of x1, x2, …, xk of x1, x2, …, xn, a scan algorithm knows the value of f (x1, x2, …, xk).

When the algorithm reaches the end of x1, x2, …, xn, the prefix seen so far is the entire sequence. So the algorithm must know f (x1, x2, …, xn).

Example: computing n!

Let's write a definition of function factorial where factorial(n) = n! = 1×2×…×n and n is a positive integer. Initially, factorial does not look like a candidate for a scan algorithm since its parameter is an integer, not a sequence. But positive integer n can be thought of as a stand-in for sequence [1, 2, …, n]; the goal is to compute the product of the numbers in that implicit sequence.

Using a while-loop

Let's start by writing factorial using a while-loop. Choosing to use a scan algorithm tells you how to do a pre-simulation. It tells you to keep track of

Here is a pre-simulation for factorial for n = 5. The implicit sequence is [1, 2, 3, 4, 5].

    n    k    r    prefix already looked at
    5    1    1    [1]
         2    2    [1,2]
         3    6    [1,2,3]
         4   24    [1,2,3,4]
         5  120    [1,2,3,4,5]

The prefix looked at so far is [1, 2, …, k] and r = k! is the result for that prefix. After out working out initialization, updating and when to stop, we get the following.

  // factorial(n) returns n!.
  // Require: n > 0.

  int factorial(int n)
  {
    int r = 1;
    int k = 1;
    while(k != n)
    {
      k++;
      r = r * k;
    }
    return r;
  }

Using a for-loop

Using a for-loop simplifies the definition of factorial by separating walking through the sequence from updating r. Here is an implementation of factorial using a for-loop.

  // factorial(n) returns n!.
  // Require: n > 0.

  int factorial(int n)
  {
    int r = 1;
    for(int k = 1; k <= n; k++)
    {
      r = r * k;
    }
    return r;
  }

That is, starting at r = 1, multiply r by each number in sequence [1, 2, …, n].

Example: finding a maximum value

Suppose that we have been given a function f  that takes an integer parameter and produces an integer result. Exactly what f  does is not important. It can help to think of examples of what f  might be, but we cannot make any assumptions about f .

The goal is to define largestF(n) = max(f (1),  f (2),  …,  f (n)).

Before trying to write a definition of largestF(n), we should look at an example. Suppose

f (x) = (x − 3)(6 − x).

Here is a table of values of f (x) for a few values of x.

x f (x)
1 −10
2 −4
3 0
4 2
5 2
6 0
7 −4

Clearly, largestF(7) = 2, since that is the largest value in the right-hand column.

To use a scan algorithm, we need a sequence to scan. The sequence of values [f (1),  f (2),  …,  f (n)] is clumsy to manage, because it is not clear how to get from f (1) to f (2). But sequence [1, 2, …, n] works well.

Let's use a for-loop. It is easy to walk through sequence [1, 2, …, n]. For reasons that will be clear shortly, let's handle 1 separately, and start at 2 instead. Here is a bare-bones beginning.

  int largestF(int n)
  {
    ...
    for(int k = 2; k <= n; k++)
    {
      ...
    }
    ...
  }

Because we are using a scan algorithm, after looking at prefix [1, 2, …, k], we need to know the value of max(f (1),  f (2),  …,  f (k)). Let's add a variable L that will hold that value after we have looked at prefix [1, 2, …, k]. If L is initially f (1), then we have already looked at 1, and should start looking at additional values starting at 2.

  int largestF(int n)
  {
    int L = f(1);
    for(int k = 2; k <= n; k++)
    {
      ...
    }
    return L;
  }

Finally, we need to flesh out the loop body. Suppose we currently have k = 4 and

L = max(f (1),  f (2),  f (3),  f (4)).

Now the body of the loop is performed with k = 5, and its goal is to compute the new value

L′ = max(f (1),  f (2),  f (3),  f (4),  f (5))

to store into L. Observe:

L = max(f (1),  f (2),  f (3),  f (4),  f (5))
  = max(max(f (1),  f (2),  f (3),  f (4)),   f (5))
  = max(L,  f (5))

since the current value of L is max(f (1),  f (2),  f (3),  f (4)). Now it should be obvious how to update L in the loop body.

  int largestF(int n)
  {
    int L = f(1);
    for(int k = 2; k <= n; k++)
    {
      L = max(L, f(k));
    }
    return L;
  }

Exercises

  1. Write a function productOdds(n) that yields the product of the odd positive integers that are less than or equal to n. Assume that n and the result have type int. Answer

  2. Write a definition of function sumCubes(n), which returns 13 + 23 + … + n3. Answer