v22.0310-003: Homework 4

Due date: Wed November 10, 1999.

This homework has a programming part and a written part. The programming part should be submitted as usual by email. The written part must be handed in either to me, OR to the TA. [Please do not just stuff in our mail boxes without asking first]


Look here for additional information after the original posting of assignment:
  1. Problem 7.1 (page 219) in Text.
    This is a proof by induction of the maximum number of edges in a graph.
    To get full credit, the proof should be logically perfect and every sentence should be an Englist statement. So the whole proof should read like an essay. You should probably rewrite your proof several times until you get it right.

  2. Problem 7.4 (page 220) in Text.
    [Example of Depth First Search]

  3. Problem 7.5 (page 221) in Text.
    [Example of Breadth First Search]

  4. Programming Question.
    The sizes of the primitive datatypes like int and char are fixed by the java standard (32 bits for int and 16 bits for char) and cannot be increased. However for many applications we need to represent very large numbers. One such application is in cryptography, where we need to do integer arithmetic on numbers with large number of bits: reaching upto 1024 for some current systems, with the number of bits liable to increase in the future as larger and larger keys become practically "crackable" by brute force search.

    For applications like these, java provides the class BigInteger, which has instance methods for such common operations as add, subtract, multiply and divide (this is integer division, leaving a quotient); and also operations like gcd and bitwise operations like and, or, xor and complement. It also provides such useful functions as left and right shifts; and constructors to get a BigInteger from an integer, and also to generate a random BigInteger with some specified number of bits; and even some random BigInteger which is "probably" prime. These are very useful in the cryptographic application mentioned above.

    Remember, in order to represent something like an arbitrary sized integer, the internal representation is probably some kind of vector of bits. Thus each of the basic operations mentioned above are not primitive operations which the machine provides, but built up from bitwise operations. You could think of this as the software implementation of what your hardware does for regular integers.

    Addition is fairly straightforward: You start at the least significant bit; add the bits together, calculate the output bit and the carry; and go on to the next bit. This algorithm is of the order Theta(n).

    Multiplication is more complicated. However, the basic structure of the multiplication algorithm can be expressed in a recursive fashion by using a divide and conquer algorithm. This splits up the two operands in equal halves, recursively multiplying them and combining the results to get the final product.

    Suppose you want to compute Z = XY where X and Y are both 2n-bit binary numbers. We can view X as follows:

    	X = (X1 . 2n) + X0,        Y = (Y1 . 2n) + Y0
    
    where the X0, X1, Y0, Y1 are each n bit numbers. After multiplying out, we get the product as:
    	Z   =  (X1.Y1).22n  + (X1.Y0 + X0.Y1).2n + (X0.Y0)
    	    =  Z2 . 22n     + Z1.2n              + Z0
    
    where
    
    	Z0  =  X0.Y0
    	Z1  =  X1.Y0 + X0.Y1
    	Z2  =  X1.Y1
    
    So, at each step there are 4 recursive calls to multiply two n-bit numbers. The multiplication of an integer by 2n is performed by shifting the bits n places to the left (so the time complexity is O(n)). The overhead to combine the results of these multiplications comes from performing some additions and the shifts, which are linear time. Thus, to find the asymptotic complexity of this algorithm has the following recurrence:
    	T(n) = 4 * T(n/2) + n
    
    The solution to this, as you can do from the previous classes and exercises is:
    	T(n) = Theta(n2)
    
    However, one can do better than this by doing three recursive multiplications instead of four, as Karatsuba showed. Instead of calculating Z1 using the two obvious products (X1.Y0 and X0.Y1) and then adding them together, we first compute the intermediate product each step:
    	T1  =  (X0 + X1).(Y0 + Y1)
    
    Then the product is:
    	Z1 = T1 - Z0 - Z2
    
    Once we have Z0, Z2, Z1 we can put them together to form Z in linear time. The basic recurrence is thus:
    	T(n) = 3 * T(n/2) +  n
    
    whose solution is
    	T(n) = Theta(nlg 3)
    
    as shown in one of the previous homework.

    Download and read the following "abstract" java program, mult.java. It is "abstract" because we omit the various bodies for methods and putting dummy bodies where we could not. [But you can call "javac" on it without any error.] We want you to implement the Karatsuba algorithm for integer multiplication.

    To write your own mult.java program, first remove all occurences of the key word "abstract" from the sample program, then fill in the appropriate code. Remove also any dummy code. The main method has already been written for you.



  5. RECOMMENDED EXTRA PROBLEMS: Do not hand in, as they will not be graded. But we will give solutions and discuss this. Please try them yourself first!
    • Ex 7.2 and 7.3 from page 220