CS 415, Section 002 Sonoma State University Spring, 2022
 
Algorithm Analysis
Instructor: Henry M. Walker

Lecturer, Sonoma State University
Professor Emeritus of Computer Science and Mathematics, Grinnell College

Lab:  Practice with Dynamic Programming and Optimal Binary Search Trees

  1. Recall that the Fibonacci sequence is defined recursively as follows: In practice, this sequence arises is a surprising number of applications!

    Program fibonacci.c defines two procedures to compute the nth Fibonacci number.

    1. Compile and run the program to clarify what it does. (In the interests of time, you may want to change the upper bound of the loop in the main procedure from 45 to 41.)
    2. In fibSeq2, why do you think fibSeq2 has only an int as parameter, rather than an int and an array as defined in fibSeq2Helper?
    3. Explain in a few sentences the role of the array arr.
    4. In fibSeq2Helper, why not declare and initialize the array in one step?
          int arr [n+1] = {0};
          
    5. Write a sentence or two to explain the test
          if (fibArr[n] != 0)
          
    6. Explain why an assignment is included in the return statement
          return fibArr[n] = 1;
          

Computing Combinations

A widely-used problem in computer science involves determining the number of ways that k items can be chosen from a pool of n distinct objects, ignoring the ordering of elements selected. In writing and speech, this quantity is denoted several ways:

This "binomial coefficient" is discussed in CS 242 and on many Web sites, such as Combinations and Permutations by Maths is Fun.

Computationally, discussions of these combinations often specify a computational formula involving factorials (where k factorial or k! = k*(k-1)*(k-2)*...*3*2*`)

C(n,k) = n! / [k! (n-k)!]

For example, the number of 5-card hands possible from a deck of 52 cards would be C(52, 5).

Although this formula works well on paper, a pragmatic problem is that the computation of factorials produces large numbers very quickly:

  1. The program factorial.c computes successive factorials (1!, 2!, 3, ...) until integer overflow occurs—the integer is too large to be stored in an integer variable.

    1. Compile and run this program and observe the output.
    2. How large can an integer n be, so that n! can be computed as an int?
    3. Modify the program so the fact variable and associated elements use a long integer rather than an int How much does this increase in storage allow factorials to be computed.

Since factorials are often too large for many simple computations, alternative approaches are needed for computation. One approach uses the recurrence relation:

As well as being explained in CS 242, many online sources provide a proof of this relation, including Proposition: Recursive Formula for Binomial Coefficients from the Book of Proofs.

With this recurrence relation, alternative ways to compute C(n, k) could follow either of two approaches:

These alternatives form the basis for the following programming task.

  1. Write a program that reads values for n and k and computes C(n, k) in the two ways described above.

    1. Run your code with sufficient test cases, so you can give a strong argument that the functions work properly. (And include your argument in your solution of this problem.)
    2. Once your functions work properly, add code to determine the timing of each of these approaches. Write a brief commentary to indicate the extent to which the table in the second approach increases efficiency.
    3. From your work in Step 2, you know values of n for which the computation of n! fails, so the expression n! / [k! (n-k)!] fails. Determine what, if any, larger values of C(n, k) can be computed with your current program.

Optimal Binary Search Tree

  1. Determine the cost and structure of an optimal binary search tree with the following frequencies:

    Boxer Doberman Lab Spaniel Terrier
    3 2 4 6 1