12. Loop Algorithms 1: Scan Algorithms


The idea of a scan algorithm

A scan algorithm answers a question by walking through a sequence of values once. As it goes, it keeps track of information about the sequence of values that it has seen so far.

A key property of a scan algorithm is that the information being kept includes, at a minimum, the answer to the question, assuming that the algorithm is at the end of the input sequence. That way, when it actually does reach the end, the algorithm knows the answer to give.

Here are some examples of problems that are natural candidates for scan algorithms.


Using a loop to implement a scan algorithm

A loop is an obvious way to implement a scan algorithm, and it is usually the recommended approach. Just be sure to initialize your variables, to scan through the sequence correctly, and to update the information that you are keeping correctly. Here are implementations of problems similar to each of the above problems.

  //===============================================
  // sum(A,n) returns A[0] + A[1] + ... + A[n-1].
  //===============================================

  int sum(const int A[], const int n)
  {
    int total = 0;
    for(int i = 0; i < n; i++)
    {
      total = total + A[i];
    }
    return total;
  }

  //===============================================
  // factorial(n) returns n!.
  //===============================================

  int factorial(const int n)
  {
    int f = 1;
    for(int i = 1; i <= n; i++)
    {
      f = f * i;
    }
    return f;
  }

  // largest(A, n) returns the largest 
  // of A[0], ..., A[n-1].  
  //
  // Requirement: n > 0.

  int largest(const int A[], const int n)
  {
    int big = A[0];
    for(int i = 1; i < n; i++)
    {
      big = max(big, A[i]);
    }
    return big;
  }

  //===============================================
  //  ___________________________________________________________
  // |This example uses the idea of a null-terminated                |
  // |string, which is an array of characters that                 |
  // |ends on a null character, '\0'.  For example,                  |
  // |string "123" is represented as an array s where            |
  // |                   s[0] = '1'                              |
  // |                   s[1] = '2'                              |
  // |                   s[2] = '3'                              |
  // |                   s[4] = '\0'                             |
  // |We will look at null-terminated strings in detail          |
  // |later.                                                     |
  // |                                                           |
  // |If character c is a digit then c - '0' is the integer that |
  // |c stands for.  For example, '2' - '0' = 2.  Don't confuse  |
  // |character '0' (the digit 0) with '\0' (the null character).|
  // |___________________________________________________________|
  //
  // convertToInt(s) yields the integer described
  // by string s. For example, convertToInt("325")
  // = 325.
  //
  // Requirement: s must be a null-terminated string
  // that only contains decimal digits.
  //===============================================

  int convertToInt(const char s[])
  {
    int x = 0;
    for(int i = 0; s[i] != '\0'; i++)
    {
      x = 10*x + (s[i] - '0');
    }
    return x;
  }

Remark. There is a library function called atoi that does the same thing as convertToInt. Include <cstdlib> to use atoi.


Filtered scans

Sometimes you want accumulate information about only selected values in a sequence of values. To do that, just scan through the sequence, skipping over values that are not of interest. The following illustrates. We assume that a function isPrime(n) is available, which returns true just when n is prime.

  //==============================================
  // sumPrimes(n) yields the sum of the prime
  // numbers that are less than or equal to n.
  // For example, sumPrimes(5) = 2 + 3 + 5 = 10.
  //==============================================

  int sumPrimes(const int n)
  {
    int total = 0;
    for(int i = 2; i <= n; i++)
    {
      if(isPrime(i))
      {
        total += i;
      }
    }
    return total;
  }


Summary

A scan algorithm solves a problem by making one scan over a sequence of values. Examples of scans are adding up a list of numbers and finding the largest value in an array.

Scan algorithms occur frequently in practice. Suppose that A is an array of size n. A boilerplate scan algorithm to scan A is as follows.

  initialize result variable(s)
  for(int i = 0; i < n; i++)
  {
    update result variable(s)
  }

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

  3. Write a definition of largestPrime(A, n), which returns the largest of A[0], … A[n−1] that is prime, where A is an array of ints. Assume that function isPrime(n) is available. If none of those integers is prime then largestPrime(A, n) should return 0;

    If a prime number that is greater than A[i] has already been found when A[i] is being considered, then largestPrime should not compute isPrime(A[i]) at all, since it is clear that A[i] cannot be the largest prime number in array A.

    Answer