Fundamentals of Computer Science II (CSC-152 99F)


Exam 2: Algorithms and Recursion

Distributed: Friday, October 8, 1999
Due: 10 a.m., Friday, October 15, 1999
No extensions!

This page may be found online at http://www.math.grin.edu/~rebelsky/Courses/CS152/99F/Exams/exam.02.html.

Code files:

Policies

There are four problems on the exam, each with an equivalent point value (25 points). Note that the different questions are not necessarily of the same complexity or length.

This examination is open book, open notes, open mind, open computer, open Web. Feel free to use any and all resources available to you except for other people and for exam and homework solutions. As always, you are expected to turn in your own work. If you find ideas in a book or on the Web, be sure to cite them appropriately. Do not use exam or homework solutions you find on the Web.

This is a take-home examination. It is likely to take you about four hours. You may use any time or times you deem appropriate to complete the exam, provided you return it to me by the due date. No late exams will be accepted. I will make a solution key available soon after the exam.

Because different students may be taking the exam at different times, you are not permitted to discuss the exam with anyone until after I have returned it. If you must say something about the exam, you are allowed to say ``This is definitely the hardest exam I have ever taken.'' You may also summarize these policies.

Answer all of your questions electronically. That is, you must write all of your answers on the computer and print them out. You should also email me a copy of your exam (a plain text file, please). Please put your answers in the same order that the problems appear in.

I will give partial credit for partially correct answers. You ensure the best possible grade for yourself by highlighting your answer and including a clear set of work that you used to derive the answer.

I may not be available at the time you take the exam. If you feel that a question is badly worded or impossible to answer, note the problem you have observed and attempt to reword the question in such a way that it is answerable. If it's a reasonable hour (before 10 p.m. and after 8 a.m.), feel free to try to call me in the office (269-4410) or at home (236-7445).

I will also reserve time at the start of classes next week to discuss any general questions you have on the exam.


Problems

A. Recursive Running Time Analysis

Our old pals, Carl and Carla Caffeinated, have decided to branch out. Now, not only do they craft code, they also analyze algorithms. They've come up with the following equations for the running time of an algorithm, where f is ``the number of steps the algorithm takes''.

They've lost the original algorithm, so you'll have to take their word that they've come up with the correct equations.

Carl observes that this pair looks a lot like the equations we had for binary search:

He therefore concludes that the algorithm takes O(log2n) steps.

Carla says that this pair looks a lot like the equations we had for merge sort:

She therefore concludes that the algorithm takes O(nlog2n).

Is either of them right? Precisely determine the behavior of f1.

B. Greatest Difference

Carla and Carl have decided to write a program to determine the greatest difference between any two values in an array of integers. They recall seeing a similar formulation on assignment 3:

To find the smallest difference between any two 
  different numbers in a collection ...
Set estimate to ``infinity''
For each value, v, in the collection
  For each value, u, not equal to v
    If |u-v| < estimate then
      Set estimate to |u-v|
Return estimate

They rewrite this as:

To find the greatest difference between any two 
  different numbers in a collection ...
Set estimate to 0
For each value, v, in the collection
  For each value, u, not equal to v
    If |u-v| > estimate then
      Set estimate to |u-v|
Return estimate

They then implement both methods as follows.


/**
 * Utilities for finding interesting facts about arrays of integers.
 *
 * @author Samuel A. Rebelsky
 * @author Carla Caffeinated
 * @author Carl Caffeinated
 * @version 1.0 of October 1999
 */
public class DifferenceComputer {
  /**
   * Determine the greatest difference between any pair of values
   * in the an array of integers.
   * Pre: The array is nonempty.
   * Pre: The greatest difference is less than the maximum integer.
   * Post: Returns the largest |values[i]-values[j]|.
   */
  public int greatestDifference(int[] values) {
    int estimate = 0;
    int difference;
    // For each value, values[i], in the collection
    for (int i = 0; i < values.length; ++i) {
      // For each value, values[j], in the collection
      for (int j = 0; j < values.length; ++j) {
        // Determine the difference between the two values
        difference = Math.abs(values[i]-values[j]) ;
        // If it's bigger than the biggest difference so far then
        if (difference > estimate) {
          // Update our estimate of the biggest difference
          estimate = difference;
        }
      } // for j
    } // for i
    return estimate;
  } // greatestDifference(int[] values)
  
