[Instructions] [Search] [Current] [Syllabus] [Links] [Handouts] [Outlines] [Labs] [More Labs] [Assignments] [Quizzes] [Examples] [Book] [Tutorial] [API]
Assigned: Friday, February 26, 1999
Due: Monday, March 8, 1999
In this assignment, you will apply, explore, and expand your skills at algorithm design and analysis.
One of the reasons that computer scientists developed a formal description of Big-O is that it permits us to develop and apply a number of important identities. For example, we might wish to consider whether BigO is transitive. That is, if f(N) is in O(g(n)) and g(n) is in O(h(n)), can we say that f(N) is in O(h(n))?1
We've said that Big-O notation allows us to discard lower-order terms. Let's try to formalize that idea. Prove that if f(N) is in O(g(N)+h(N)), and g(N) is in O(h(N)), then f(N) is in O(h(N)).
We've said that Big-O notation allows us to discard constant multipliers. Let's try to formalize that idea. Prove that if f(N) is in O(C*g(N)) then f(N) is in O(g(N)).
We've done some exploring of the Fibonacci sequence, 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ..., which might be defined by the following Java function.
/**
* Compute the nth Fibonacci number.
* Pre: n >= 0
* Pre: the nth Fibonacci number <= Long.MAX_VALUE
* Post: returns the nth Fibonacci number
*/
public long fib(long n) {
if (n <= 1) return n;
else return fib(n-1) + fib(n-2);
} // fib(long)
Compute upper and lower bounds on the running time of the definition of
fib given above. Express the running time in Big-O notation,
in terms of the
number of calls to fib in order to compute fib(n).
The bounds should be as tight as you can
get them (preferably, both will be the same).
Hint: you can assume that the number of steps to compute fib(n) is at least as many as the number to compute fib(n-1).
Improve the computation of the nth Fibonacci number using the technique of dynamic programming (caching previously computed results in a table). Test your revised function appropriately.
Compute an upper bound on the running time for your new method of computing
the Fibonacci numbers. Express the running time in Big-O notation,
in terms of the
number of calls to fib in order to compute fib(n).
The bound should be as tight as you can
get it.
Rewrite your efficient Fibonacci computer iteratively (using loops rather than recursion).
Theodore and Themla Trinary enjoyed the use of binary search so much that they've decided to develop their own variant, based on dividing the subarray into three parts, rather than two. Here's their algorithm.
/**
* Determine the index of x in array A.
* Pre: A is sorted in increasing order.
* Post: If x is in A, then returns i s.t. A[i] == x
* Post: If x is not in A, then throws an exception
*/
public int trinarySearch(int x, int[] A)
throws Exception
{
// Use the marvelous helper function
return trinarySearch(x, A, 0, A.length-1);
} // trinarySearch(int, int[])
/**
* Determine the index of x in the subarray of A given by lb..ub.
* Pre: A is sorted in increasing order.
* Pre: If x is in A, then x is in the subarray.
* Pre: If x is not in A, then x is not in the subarray.
* Post: If x is in A, then returns i s.t. A[i] == x
* Post: If x is not in A, then throws an exception
*/
public int trinarySearch(int x, int[] A, int lb, int ub)
throws Exception
{
// Base case: empty subarray. x is not in A.
if (ub < ub) throw new Exception("Not found");
// Base case: single-element subarray. See if x is that element.
else if (lb == ub) {
if (x == A[lb]) return lb;
else throw new Exception("Not found");
} // single-element subarray
// Recursive cases: split the array and search the appropriate subarray.
else {
// The first split point is one-third of the way from lb to ub. We
// compute the distance from lb to ub, take 1/3 of that, and add it
// to lb.
int splitOne = lb + 1/3 * (ub-lb);
// The second split point is two-thirds of the way from lb to ub. We
// compute the distance from lb to ub, take 2/3 of that, and add it
// to lb.
int splitTwo = lb + 2/3 * (ub-lb);
// Recursive case: in the first third of the array.
if (x <= splitOne) return trinarySearch(x, A, lb, splitOne);
// Recursive case: in the second third of the array.
else if (x < splitTwo) return trinarySearch(x, A, splitOne+1, splitTwo-1);
// Recursive case: in the third third of the array.
else return trinarySearch(x, A, splitTwo, ub);
}
} // trinarySearch(int, int[], int, int)
Unfortunately, their code is riddled with errors. Identify and correct the errors, both syntactic and semantic. If you use a test suite to identify the errors, please include the test suite.
Find a tight upper bound on the running time of the working trinary search, using Big-O notation.
Endnotes
1 The answer is yes. Here's a proof.
[Instructions] [Search] [Current] [Syllabus] [Links] [Handouts] [Outlines] [Labs] [More Labs] [Assignments] [Quizzes] [Examples] [Book] [Tutorial] [API]
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/99S/Assignments/assign.06.html
Source text last modified Tue Mar 2 08:37:08 1999.
This page generated on Wed Mar 3 09:24:56 1999 by SiteWeaver. Validate this page's HTML.
Contact our webmaster at rebelsky@math.grin.edu