Assignment 10
Power Computing
Due Friday, May 2, 1998
60 points
The purpose of this assignment is to exercise analytic complexity and measurement techniques in a simple example, computing powers of a number. We will also see how such derived results are dependent on assumptions about times of basic operations. This assignment requires mostly analysis; the code is written for you.
In /cs/cs60/c++/powers.cc is the C++ code for two functions for computing powers of a number: slow_power(k, n) computes k to the n-th power in the most obvious way, multiplying k by itself n times; fast_power computes the same function, but uses the "Russian peasant's" algorithm, repeatedly squaring k (to get powers which are powers of 2) and multiplying selected ones of those powers together to get the desired power. The program has already been compiled. The executable is in /cs/cs60/bin/powers. The result of a typical execution on turing is in /cs/cs60/ht/examples/powers.out.
For this assignment we are going to concentrate on computing powers of a fixed number, 7. This removes one degree of freedom. The number of decimal digits in the power is thus directly proportional to the power itself. The function on which we are focused is then T(n), the time required to compute 7n.
We are going to try each function on two different numeric types: double and Integer (not int, and not the same as the Java Integer). Integer multiplication preserves all of the digits in the answer, whereas double truncates the digits to a fixed precision. The assumption of constant multiplication time thus may not be valid for the Integer version.
The program sets up runs for four cases in all:
{fast, slow} x {double, Integer}
Here is an example of the output for one of the four cases is shown for a sample program:
Using doubles slow time/ n 1 log n n n log n n*n reps 128 2.00e-05 4.12e-06 1.56e-07 3.22e-08 1.22e-09 1000 256 5.00e-05 9.02e-06 1.95e-07 3.52e-08 7.63e-10 1000 512 1.10e-04 1.76e-05 2.15e-07 3.44e-08 4.20e-10 1000 1024 2.10e-04 3.03e-05 2.05e-07 2.96e-08 2.00e-10 1000 2048 4.10e-04 5.38e-05 2.00e-07 2.63e-08 9.78e-11 1000 4096 8.30e-04 9.98e-05 2.03e-07 2.44e-08 4.95e-11 1000The first column indicates the exponent and the second the time the power function takes on that exponent. The other columns are the second column divided by the analytic expressions indicated. reps indicates the number of repetitions used to get within the timer's resolution.
Your submission will consist of an analysis of the two programs under two sets of assumptions, and an empirical justification of your analysis.
(Extra credit) Program a way to multiply Integers faster than the one provided in class Integer and show that it really works. I have in mind something like the Karatsuba algorithm, which multiplies two Integers of length n in time n1.5, although this is not the fastest possible.(Note: You are not restricted to the run-time functions shown in the example if you want to modify the code.)
Below is the code for the two functions. Since these use templates, the type Number will automatically be instantiated by the compiler to either double or Integer depending on which type of variable we give it.
// compute k to the power n using the obvious method template <class Number> Number slow_power(Number k, long n) { Number p = 1; while( n-- > 0 ) // loop invariant: p is k to the power n0-n p = p * k; // where n0 is the original value of n return p; } // compute k to the power n using the Russian peasant's method template <class Number> Number fast_power(Number k, long n) { Number p = 1; while( n > 0 ) // loop invariant: p is k to the power N { // where N is the product of the low-order I if( n % 2 == 1 ) // powers of 2 in the binary expansion of n0, with p = p * k; // I being the number of iterations of the loop so far n = n/2; if( n == 0 ) // avoid computing k*k if done now break; k = k*k; } return p; }