  /**
   * Determine the greatest difference between any pair of values
   * in the an array of integers.
   * Pre: The array is nonempty.
   * Pre: The greatest difference is less than the maximum integer.
   * Post: Returns the largest |values[i]-values[j]|.
   */
  public int leastDifference(int[] values) {
    int estimate = Integer.MAX_VALUE;
    int difference;
    // For each value, values[i], in the collection
    for (int i = 0; i < values.length; ++i) {
      // For each value, values[j], in the collection
      for (int j = 0; j < values.length; ++j) {
        // If the two values are not the same
        if (values[i] != values[j]) {
          // Determine the difference between the two values
          difference = Math.abs(values[i]-values[j]) ;
          // If it's smaller than the least difference so far then
          if (difference < estimate) {
            // Update our estimate of the least difference
            estimate = difference;
          }
        } // if the two values are different
      } // for j
    } // for i
    return estimate;
  } // leastDifference(int[] values)
} // class DifferenceComputer


They even go so far as to build a test class and their program seems to work.


import SimpleOutput;

/**
 * Test our amazing and clearly marketable difference computer.  Thanks
 * to our beloved teacher, Sam Rebelsky, for giving us the initiative
 * to create such a great product.
 *
 * Usage:
 *   ji TestDC value1 value2 ... valuen
 *
 * @author Carl "Creative Code" Caffeinated
 * @author Carla "Clearly Clever" Caffeinated
 * @version 1.0 of October 1999
 */
public class TestDC {
  public static void main(String[] args) {
    // Prepare for output.
    SimpleOutput out = new SimpleOutput();
    // The array of integers.
    int[] values = new int[args.length];
    // An index into that array;
    int numValues = 0;
    // Did the user screw up?  Assume not.
    boolean badInput = false;
    // Create something that can compute differences.
    DifferenceComputer computer = new DifferenceComputer();
    // Convert each value to an integer.  Let the user know if (s)he
    // screwed up.
    for (int i = 0; i < args.length; ++i) {
      try {
        values[numValues++] = Integer.parseInt(args[i]);
      }
      catch (NumberFormatException nfe) {
        out.println("'" + args[i] + "' is not a number");
        badInput = true;
      }
    } // for
    // If the user gave bad input, we need to update the array
    if (badInput) {
      int[] tmp = values;
      values = new int[numValues];
      for (int i = 0; i < numValues; i++) {
        values[i] = tmp[i];
      } // for
    } // if (badInput)
    // Find the biggest difference
    out.print("The greatest difference between values is: ");
    out.println(computer.greatestDifference(values));
    // And the least.
    out.print("The least difference between values is: ");
    out.println(computer.leastDifference(values));
  } // main(String[])
} // class TestDC


Because they'd done the homework assignment (or at least checked the answer key), they know that the running time of this algorithm is O(n2), were n is the number of elements in the array.

They are bragging about their solution to Mable and Myron Mathemagician when their friends observe ``You should be able to find the greatest difference in many fewer steps, perhaps even O(n)!'' Help the Caffeinateds write a better solution. The Mathemagicians suggest that you think about how the greatest difference differs from the least difference.

You need not write working Java code. An algorithm in understandable pseudocode is fine.

C. Quicksort

Mable and Myron Mathemagician have decided to implement Quicksort. Here's what they've come up with so far.


import Array;
import Comparator;
import IncomparableException;
import Sortable;

