The purpose of this assignment is for you to familiarize yourselves with different algorithms for integer and polynomial multiplication. Algorithms considered in this assignment include (a) the long (gradeschool) multiplication method, and (b) the basic divide-and-conquer method. Work on Karatsuba's method is also included as extra credit.
The source code of the programs polymult.cpp and polymult.java is provided. These are programs for multiplying 2 polynomials using the long multiplication algorithm. They first ask the user to enter a value N. They then either randomly (for N > 10) or by user input (for N <= 10) generate two polynomials of degree N-1. The programs then multiply these 2 polynomials. They finally print out the original and the resulting polynomials. The programs represent a polynomial of degree N-1 as an array of N elements. Element i in the array contains the coefficient of the monomial of degree i for i = 0,...,N-1. For example, 3+4x-5x2 can be represented as
int a[] = { 3, 4, -5 };
Note that a dynamically allocated array may also be used to represent a polynomial (see polymult programs).
Your assignment is to write a program called intmult.cpp or intmult.java with the following requirements:
1) Using a simple dynamic array of integers, represent arbitrary length non-negative integers rather than polynomials. Some fundamental differences include:
a) Since each value in the array represents a base-ten digit, it must be in the range [0,9]. Thus, when inputting the digits or randomly generating them, you must ensure that all digits are in the correct range.
b) Since integers are typically shown with the most significant digit on the left, you should print them in that way.
2) Implement long multiplication (gradeschool) of arbitrary length integers in a manner similar to the multiplication of the polynomials of the example programs. You can do this more or less by modifying subroutine polymult() in the example programs so that it multiplies "integers" in base 10 rather than "polynomials". To make the modification, you should incorporate some code to deal with carries (since base 10 integer digits can only have values between 0 and 9).
3) Implement the basic divide-and-conquer multiplication algorithm. This is the simple divide and conquer algorithm discussed in class (NOT Karatsuba's algorithm). The subroutine should have 4 recursive calls. If it helps, you may change the way parameters are passed to and returned. You may also want to create a subroutine for polynomial addition and for shifting. As a general guideline, a subroutine should not be longer than a page. If it is, break it down into smaller subroutines.
4) Verify the asymptotic running time of the multiplication algorithms from parts 2) and 3). We discussed in lecture that the asymptotic running time of both algorithms is Theta(n2). To verify this run-time you will test each algorithm in the following way:
a) Generate and time the multiplication of 5 randomly generated pairs of numbers for each of the following numbers of decimal digits: { 2000, 4000, 6000, 8000, 10000 }. Determine the average run-time per multiplication for each algorithm for each number of digits. Note that you should time only the multiplications – do not time the generation of the numbers that you are to multiply. Output your results to a file called intmult.out.
b) Clearly, you will need to incorporate into your program code for measuring the running time of a code segment. For C++ programs on Unix, you should use gethrtime(). For C++ programs on Windows, you should use clock(). For Java programs, you should use the Date class. See the man page on Unix, the help file on Windows or the Java API description for more details.
5) Allow the TA to test your algorithms in the following way: When your program begins, ask the user if he/she wants to show one multiplication or run the simulation. If the user elects to show one multiplication, prompt the user to input N, then input the N digits of each number. Then output the numbers and the product you produce for each algorithm.
6) Once you have completed and executed your program, write a report that addresses each of the following issues:
a) Explain the code you wrote for this assignment in a concise manner. For step 2, explain how you modified the code from the polymult program to get it to work for integers. For steps 2 and 3, explain how the parameters are passed to and returned from your subroutine. Does the caller or the callee allocate the space for the resulting integer? Briefly explain the use of any important temporary variable. In step 3, explain your base cases and how you handle values of N that are not even.
b) Address the issue of temporary space required for your divide and conquer algorithm. Is it possible to reduce the amount of temporary space that needs to be allocated? How does this effect the running time? (as a function of n, an additive constant, or a multiplicative constant?).
c) Show a graph of N values (X-axis) versus runtimes (Y-axis) for both multiplication algorithms. Discuss your results and indicate anything that you may not have expected. For large N, which algorithm will be faster? By how much? Include a brief argument and/or calculation to support your claim. For your graph, you may use a Excel or, if you prefer, you may do it by hand and scan it into a .jpg file.
7) Extra credit: Implement an integer multiplication subroutine using Karatsuba's algorithm, time it as you did with the other two algorithms, and discuss your implementation and results in the report.