# CMPSCI 311: Theory of Algorithms

### David Mix Barrington

### Fall, 2003

### Discussion Notes #4

### from Wednesday 1 Oct 2003

### Multiplying Large Integers (Levitin 4.5)

Please answer the questions *during the discussion period*.

Java provides a `BigInteger`

class for storing integers too long
for an `int`

or `long`

. Here we will look at the
complexity of
addition and multiplication of `BigInteger`

objects, and determine
whether the unusual multiplication algorithm of Levitin section 4.5 is
useful in this context.

We'll assume that a (non-negative)
`BigInteger`

is written in the form
"the sum for i from 0 to n of b_{i}r^{i}, where r is a
base such as `MAXINT+1`

. Each
b_{i} is kept in an `int`

variable, and these
`int`

variables are kept in some sort of list. We can get
b_{i} from b, for example, in O(1) time by the call
`b.getTerm(i)`

, and change b_{i} to x in O(1) time by
`b.setTerm(i,x)`

.

**Question 1:**
Here are my sketched versions of two methods from the
`BigInteger`

class:
```
BigInteger add (BigInteger c)
{// returns sum of this and c
int n = max(this.size, c.size);
BigInteger result = new BigInteger(n+1);
int carryBit = 0;
for (int i=0; i < n; i++) {
result.setTerm(i, this.getTerm(i) + c.getTerm(i) + carryBit);
carryBit = addCarry(this.getTerm(i), c.getTerm(i), carryBit);}
result.setTerm(n, carryBit);
return result;}
BigInteger multiply (int x)
{// returns this times x
int n = this.size;
BigInteger result = new BigInteger(n+1);
int carryInt = 0;
for (int i=0; i < n; i++) {
result.setTerm(i, this.getTerm(i) * x + carryInt);
carryInt = multCarry(this.getTerm(i), x, carryInt);}
result.setTerm(n,carryInt);
return result;}
```

Argue that each of these methods takes $O(n)$ time.

**Question 2:**
Write Java pseudocode using the `add`

and `multiply`

methods
above to implement `BigInteger multiply (BigInteger c)`

using the
ordinary algorithm for multiplication. Analyze the running time.
**Question 3:**
Write Java pseudocode to reimplement `multiply`

utilizing the trick from section 4.5, where it
is observed that if a = a_{1}r^{n/2} + a_{0} and
b = b_{1}r^{n/2} + b_{0}, then ab =
(a_{1}b_{1})r^{n} +
((a_{1} + a_{0})(b_{1} + b_{0}) -
a_{1}b_{1} - a_{0}b_{0})r^{n/2} +
(a_{0}b_{0}).
Assume you also
have a method `shift`

in the `BigInteger`

class, taking an `int`

argument, such that `b.shift(i)`

is b times r^{i} if i is positive and b divided by r^{i}
(with no remainder) if i is negative. There is also a method `tail`

such that `b.tail(i)`

gives the remainder when b is divided by
r^{i}. Each of these methods takes Θ(i) time.

Analyze the running time of your method.

Last modified 2 October 2003