/**
 * Arrays that can be sorted using the amazing Quicksort routine.  Note
 * that we violate Java's method naming conventions because Quicksort is
 * a proper name.
 *
 * @author Samuel A. Rebelsky (general structure)
 * @author Mable Mathemagician
 * @author Myron Mathemagician
 * @version 1.1 of October 1999
 */
public class Quicksortable
  extends Array
  implements Sortable
{
  // +--------------+--------------------------------------------
  // | Constructors |
  // +--------------+
  
  /**
   * Build a new sortable array of a particular size.
   */
  public Quicksortable(int n) {
    super(n);
  } // Quicksortable(int)
  
  /**
   * Build a new sortable array with a particular set of elements. 
   */
  public Quicksortable(Object[] elements) {
    super(elements);
  } // Quicksortable(Object[])
  
  // +-----------------------+-----------------------------------
  // | Methods from Sortable |
  // +-----------------------+
  
  /**
   * Sort an array using Quicksort.  
   * Pre: All elements in the array can be compared to each other.
   * Post: The array is sorted (using the standard meaning).
   */
  public void sort(Comparator compare) 
    throws IncomparableException
  {
    sort(compare, null);
  } // sort(Object[])

  /**
   * Sort an array using Quicksort.  If observer is non-null,
   * prints out pithy notes about the sorting.
   * Pre: All elements in the array can be compared to each other.
   * Post: The array is sorted (using the standard meaning).
   */
  public void sort(Comparator compare, SimpleOutput observer) 
    throws IncomparableException
  {
    Quicksort(0, size()-1, compare, observer);
  } // sort(Object[])

  // +----------------+------------------------------------------
  // | Helper Methods |
  // +----------------+
  
  /**
   * Sort part of an array using Quicksort.
   * Pre: All elements in the subarray can be compared to each other.
   * Pre: 0 <= lb <= ub < size()
   * Post: The array is sorted (using the standard meaning).
   */
  protected void Quicksort(int lb, int ub, 
                           Comparator compare,
                           SimpleOutput observer) 
    throws IncomparableException
  {
    if (observer != null) {
      observer.println("lb = " + lb + "; ub = " + ub);
    }
    // Variables
    Object pivot;	// The pivot we select.  Must be part of the subarray.
    int mid;		// The position of the pivot
    // Base case: size one arrays are sorted.
    if (lb == ub) return;
    // Pick a pivot and put it at the front of the array.
    swap(lb,pickPivot(lb,ub));
    // Determine the position of the pivot, while rearranging the array.
    mid = partition(lb,ub,compare);
    // Recurse on nonempty subarrays.
    if (lb<=mid-1) Quicksort(lb,mid-1, compare, observer);
    if (mid+1<=ub) Quicksort(mid+1,ub, compare, observer);
  } // Quicksort
  
  /**
   * Split the subarray given by [lb .. ub] into ``smaller'' and
   * ``larger'' elements, where smaller and larger are defined by
   * their relationship to a pivot.  Return the index of the pivot 
   * between those parts.  Uses the first element of the array 
   * as the pivot.
   * Pre: All the elements in the subarray can be compared.
   * Post: Returns n such that for all i between lb and n inclusive
   *   and j between n+1 and ub inclusive, get(i) <= pivot < get(j).
   */
  protected int partition(int lb, int ub, Comparator compare) 
    throws IncomparableException
  {
    // STUB.  Can you figure it out?
    return lb;
  } // partition(int, int, Comparator)
  
  /**
   * Pick a pivot in a subarray.
   * Pre: 0 <= lb <= ub < size()
   * Post: returns the index of the pivot (a value between lb and ub inclusive).
   */
  protected int pickPivot(int lb, int ub) {
    // Simple implementation: pick the last element.  Might be extended
    // to do something more interesting, such as pick a random element
    // in the subarray.
    return ub;
  } // pickPivot(int,int)
} // Quicksortable


They've even convinced me to write them a test class.


import IncomparableException;
import StringComparator;
import Quicksortable;
import SimpleOutput;

