CS 2413 001 Summer 2000
Homework #2 Solutions


  1. Using the definition of big-O on page 53 of OODS1 for each of the following functions, if an algorithm takes that many primitive operations on an input of size n, what is the time complexity of the algorithm? Show that your answer is correct by choosing suitable values for the constants c and n0. You do not need to give a formal mathematical proof (which is beyond the scope of this course); instead, you may obtain the values of these constants by testing or by informal reasoning. Explain how you got your answer.

    1. 6n2 + 9n + 12
      Answer: O(n2)
      Explanation: In general, the fastest growing term will give us our big-O. By the definition in OODS:
      Let f(n) and g(n) be functions mapping nonnegative integers to real numbers. We say that f(n) ... is of order g(n) ... if there is a real number c > 0 and a fixed integer n0 > 1 such that f(n) < cg(n) for every integer n > n0.
      More specifically, if we choose c = 7 and n0 = 11, we see that
      f(n0) = 6 × n02 + 9 × n0 + 12 = 6 × 112 + 9 × 11 + 12 = 837 and cg(n0) = c × n02 = 7 × 112 = 847, so f(n0) < cg(n0).
      For n > n0, this relationship will continue to hold (which we could prove by induction).

    2. 0.015n log2n + 22n + 126 log2n + 329
      Answer: O(n log2n)
      Explanation: Again, the fastest growing term will give us our big-O, and in this case the n log2n term grows fastest.
      More specifically, if we choose c = 67 and n0 = 5, we see that
      f(n0) = 0.015n log2n + 22n + 126 log2n + 329 = 0.015 × 5 log25 + 22 × 5 + 126 log25 + 329 = 731.737
      and cg(n0) = c × n0log2n0 = 67 × 5 log25 = 777.846, so f(n) < cg(n).
      For n > n0, this relationship will continue to hold.

    3. 17 log2n + 8
      Note that log2n is the same as (logn)2.
      Answer: O(n log2n)
      Explanation: In this case, the n log2n term grows fastest.
      More specifically, if we choose c = 18 and n0 = 2, we see that
      f(n0) = 17 × log2n0 + 8 = 17 × log22 + 8 = 17 × 12 + 8 = 25
      and cg(n0) = c × log2n0 = 18 × log22 = 18 × 12 = 36, so f(n0) < cg(n0).
      For n > n0, this relationship will continue to hold

    Ask for help with this question.


  2. In Figure 1.1 on page 55 of OODS, the value of log 10 is 3.322, which is actually log210 (log to the base 2 of 10). Does it matter whether we use log2, log10 or loga for some real number a > 1? Explain. (Hint: what is the relationship between logpn and logqn?

    Answer: No, it does not matter. As any precalculus or calculus book2 will tell us,

    logpn = logqn / logqp

    so when comparing bases 2 and 10, we have that

    log2n = log10n / log102

    and log102 is a constant.
    So, the distinction between log2n and log10n is a constant, and we can drop the constant out of our big-O by incorporating it into the value that we choose for c (with an associated adjustment of n0) in the above definition.

    Ask for help with this question.


  3. For each of the following problems, is it possible to write a procedure that will solve the problem and that meets the definition of an algorithm (OODS, page 47)? You don't need to write the algorithm, but you must explain your answer.
    1. Determine whether two strings are the reverse of each other (i.e., the last character of one is the first of the other, etc).
      Answer: Yes. From OODS:
      ... An algorithm consists of a set of finite steps satisfying the following conditions:

      1) Input: The number and type of input values must be made clear.
      2) Precise specification of each step: Each step or instruction of an algorithm must be feasible and unambiguously defined.
      3) Finiteness: for all input possibilities, the algorithm must terminate in finite time.
      4) Result: It must be clear what the algorithm is intended to accomplish. There may be an output that spells out the outcome of the execution of the algorithm.
      ...
      Input: two strings (which are of finite length by definition, since the abstract data type of a string has a length method)
      Output: Boolean
      Algorithm: loop simultaneously through both of the strings, the first from front to rear and the second from rear to front, comparing the current pair of elements. This can be accomplished in an amount of time proportional to the length of the shorter string, which is finite.

    2. Determine whether a given integer x is a prime number (only factors are 1 and itself).
      Answer: Yes.
      Input: integer x
      Output: Boolean
      Algorithm: loop through the integers from 2 to x-1 (a finite number of values), checking whether x is divisible by the current value. (Note that there are much more efficient algorithms for this problem.)

    3. Determine whether the countries on a map can be colored with only four colors, so that no two adjacent countries have the same color.
      Answer: Yes.
      Input: a finite number of countries n, and a map expressed as a set of relationships between the countries
      Output: Boolean
      Algorithm: loop through all possible 4-colorings of the map (of which there are 4n, because each country can get any of 4 colors), and if the current 4-coloring meets the condition that no two adjacent countries have the same color, output TRUE; otherwise, if no such coloring is ever found, output FALSE. There are a finite number (4n) of cases to examine, so the algorithm will terminate in finite time.

    4. Consider this formula: xn + yn = zn
      Is n = 2 the largest integer value for n for which there exist positive integers x, y and z such that the formula has at least one solution?
      Answer: No.
      Input: none
      Output: Boolean
      Algorithm: for every n > 2, examine every set of integers x > 1, y > 1, z > 1, until a solution is found.
      The algorithm will require infinite time, because it will need to examine infinitely many values for each of n, x, y and z.

    Ask for help with this question.


  4. Write recursive functions in C++ to solve the following recurrence relations.
    1. Ackerman's function:

      / n + 1, if m = 0
      A(m,n) = | A(m-1,1), if n = 0
      \ A(m-1,A(m,n-1)), otherwise

      Answer:
      int A (int m, int n)
      { // A
          if (m == 0) return n + 1;
          if (n == 0) return A(m - 1, 1);
          return A(m - 1, A(m, n - 1));
      } // A
      
      Demonstration:
      % cat hw2q4a.cpp
      #include <iostream>
      
      int A (int m, int n)
      { // A
          if (m == 0) return n + 1;
          if (n == 0) return A(m - 1, 1);
          return A(m - 1, A(m, n - 1));
      } // A
      
      int main ()
      { // main
          int m, n;
      
          cout << "Enter m:" << endl;
          cin >> m;
          cout << "Enter n:" << endl;
          cin >> n;
          cout << "A(" << m << "," << n << ") = " << A(m, n) << endl;
      } // main
      % g++ hw2q4a.cpp
      % a.out
      Enter m:
      3
      Enter n:
      2
      A(3,2) = 29
      
    2. Greatest Common Divisor:

      / m, if n = 0
      GCD(m,n) = | n, if m mod n = 0
      \ GCD(n,m mod n), otherwise

      Answer:
      int GCD (int m, int n)
      { // GCD
          if (n == 0) return m;
          if ((m % n) == 0) return n;
          return GCD(n, m % n);
      } // GCD
      

      Demonstration:
      % cat hw2q4b.cpp
      #include <iostream>
      
      int GCD (int m, int n)
      { // GCD
          if (n == 0) return m;
          if ((m % n) == 0) return n;
          return GCD(n, m % n);
      } // GCD
      
      int main ()
      { // main
          int m, n;
      
          cout << "Enter m:" << endl;
          cin >> m;
          cout << "Enter n:" << endl;
          cin >> n;
          cout << "GCD(" << m << "," << n << ") = " << GCD(m, n) << endl;
      } // main
      % g++ hw2q4b.cpp
      % a.out
      Enter m:
      30
      Enter n:
      75
      GCD(30,75) = 15
      

    Ask for help with this question.


  5. Draw the call tree (OODS, page 62) for the recursive algorithms from the previous question:
    1. Ackerman's function for m = 5 and n = 3

      Answer: Yikes!

    2. Greatest Common Divisor for m = 30 and n = 75

      Answer:

      GCD(30,75)
          |
          |
      GCD(30,15)
      


    Ask for help with this question.


  6. Write a constructor for ArrayClass with the signature

    ArrayClass (int arrayLength, int listLength, Object* list)

    that constructs an instance of ArrayClass with arrayLength elements, such that:
    template <class Object>
    ArrayClass<Object>::ArrayClass (
            int arrayLength, int listLength, Object* list)
    { // ArrayClass<Object>::ArrayClass
        int n;
    
        _size = 0;
        paObject = new Object[arrayLength];
        if (paObject == NULL)
            throw ArrayMemoryException();
        _size = arrayLength;
        if (arrayLength <= listLength) 
             n = arrayLength;
        else n = listLength;
        for (int i = 0; i < n; i++)
            paObject[i] = list[i];
    } // ArrayClass<Object>::ArrayClass
    

    Ask for help with this question.


  7. Write a function that takes an instance of ArrayClass and returns another instance of ArrayClass that has the same elements, except that repeats of the same value are eliminated; for example, applying the function to an array containing 4,12,64,12,92,12,53,92,4 will return an array containing 4,12,64,92,53. (Hint: use a bucket array.)

    Note: because this question may appear on an exam, a solution is not provided here.


References

1 S. Radhakrishnan, L. Wise & C. N. Sekharan, Object-Oriented Data Structures Featuring C++, 1999.
2 S.L. Salas & E. Hille, Calculus: One and Several Variables with Analytic Geometry, 3rd ed., John Wiley & Sons, New York, 1978, p. 262.

Ask for help with this question.