Sonoma State University | ||
Algorithm Analysis
|
||
Instructor: Henry M. Walker
Lecturer, Sonoma State University |
Although CS 415 has been well developed for several years, last year the CS faculty made a significant, long-term curricular change regarding SSU's Upper Division GE Area B Requirement.
Historically, CS Majors could satisfy this requirement by taking CS 454, Theory of Computation, and CS 454 will continue in this role for the next several semesters.
At some time in the future (but not Spring 2025), CS 415, Algorithm Analysis, will allow students to satisfy SSU's Upper Division GE Area B Requirement.
During an anticipated time of transition:
For future semesters, students should check with the CS faculty regarding which course(s) (CS 415 and/or CS 454) will satisfy SSU's Upper Division GE Area B Requirement.
For this problem, refer to Levitin, Section 6.5, for discussions of right-to-left and left-to-right binary exponentiation.
Notes: For Part b and c of this problem:
Program fibonacci.c defines two procedures to compute the nth Fibonacci number.
int arr [n+1] = {0};
if (fibArr[n] != 0)
return fibArr[n] = 1;
This problem is based on the Stack Overflow blog, The complete beginners guide to dynamic programming, with a translation to C/C++ and some additional modifications.
As you know, "dynamic programming" is commonly used to denote a programming practice of storing numbers the first time they are computed, and then refers to those results as needed later on, rather than recomputing expressions repeatedly.
Other terms, commonly used for the same approach, include "a memoized approach", "memoization" or "memoisation". According to Wikipedia, "In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls to pure functions and returning the cached result when the same inputs occur again."
Wikipedia also notes that "memoization" is derived from the word "memorandum" ("to be remembered"), which in American English is abbreviated "memo". This term "has a specialized meaning in computing", which is separate from the term, "memorization".
Given an array a of n [non-negative] integers, a[0], a[1], . . . a[n-1], and a target sum. Determine if there exist two of these numbers (a[i] and a[j] (with i ≠ j)), so that a[i] + a[j} = sum. If such numbers exist, print i and j; otherwise, print "none".
A brute force approach checks all a[i] with all a[j] to determine if adding them together yields the target sum.
Implement this brute force approach
bruteForcePairSum
, with the following
specification
/** ****************************************************** * function to determine whether two numbers in an array * * add to a specified target * * * * @param target the desired sum * * @param n the size of the array * * @param a the array of candidate numbers * * * * @pre all integers in a are non-negative * * * * @post if some indices i and j exist with i != j * * so that a[i] + a[j] = target * * then i and j are printed * * only one (i, j) pair need be printed, even * * if many such pairs satisfy this condition * * if no (i, j) pairs satisfy this condition * * the function prints "none" * * * **********************************************************/ bruteForcePairSum (int target, int n, int a [ ])
In the above reading, i and j both range between 0 and
n-1.
However, if a[i] + a[j] = target, then a[j] + a[i] = target,
as well. Thus, in printing the pair (i, j), one can specify
that i < j.
For efficiency, your implementation
of bruteForcePairSum
should ensure that in the nested loops for i and j,
j is always larger than i.
Of course, once an i and j are found, so that a[j] + a[i] = target, the (i, j) pair can be printed and the function can return. However, processing must continue to check all relevant pairs, before the conclusion "none" can be determined.
For testing, place bruteForcePairSum
within a
program that reads the size of the array and the
array elements and then calls the function.
The above reading indicates that this approach yields an
O(n2) algorithm. Perform a careful microanalysis
of your implementation of bruteForcePairSum
in
the worse case to prove that this algorithm indeed has
O(n2).
Will this same approach work if the array elements are
allowed to be negative as well as non-negative?
Explain.
Will this same approach work if the target sum is allowed to
be negative as well as non-negative? Explain.
Continuing the previous problem and building on the above reading
from StackOverflow, write a function dynamicPairSum
also with the above function specification. However, for this
implementation using dynamic programming, proceed as follows.
sumMinusElt[target]
initialized to 0.
From the "memoized approach" in the StackOverflow reading,
loop through each element a[i] of array a, and set
sumMinusElt[target-a[i]] = i
. (In this code,
you may need to check that
subscript target-a[i]
does not go out of bounds.)
After adding this code your
your dynamicPairSum
function, explain why this
array takes the place of the diff
variable in
the StackOverflow pseudocode. (Hint: this approach
provides a very fast way to access known
differences target-a[i]
—but why is that
helpful?)
Add one more (non-nested) loop through array a
to dynamicPairSum
, checking for each j
whether sumMinusElt[a[j]]
is non-zero. If so,
print j and sumMinusElt[a[j]]
. If all
elements sumMinusElt[a[j]]
are zero, print
"none".
(Again, in this code, you may need to check that
subscript a[i]
does not go out of bounds.)
After adding this final loop to
your dynamicPairSum
function, explain why
this approach effectively takes handles the rest of
the code in the StackOverflow pseudocode.
The above reading indicates that this approach yields an
O(n) algorithm. Perform a careful microanalysis of your
implementation of dynamicPairSum
in the worse
case to proved that this algorithm indeed has O(n).
Add your dynamicPairSum
function to the program
in Step 3, and run the program with several different arrays
and target sums.
(Optional, for extra credit) To test both functions in a worst case, you might try setting array a to a few thousand even numbers (no reading of numbers---just generating the array in code) and then search for a target sum that is odd. Compare the approximate run times of the two algorithms (using a clock or mobile device for rough timing) in this case. (In my testing, I found noticeable differences, when n=100000.)
Will this same approach work if the array elements are
allowed to be negative as well as non-negative?
Explain.
Will this same approach work if the target sum is allowed to
be negative as well as non-negative? Explain.
Note: To submit your work for Steps 3 and 4, include a listing of the program, the output obtained from several tests with different arrays and target sums, and your statements answering questions in steps 4c, 4e, and 4i.
Looking Ahead: The function dynamicPairSum
keeps track of differences target-a[i]
in a way that
can be accessed and checked quickly, although this approach may
require a large sumMinusElt
array. Later in the
semester, we will consider another way to store the same basic data
but in a form that may be considerable more compact (called a hash
table).
A hash function returns integers between 0 and 15 based on the first letter of a data item according to the following table.
A | B | C | D | E | F | G | H | I | J | K | L | M |
15 | 11 | 6 | 11 | 13 | 15 | 4 | 11 | 12 | 9 | 7 | 3 | 4 |
N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
11 | 5 | 13 | 6 | 12 | 15 | 7 | 3 | 9 | 10 | 13 | 10 | 4 |
For example, according to this hash function, the string TM would hash to the value 7 (based on the initial letter T; the hash function does not look at the second letter M).
Consider the following sequence of 13 data items:
LM MA OP ZA CR GH JK YF VT IA HI NZ DT
Suppose the above sequence is to construct each of the following data structures 16 locations (labeled 0 through 15), based on the hash function given above.
Show the resulting structure by filling in the following tables:
(If collision resolution does not find a location for an element,
explain the trouble, omit that element, and go on to insert any
remaining elements.)
Closed, unbucketed hash table using linear probing |
Closed, unbucketed hash table using quadratic probing | Open, bucketed hash table (with chaining) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
created December, 2021 revised December-January 2021 revised and expanded July 29, 2022 minor editing October 20, 2022 adjustments of problem 4 for C and C++ October 24, 2022 revised December 31, 2022–January 3, 2023 revised Summer, 2023 revised December 1-2, 2024 |
|
For more information, please contact Henry M. Walker at walker@cs.grinnell.edu. |
Copyright © 2011-2025
by Henry M. Walker.
|