/**
 * A test of the Quicksortable class.  Sorts the strings on the command
 * line and then prints them out.
 *
 * @author Samuel A. Rebelsky
 * @version 1.0 of October 1999
 */
public class TestQS {
  /** Do the testing. 
   *
   * @exception IncomparableException
   *   Never, since StringComparator never throws one, but it's
   *   easier to claim that we might throw one since it seems that
   *   the sort method might.
   */
  public static void main(String[] args)
    throws IncomparableException
  {
    // Prepare for output.
    SimpleOutput out = new SimpleOutput();
    // Build the array for sorting.
    Quicksortable stuff = new Quicksortable(args);
    // Sort!
    stuff.sort(new StringComparator(),out);
    // And print it out.
    for (int i = 0; i < stuff.size(); ++i) {
      out.println(i + ": " + stuff.get(i));
    } // for
  } // main(String[])
} // class TestQS


Unfortunately, they haven't figured out how to write the partition method. They've asked their friends Carl and Carla to help. However, Carl and Carla are too busy raking in cash from their code to help. So Mable and Myron have turned to you. Write the partition method for them. Make sure to test your code.

D. Binary Search in Unsorted Lists

Carl and Carla have become so enamored of divide-and-conquer that they use it for almost every algorithm they write. Last week, they needed to write a search method for unsorted lists, and decided to use divide-and-conquer. Of course, since they don't know which half to look in if the object is not in the middle, they search in both halves. Here's their code (yes, they still need to work on their commenting).


import Comparator;

/**
 * Utilities for searching in arrays.
 *
 * @author Carl Caffeinated
 * @author Carla Caffeinated
 */
public class Searcher {
  /** 
   * Determine if a value is in the array.
   */
  public boolean search(Object lookForMe, Object[] lookHere, Comparator compare)
    throws IncomparableException
  {
    return search(lookForMe, lookHere, 0, lookHere.length-1, compare);
  } // search(Object, Object[])
  
  /**
   * Determine if a value is in a subarray.
   */
  protected boolean search(Object lookForMe, Object[] lookHere, 
                           int lb, int ub,
                           Comparator compare) 
    throws IncomparableException
  {
    // Is the subarray empty?  If so, the object can't be there.
    if (ub < lb) return false;
    // Otherwise, it must be in the middle, left half, or right half.
    int mid = (lb + ub) / 2;
    return ( compare.equals(lookForMe, lookHere[mid]) ||
             search(lookForMe, lookHere, lb, mid-1, compare) ||
             search(lookForMe, lookHere, mid+1, ub, compare) );
  } // search(Object, Object[], int, int)
} // class Searcher


Unfortunately, Carla and Carl have realized that they just don't get running time analysis and ask for your help. Determine the running time of this method.

Extra Credit

Each of these problems is worth up to five points of extra credit.

E. Improved Pivot Selection

Update Quicksortable so that pickPivot actually selects a random pivot value from within the range lb .. ub. You will need to refer to the Java API to figure out how to generate random values. Your code must work.

F. Finding the Least Difference, Revisited

Mable and Myron, apparently frustrated at Carl and Carla's lack of help, tell Carl and Carla ``not only is your greatestDifference much too slow, so is your leastDifference''. Determine a better way (in terms of Big-O running time) to implement leastDifference. In this case, working code is not necessary. All you need to do is convince me that you've come up with the appropriate idea (and analyzed the running time).


History

Wednesday, 6 October 1999

Thursday, 7 October 1999


Disclaimer Often, these pages were created "on the fly" with little, if any, proofreading. Any or all of the information on the pages may be incorrect. Please contact me if you notice errors.

This page may be found at http://www.math.grin.edu/~rebelsky/Courses/CS152/99F/Exams/exam.02.html

Source text last modified Fri Oct 8 09:09:57 1999.

This page generated on Sun Oct 17 20:56:02 1999 by Siteweaver. Validate this page's HTML.

Contact our webmaster at rebelsky@grinnell.edu