RHEINISCH- WESTFÄLISCHE TECHNISCHE HOCHSCHULE AACHEN LEHR- UND FORSCHUNGSGEBIET INFORMATIK II RWTH Aachen D-52056 Aachen GERMANY http://programmierung.informatik.rwth-aachen.de LuFG Informatik II Prof. Dr. Jürgen Giesl Peter Schneider-Kamp, René Thiemann, Thomas Weiler Übung Informatik I - Programmierung - Blatt 7 (Lösungsvorschlag) Aufgabe 1 b) Stelligkeit: 419 c) ack(1, 100), ack(2, 100), ack(3, 8..15), ack(4, 0..1) d) Bit-Shifting: 2ms, wiederholtes Quadrieren: 1835ms, Einfache Iteration: 59585ms Aus ack(4, 2) = 2 10 19728 ergibt sich log 10 (ack(4, 3)) = log 10 (ack(3, ack(4, 2))) = log10 (2 2 1019728 ) = log 10 (2) 2 10 19728 = 6 10 19727 Folglich hat ack(4, 3) ungefähr 6 10 19727 viele Stellen. Fibonacci import java. math. B i g I n t e g e r ; * Implementation of the Fibonacci function with precise * numbers ( <code >java. math. BigInteger </ code >). The class provides a slow and a fast routine. * * @author thiemann * @version 28.11.2003 1
public class Fib { private f i n a l static B i g I n t e g e r zero = B i g I n t e g e r.zero; private f i n a l static B i g I n t e g e r one = B i g I n t e g e r.one; * Computes the Fibonacci function in the naive way * @param n the input value as integer * @return the precise value of fib(n) public static B i g I n t e g e r f i b S i m p l e ( int n ) { i f ( n <= 0) return zero ; i f ( n == 1) return one ; return ( f i b S i m p l e (n 1). add ( f i b S i m p l e (n 2) ) ) ; * Computes the Fibonacci function in time linear to n * @param n the input value as integer * @return the precise value of fib(n) public static B i g I n t e g e r f i b F a s t ( int n ) { return f i b F a s t 2 ( n, zero, one ) ; * idea: * the first parameter stores the number of necessary iterations * the second parameter is fib(i) * the third one is fib(i+1) private static B i g I n t e g e r f i b F a s t 2 ( int n, B i g I n t e g e r f i b i, B i g I n t e g e r f i b i p l u s 1 ) { i f ( n <= 0) return f i b i ; return f i b F a s t 2 ( n 1, f i b i p l u s 1, f i b i. add ( f i b i p l u s 1 ) ) ; Ackermann import java. math. B i g I n t e g e r ; 2
* The Ack class computes in various ways the Ackermann function * call the main routine with the specific calculation method as * a simple interface. ( the different calculation methods have very different * time and space consumption) * <p> * Beware of values ack(x,y) with x >= 5 or with x=4 and y >=3 * * @author thiemann * @version 1.0 (28.11.2003) public class Ack { * start simple interface asking for values x and y and * then computes and outputs ack(x,y); * additionally the computation time is displayed * @param args There should be passed one argument: the method. * Valid methods ( from slow to fast) are " recursive", " iterative ", "iterativenolist", "direct" public static void main ( S t r i n g [ ] args ) { i f ( args. length! = 1 ) { System. e r r. p r i n t l n ( "Please specify exact one method" ) ; return ; S t r i n g method = args [ 0 ] ; S t r i n g [ ] methods = {"recursive", "iterative", "iterativenolist", " direct" ; for ( int i = 0 ; i < methods. length ; i ++) { i f ( method. e q u a l s ( I n t e g e r. t o S t r i n g ( i +1)) method. e q u a l s ( methods [ i ] ) ) { System. out. p r i n t l n ( "Method: "+methods [ i ] ) ; System. out. p r i n t ( "x: " ) ; int x = IO. readint ( ) ; System. out. p r i n t ( "y: " ) ; int y = IO. readint ( ) ; int a ; B i g I n t e g e r biga ; long time ; time = System. c u r r e n t T i m e M i l l i s ( ) ; switch ( i ) { case 0 : a = ack ( x, y ) ; time += System. c u r r e n t T i m e M i l l i s ( ) ; System. out. p r i n t l n ( a ) ; break ; 3
case 1 : a = a c k i t e r ( x, y ) ; time += System. c u r r e n t T i m e M i l l i s ( ) ; System. out. p r i n t l n ( a ) ; break ; case 2 : a = a c k i t e r 2 ( x, y ) ; time += System. c u r r e n t T i m e M i l l i s ( ) ; System. out. p r i n t l n ( a ) ; break ; case 3 : biga = a c k d i r e c t ( x, y ) ; time += System. c u r r e n t T i m e M i l l i s ( ) ; System. out. p r i n t l n ( biga ) ; int length = biga. t o S t r i n g ( ). length ( ) ; i f ( length > 80) { System. out. p r i n t l n ( "Length: " + length ) ; System. out. p r i n t l n ( "Time: "+time+"ms" ) ; return ; System. e r r. p r i n t l n ( "Wrong method! Please choose one of \n" ) ; for ( int i = 0 ; i < methods. length ; i ++) { System. e r r. p r i n t l n ( i+1+") "+methods [ i ] ) ; * computes the Ackermann function ack(x,y) in a recursive way * @param x the first argument to ack * @param y the second argument to ack * @return the value of ack(x,y) as integer public static int ack ( int x, int y ) { i f ( x == 0) return ( y+1) ; i f ( y == 0) return ack ( x 1,1) ; return ack ( x 1, ack ( x, y 1) ) ; * computes the Ackermann function ack(x,y) in an iterative way; * here we use lists to store all the values of ack * ( ack(x,y) space consumption) * @param x the first argument to ack * @param y the second argument to ack * @return the value of ack(x,y) as integer 4
public static int a c k i t e r ( int x, int y ) { // initialize fields and lists int [ ] ack want = new int [ x +1]; DoubleLinkedList [ ] v a l u e s = new DoubleLinkedList [ x +1]; I n t e g e r t e s t ; for ( int i x =0; i x <= x ; i x++) { ack want [ i x ] = 1; v a l u e s [ i x ] = new DoubleLinkedList ( ) ; ack want [ x ] = y ; // initialize loop int i x = x ; int i y = y ; // outer loop for x i x l o o p : while ( i x <= x ) { // inner loop for y i y l o o p : while ( v a l u e s [ i x ]. length ( ) <= ack want [ i x ] ) { i y = v a l u e s [ i x ]. length ( ) ; // case 1 i f ( i x == 0) { v a l u e s [ i x ]. addend ( i y + 1) ; continue ; // case 2 i f ( i y == 0) { t e s t = v a l u e s [ ix 1]. getpos ( 1 ) ; i f ( t e s t! = null ) { // value present v a l u e s [ i x ]. addend ( t e s t. intvalue ( ) ) ; continue ; ack want [ ix 1] = 1; i x = 1; continue i x l o o p ; 5
// case 3 // we know by construction that a(ix,y-1) is present int ay 1 = v a l u e s [ i x ]. getpos ( iy 1). intvalue ( ) ; t e s t = v a l u e s [ ix 1]. getpos ( ay 1 ) ; i f ( t e s t! = null ) { // value present v a l u e s [ i x ]. addend ( t e s t. intvalue ( ) ) ; continue ; // enforce computation of a(ix-1, ay_1) ack want [ ix 1] = ay 1 ; i x = 1; continue i x l o o p ; // so current ix level completed i x += 1; continue ; return v a l u e s [ x ]. getpos ( y ). intvalue ( ) ; * computes the Ackermann function ack(x,y) in an iterative way; * here we only store the last computed values a(x,i) for each x * ( linear space consumption in x) * @param x the first argument to ack * @param y the second argument to ack * @return the value of ack(x,y) as integer public static int a c k i t e r 2 ( int x, int y ) { int [ ] ack want = new int [ x +1]; int [ ] ack have = new int [ x +1]; int [ ] a c k l a s t = new int [ x +1]; for ( int i x =0; i x <= x ; i x++) { ack want [ i x ] = 1; ack have [ i x ] = 1; a c k l a s t [ i x ] = 1; int i x = x ; int i y = y ; ack want [ x ] = y ; i x l o o p : while ( i x <= x ) { i y l o o p : while ( ack have [ i x ] < ack want [ i x ] ) { 6
i y = ack have [ i x ] + 1 ; // case 1 i f ( i x == 0) { a c k l a s t [ i x ] = i y + 1 ; ack have [ i x ] = i y ; continue ; // case 2 i f ( i y == 0) { i f ( ack have [ ix 1] >= 1) { // value present a c k l a s t [ i x ] = ack have [ i x ] = i y ; continue ; ack want [ ix 1] = 1; i x = 1; continue i x l o o p ; a c k l a s t [ ix 1]; // case 3 // we know by construction that a(ix,y-1) is present and is stored in ack_last[ix] int ay 1 = a c k l a s t [ i x ] ; i f ( ack have [ ix 1] >= ay 1 ) { // value present a c k l a s t [ i x ] = a c k l a s t [ ix 1]; ack have [ i x ] = i y ; continue ; // enforce computation of a(ix-1, ay_1) ack want [ ix 1] = ay 1 ; i x = 1; continue i x l o o p ; // so current ix level completed i x += 1; continue ; return a c k l a s t [ x ] ; * Easy access to BigIntegers 7
private static B i g I n t e g e r tobig ( int i ) { return new B i g I n t e g e r ( I n t e g e r. t o S t r i n g ( i ) ) ; private f i n a l static B i g I n t e g e r zero = tobig ( 0 ) ; private f i n a l static B i g I n t e g e r one = tobig ( 1 ) ; private f i n a l static B i g I n t e g e r two = tobig ( 2 ) ; private f i n a l static B i g I n t e g e r t h r e e = tobig ( 3 ) ; private f i n a l static B i g I n t e g e r maxint = tobig ( I n t e g e r.max VALUE) ; * Exponentation ( simple version) private static B i g I n t e g e r pow2simple ( B i g I n t e g e r x ) { B i g I n t e g e r r e s = one ; while ( x. compareto ( zero ) > 0) { r e s = r e s. multiply ( two ) ; x = x. s u b t r a c t ( one ) ; return r e s ; * Exponentation with repeated squaring * without bit- shifting operations private static B i g I n t e g e r pow2square ( B i g I n t e g e r x ) { B i g I n t e g e r r e s = one ; B i g I n t e g e r base = two ; while ( x. compareto ( zero ) > 0) { i f ( x. and ( one ). e q u a l s ( one ) ) { r e s = r e s. multiply ( base ) ; base = base. multiply ( base ) ; x = x. d i v i d e ( two ) ; return r e s ; * Exponentation with base 2 by bit- shifting private static B i g I n t e g e r pow2shift ( B i g I n t e g e r x ) { B i g I n t e g e r erg = one ; while ( x. compareto ( maxint ) > 0) { 8
erg = erg. s h i f t L e f t ( I n t e g e r.max VALUE) ; x = x. s u b t r a c t ( maxint ) ; erg = erg. s h i f t L e f t ( x. intvalue ( ) ) ; return erg ; * Choose method for potentiation private static B i g I n t e g e r pow2( B i g I n t e g e r x ) { // return pow2simple(x); // return pow2square(x); return pow2shift ( x ) ; * Computing towers of 2 s private static B i g I n t e g e r tower2 ( int h ) { B i g I n t e g e r r e s = one ; while ( h > 0) { r e s = pow2( r e s ) ; h ; return r e s ; * computes the Ackermann function ack(x,y) in an explicit way * @param x the first argument to ack * @param y the second argument to ack * @return the value of ack(x,y) as BigInteger public static B i g I n t e g e r a c k d i r e c t ( int x, int y ) { i f ( x == 5 && y == 0) { x = 4 ; y = 1 ; i f ( x == 0) return tobig ( y+1) ; i f ( x == 1) return tobig ( y+2) ; i f ( x == 2) return tobig (2 y+3) ; i f ( x == 3) return pow2( tobig ( y+3)). s u b t r a c t ( t h r e e ) ; i f ( x == 4 && y <= 2) return tower2 ( y+3). s u b t r a c t ( t h r e e ) ; 9
// all other values are to big to be computed System. e r r. p r i n t l n ( "Computing..." ) ; for ( int i = 0 ; i < 20000; i++) { System. e r r. p r i n t ( "." ) ; System. e r r. p r i n t l n ( "\n1. Buy me much more memory" ) ; System. e r r. p r i n t l n ( "2. Buy me a very fast processor" ) ; System. e r r. p r i n t l n ( "3. Attach a display of at least 18*10^19727 * 8 pixel to display the number" ) ; System. e r r. p r i n t l n ( " (If we have 10 pixels per mm, then the width is only 6*10^19701 million light years)" ) ; System. e r r. p r i n t l n ( "4. Wait for output and take your time to read it" ) ; System. e r r. p r i n t l n ( "\notherwise I give up here," ) ; System. e r r. p r i n t l n ( "Your computer" ) ; System. e x i t ( 0 ) ; return null ; Aufgabe 2 * Double- linked list of integers with constant access * functions on both ends and constant length computation. * Direct access to elements in the middle costs at most * length/2 iterations. * Duplicates are allowed. * The implementation bases on the <code >Elem </ code > * data structure. * * @author thiemann * @version 1.12.2003 * @see Elem public class DoubleLinkedList { private Elem head ; // head of the list; null, if list is empty private Elem end ; // end of the list; null, if list is empty private int length ; // the current list-length 10
* Standard- Representation ( linear time) * @return a String representing the list from head to end public S t r i n g t o S t r i n g ( ) { return t o S t r i n g ( this. head, f a l s e ) ; * Standard- Representation of the reversed list ( linear time) * @return a String representing the list from end to head public S t r i n g t o S t r i n g R e v e r s e ( ) { return t o S t r i n g ( this. head, true ) ; * produces a string representing the list beginning at * the given parameter " e" in given direction private S t r i n g t o S t r i n g ( Elem e, boolean r e v e r s e ) { i f ( e == null ) { return "" ; else { int v = e. getvalue ( ) ; i f ( r e v e r s e ) { return ( t o S t r i n g ( e. getright ( ), r e v e r s e )+" "+v ) ; else { return ( v+" "+t o S t r i n g ( e. getright ( ), r e v e r s e ) ) ; * Computes the list - length ( in constant time) * @return the list length public int length ( ) { return this. length ; 11
* Creates an empty list public DoubleLinkedList ( ) { this. head = null ; this. end = null ; this. length = 0 ; * computes the value of the * first element of the list, if this exists ( constant time) * @return null, if the list is empty; the first element otherwise public I n t e g e r gethead ( ) { return getpos ( 0 ) ; * computes the value of the last element of the list, * if this exists ( constant time) * @return null, if the list is empty; the last element otherwise public I n t e g e r getend ( ) { return getpos ( this. length 1) ; * computes the value of the element at position p of the list. Here, we * start counting from 0, so a valid range for p is 0 <= p < length * ( linear time, but at most length/2 iterations) * @return null, if the position is out of bounds, or otherwise * the element at the given position public I n t e g e r getpos ( int p ) { Elem e = getpospriv ( p) ; i f ( e == null ) { return null ; return new I n t e g e r ( e. getvalue ( ) ) ; 12
* Adds the value v in front of the list ( in constant time) * @param v the integer to be added public void addhead( int v ) { Elem e = new Elem( v, null, head ) ; i f ( this. head! = null ) { // non-empty list this. head. s e t L e f t ( e ) ; this. head = e ; else { // empty list head = e ; end = e ; this. length ++; * Adds the value v at the end of the list ( in constant time) * @param v the integer to be added public void addend ( int v ) { Elem e = new Elem( v, end, null ) ; i f ( this. end! = null ) { // non-empty list this. end. setright ( e ) ; this. end = e ; else { // empty list this. head = e ; this. end = e ; this. length ++; * adds the value v before element at position p; * to store an element at the end, p should be length; * look at <code >getpos </ code > for more information * about positions. * * @param v the value to be inserted 13
* @param p insert before this element; 0 <= p <= length * @return true, if v was inserted; false, otherwise public boolean addpos ( int v, int p ) { // check for valid position i f ( p < 0 p > this. length ) { return f a l s e ; // deal with special cases i f ( p == 0) { addhead( v ) ; else i f ( p == this. length ) { addend ( v ) ; else { // now, we have to add the element in the middle // so, no update of head or end has to be done! // catch the right element Elem curr = getpospriv ( p 1) ; // and update all pointers from the elements Elem e = new Elem( v, curr, curr. getright ( ) ) ; curr. setright ( e ) ; e. getright ( ). s e t L e f t ( e ) ; this. length ++; return true ; * get the element at position p starting from the best side * null, if p is not valid private Elem getpospriv ( int p ) { // position valid? i f ( p < 0 p >= this. length ) return null ; // which way boolean forward = this. length p > p ; // start iteration i f ( forward ) { return DoubleLinkedList. getpos (p, forward, this. head ) ; else { return DoubleLinkedList. getpos ( this. length p 1, forward, this. end ) ; 14
* walks count steps from current Element in direction * and returns the element at this position private static Elem getpos ( int count, boolean forward, Elem c u r r e n t ) { i f ( count == 0) { return c u r r e n t ; else i f ( forward ) { return getpos ( count 1, forward, c u r r e n t. getright ( ) ) ; else { return getpos ( count 1, forward, c u r r e n t. g e t L e f t ( ) ) ; * searches for the value v in forward direction, or * backward direction, if " forward" = false private Elem search ( int v, boolean forward ) { i f ( forward ) { return DoubleLinkedList. searchf ( v, this. head ) ; else { return DoubleLinkedList. searchb ( v, this. end ) ; * searches for v beginning from current in forward direction private static Elem searchf ( int v, Elem c u r r e n t ) { // value found? i f ( c u r r e n t. getvalue ( ) == v ) { return c u r r e n t ; // reached end? else i f ( c u r r e n t. getright ( )! = null ) { // no, then iterate return searchf ( v, c u r r e n t. getright ( ) ) ; else { 15
// yes, then the value is not present return null ; * searches for v beginning from current in backward direction private static Elem searchb ( int v, Elem c u r r e n t ) { // value found? i f ( c u r r e n t. getvalue ( ) == v ) { return c u r r e n t ; // reached end? else i f ( c u r r e n t. g e t L e f t ( )! = null ) { // iterate return searchb ( v, c u r r e n t. g e t L e f t ( ) ) ; else { // failure return null ; * deletes the element e from the list private boolean d e l e t e ( Elem e ) { // delete null- element can not be done i f ( e == null ) return f a l s e ; // update with left neighbor // special case head- element i f ( e == this. head ) { this. head = e. getright ( ) ; // test, if head was not the only element i f ( this. head! = null ) { this. head. s e t L e f t ( null ) ; else { // usual left element e. g e t L e f t ( ). setright ( e. getright ( ) ) ; // update with right neighbor 16
// special case end- element i f ( e == this. end ) { this. end = e. g e t L e f t ( ) ; // test, if end was not the only element i f ( this. end! = null ) { this. end. setright ( null ) ; else { // usual right element e. getright ( ). s e t L e f t ( e. g e t L e f t ( ) ) ; this. length ; return true ; * Deletes the head of the list * (constant time) * @return true, if there was a head ( if list was not empty); * false, otherwise public boolean deletehead ( ) { return d e l e t e ( this. head ) ; * Deletes the end of the list * (constant time) * @return true, if there was a end ( if list was not empty); * false, otherwise public boolean deleteend ( ) { return d e l e t e ( this. end ) ; * Deletes the first occurence of the value v in the list * (linear time) * @param v the value to be deleted * @return true, iff something was deleted public boolean d e l e t e F i r s t ( int v ) { Elem e = search ( v, true ) ; 17
return d e l e t e ( e ) ; * Deletes the last occurence of the value v in the list * (linear time) * @param v the value to be deleted * @return true, iff something was deleted public boolean d e l e t e L a s t ( int v ) { Elem e = search ( v, f a l s e ) ; return d e l e t e ( e ) ; * deletes all elements with value v in the list * (linear time) * @param v the value to be deleted * @return the number of elements that have been deleted public int d e l e t e A l l ( int v ) { return d e l e t e A l l ( v, this. head ) ; * delete all elements that have value v * beginning from current, iterating in forward * direction * current must be non- null! private int d e l e t e A l l ( int v, Elem c u r r e n t ) { i f ( c u r r e n t == null ) return 0 ; // fetch next element v c u r r e n t = DoubleLinkedList. searchf ( v, c u r r e n t ) ; // found something? i f ( c u r r e n t! = null ) { // yes, then delete and look further d e l e t e ( c u r r e n t ) ; return (1+ d e l e t e A l l ( v, c u r r e n t. getright ( ) ) ) ; // no, then abort 18
return 0 ; * tests, whether the value v is stored in the list * @param v the test value * @return true, iff v was in the list public boolean elem ( int v ) { // direction does not matter Elem e = search ( v, true ) ; return ( e! = null ) ; 19