CS 1501
Data
Structures and Algorithms
Programming
Project 1
The purpose of this assignment
is to empirically compare a production very-long-integer multiplication
algorithm such as that in the NTL library with one that you write yourself. You
will time both algorithms for various integer sizes (i.e. numbers of
bits/digits) and see how the run-times compare.
The ZZ multiplication
algorithm from the NTL library is a hybrid algorithm – it uses the Gradeschool
algorithm ("classical" algorithm as described in NTL) until a certain
integer size, and then crosses over to the Karatsuba algorithm. Some details about how integers are stored
and manipulated in the NTL library can be found at this link. For better space (and perhaps time)
efficiency, NTL ZZ values are stored and manipulated in binary form.
As we discussed in
class, we could alternatively store very-long-integers (VLIs) as arrays of
digits, with each array location corresponding to a single base-ten digit of
the number (we will assume no negative numbers are allowed). The advantage of
this method is easier understanding and manipulation by the programmer, with
the cost being more space (and perhaps more time).
Your assignment
consists of two parts:
1) Implement
your own VLI class in C++, including all of the operations that the integers
would need. Minimally these operations
should include:
·
A constructor that takes a character string of
digits and produces a VLI from it
·
Various other constructors, destructors and
assignment operators as necessary (your data must be stored in some type of
dynamic array, sized to the exact number of digits of the integer)
·
Overloaded input and output operators (>>
and <<)
·
Addition operator (+)
·
Multiplication (*) using the Gradeschool
algorithm
To demonstrate that your VLI class works properly, you MUST write a
simple driver program that tests each of the operations for your class. The TA should be able to execute this
program to see that each of your operations is correct. Call this program assig1a.cpp.
2) Time
your multiplication algorithms with both ZZ integers and your VLIs in the
following way:
·
Generate random strings of digits (as
characters) and for each string generate both a ZZ and a VLI using the
appropriate constructors/conversion functions (i.e. you should use the SAME
VALUES for both algorithms). You should generate strings with the following
numbers of decimal digits: 20, 40, 80, 160, 320, … continuing until it takes at
least 1 minute to do the multiplication (as timed below) for the algorithm in
question.
·
Time both of your multiplication algorithms for
each of the random integers as described below:
1)
Generate the next pair of random values (one for
each operand)
2)
Start the system clock
3)
Execute the multiplication algorithm
4)
Stop the system clock
5)
Calculate the elapsed time
6)
If the number of digits is <= 40, output the
answer (the product) calculated by the algorithm in a clearly labeled way (this
is for grading correctness of your multiplication algorithm – the answer
produced by the NTL multiplication and by your VLI multiplication should be the
same)
·
The timing will differ depending on which C++
environment you use. See documentation
for your environment to help you with the timing.
·
Store/save the (digits, elapsed time) pairs for
both of the multiplication algorithms for each digit value used (remember that
the max value for digits may be different for different algorithms). For the smaller numbers of digits, you may
get results of 0 -- this is ok as long as you eventually get significant values
for the larger numbers of digits.
·
Run your program 10 times and average the
results for each algorithm.
·
Call this program assig1b.cpp.
BE SURE TO DO ALL OF
YOUR RUNS on the SAME COMPUTER under the SAME CIRCUMSTANCES. Timing on Windows NT machines may not be
very accurate but if you try to have the same conditions for all runs the
relative times will still be somewhat meaningful. Also, DO NOT do anything in the background while your program is
running -- this will affect your results!
Clearly, an important
part of this assignment is installing/figuring out how to use the NTL Library.
If you do not have a PC, and do not want to try using Unix, I suggest buying a
ZIP disk and installing the library onto the ZIP disk (many campus computing
labs have ZIP drives). Then you can
take your library with you to any campus PC that has a ZIP drive.
The Gradeschool
algorithm that you will use in your VLI class is not exactly the way you
learned it in gradeschool. If we had to
calculate each of the partial products and store it before adding, it would
waste too much memory (Theta(D2) memory would be needed, where D is
the number of decimal digits). Instead
we can add while we are multiplying if we calculate the answer in a column-wise
rather than a row-wise fashion. The
pseudo-code below should show how this is done.
|
function Mult (X[0..n-1], Y[0..n-1]) returns Z[0..2n-1] // note:
X and Y have n digits Z has UP TO (2n) digits // The LEAST
significant digits (smaller powers of ten) //
stored in the smaller indices long S = 0 for j
from 0 to
(2n-1) do // go through digits of result
for i from
0 to (n-1) do // digits of X
if (0 <= (j-i) <=
(n-1)) then
S = S + X[i] * Y[j-i]
Z[j] = S
mod 10 // remainder
S = S / 10 //
integer division return Z end function |
Be careful when
implementing this algorithm. Try a few
simple examples with a pencil and paper before coding it. Also make sure you don't violate any of the
array bounds. Since the final length of
Z is not certain until the multiplication is complete, you can define a
temporary array of length 2n to store the result, then create an exact length
VLI to return when finished. Remember
that it is VITALLY IMPORTANT that
your VLI class functions correctly in all aspects. If your VLI class does NOT work correctly (and, in particular,
does not produce the correct answer to the multiplication) you will lose much
of the credit for it.
Once you have
completed your algorithm runs, compile a table of your averaged results and
make a graph for each of the algorithms.
Use the digits (n value) as your x-coordinate and the average run-time
as your y-coordinate.
Important
Submission Note: You must submit 2 programs for this assignment, one that demonstrates
your VLI class and one that does the simulation/timing for the comparison
results. For EACH of these programs,
you MUST submit both your SOURCE code AND your EXECUTABLE code on a disk (or in
an AFS directory if you did it using g++).
The executables that you submit MUST EXECUTE INDEPENDENT of any software
you used or PC configuration used to develop them. In other words, the TA should be able to put your disk into ANY
WIN32 machine and your programs should execute correctly. If you have ANY DOUBT about this with your
programs, TEST your executables on various, differently configured PCs BEFORE
handing them in.
Write a short (~ 2
double-space (NOT HAND-WRITTEN)
pages) paper discussing your program and results thoroughly. Be SURE to address the following
questions:
·
Program: How did you set up your main program?
Which functions in NTL did you utilize in addition to the multiplication and
why? How did you do your timing? What (if any) obstacles did you have to
overcome? Did all 10 runs give similar
results? If not, speculate as to why
not.
·
Results: Which of the algorithms was faster and
why? Did the difference in the
run-times seem to be a constant factor throughout all of the numbers of
digits? Speculate as to why or why
not. What could you have done to
improve the run-time of your algorithm?
From your results does it seem that the added complexity of the NTL code
is worth it?
(Difficult) Extra
Credit Idea: Also implement the
Karatsuba algorithm for your VLI class and test it along with the Gradeschool
algorithm.