Musterlösung zum 8. Aufgabenblatt vom Dienstag, den 02. Juni 2009 zur Vorlesung Informatik B von Jacob Krause 1. Rückgeldstückelung Schreiben Sie Pseudocode und ein Java Programm zur Bestimmung des Wechselgeldes bei einem Kaufvorgang. Eingabe sind zwei Zahlen: der Kaufbetrag und der vom Kunden dem Kassierer gegebene Betrag. Das Programm soll das Wechselgeld berechnen und dabei eine Zerlegung desselben in möglichst wenig Banknoten und Münzen ausgeben. Gehen Sie von der Euro-Stückelung aus. Lösung von Nils Krane, Christian Krumnow Pseudocode: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Sei geg der gegebene Betrag , z der zu zahlende i f ( geg mod 0.01 != 0) or ( z mod 0.01 != 0) then Abbruch und Hinweise : ungültige Eingabe diff = geg - z i f ( diff < 0) then Abbruch und Hinweis : zu wenig gezahlt ausgabe ( " Wechselgeld = " diff ) noten < -[500 ,200 ,100 ,50 ,20 ,10 ,5 ,2 ,1 ,0.50 ,0.2 ,0.1 ,0.05 ,0.02 ,0.01] Ausgabe ( " Ideale Zerlegung : " ) hilf <- 0 f o r i = 1 to 15 do hilf <- ( diff div noten [ i ]) i f hilf != 0 if i < 8 Ausgabe ( hilf noten [ i ] " - Euro Scheine " ) else Ausgabe ( hilf noten [ i ] " - Euro Münzen " ) diff <- ( diff mod noten [ i ]) Umsetzung: 1 import java . io .*; 2 p u b l i c c l a s s Wechselgeld { 3 p u b l i c s t a t i c void main ( String [] args ) { 4 System . out . println ( " * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * " ) ; 5 System . out . println ( " *** Aufgabe 1 " ) ; 6 System . out . println ( " * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * " ) ; 7 System . out . println () ; 8 BufferedReader buffer = new BufferedReader (new 9 10 Inpu tStrea mRead er ( System . in ) ) ; // B a n k n o t e n b e t r ä g e ∗ 100 um I n t e g e r − D i v i s i o n z u n u t z e n i n t [] banknoten = {50000 ,20000 ,10000 ,5000 ,2000 ,1000 ,500 ,200 ,100 ,50 ,20 ,10 ,5 ,2 ,1}; 11 try { 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 // E i n l e s e n d e r B e t r ä g e System . out . println ( " Zu zahlender Betrag : " ) ; String zuZahlenString = buffer . readLine () ; double zuZahlenDouble = Double . parseDouble ( zuZahlenString ) ; i n t zuZahlen = ( i n t ) ( zuZahlenDouble *100) ; System . out . println ( " Gegbener Betrag " ) ; String gegebenString = buffer . readLine () ; double gegebenDouble = Double . parseDouble ( gegebenString ) ; i n t gegeben = ( i n t ) ( gegebenDouble *100) ; // P r ü f e ob g e g e b e n e W e r t e G e l d b e t r ä g e n e n t s p r e c h e n k ö n n e n i f (( gegeben != gegebenDouble *100) || ( zuZahlen != zuZahlenDouble *100) ) { System . out . println ( " Ungueltige Eingabe ! " ) ; return ; } // B e r e c h n u n g d e s R ü c k g e l d e s i n t differenz = gegeben - zuZahlen ; // P r ü f e ob g e n u g g e z a h l t i f ( differenz < 0) { System . out . println ( " Gegebener Betrag zu gering ! " ) ; return ; } // B e g i n n e A u s g a b e System . out . println ( " Das Rueckgeld betraegt " +( differenz /100.0) + " Euro " ) ; System . out . println ( " " ) ; System . out . println ( " Zerlegung : " ) ; i n t hilf = 0; // S p e i c h e r t A n z a h l d e r momentan b e n ö t i g t e n S c h e i n e / Münzen f o r ( i n t i =0; i < banknoten . length ; i ++) { // I n t e g e r − D i v i s i o n e n t s p r i c h t dem d i v O p e r a t o r hilf = differenz / banknoten [ i ]; i f ( hilf != 0) { i f (i <7) System . out . println ( hilf + " " +( banknoten [ i ]/100) + " - Euro Schein ( e ) " ) ; e l s e i f (i <9) System . out . println ( hilf + " " +( banknoten [ i ]/100) + " - Euro Muenzen " ) ; else System . out . println ( hilf + " " +( banknoten [ i ]) + " - Cent Muenzen " ) ; } // Abzug d e s b e r e i t s g e l i s t e t e n R ü c k g e l d e s differenz = differenz % banknoten [ i ]; } 48 49 50 51 52 } 53 catch ( IOException eIO ) { 54 System . out . println ( " Fehler " + eIO + " ist aufgetreten . " ) ; 55 } 56 } 57 } 2. Klassenhierarchie Definieren Sie eine Klasse Geom3D und eine Hierarchie, in der als Unterklassen von Geom3D geeignet definierte Klassen Wuerfel, Quader, Kugel und Tetraeder vorkommen. Definieren Sie für die einzelnen Klassen sinnvolle Attribute, Konstruktoren sowie Instanzmethoden volume() und surfaceArea(), die das Volumen bzw. den Umfang bestimmen. Schreiben Sie dann ein Programm, das für ein Array von verschiedenen solchen geometrischen Körpern das Gesamtvolumen bzw. die Gesamtoberfläche berechnet. Mit einer Klasse Geom3Demo soll das Ganze dann getestet werden. Klassenübersicht mit definierenden Atributen und Methoden und Vererbung (Pfeile) class Geom3D abstract double volume() abstract double surfaceArea() class Quader extends Geom3D double height, length, width; double volume() {...} double surfaceArea() {...} class Wuerfel extends Quader double length; class Kugel extends Geom3D double radius; double volume() {...} double surfaceArea() {...} class Tetdaeder extends Geom3D double length; double volume() {...} double surfaceArea() {...} class Geom3Demo public static void main(String[] args){...} double sumVolume(Geom3D[] koerper){...} double sumSurface(Geom3D[] koerper){...} Umsetzung von Jasmin Kam 1 p u b l i c a b s t r a c t c l a s s Geom3D 2 { 3 p u b l i c a b s t r a c t double surfaceArea () ; 4 p u b l i c a b s t r a c t double volume () ; 5 } SurfaceArea() und volume() sind abstrakte Methodendefinitionen. Das heißt, sie müssen von allen (nicht abstrakten) erbenden Klassen überschrieben und damit implementiert werden. 6 p u b l i c c l a s s Quader extends Geom3D 7 { 8 p u b l i c double x , y , z ; // A t t r i b u t e : 3 S e i t e n 9 p u b l i c Quader ( double xWert , double yWert , double zWert ) 10 { 11 x = xWert ; 12 y = yWert ; 13 z = zWert ; 14 } // . . . und K o n s t r u k t o r 15 16 p u b l i c double surfaceArea () 17 { 18 return (( x * y + y * z + x * z ) * 2) ; // b e r e c h n e t d i e O b e r f l ä c h e 19 } 20 21 p u b l i c double volume () 22 { 23 return ( x * y * z ) ; // b e r e c h n e t d a s Volumen 24 } 25 } 26 27 p u b l i c c l a s s Wuerfel extends Quader 28 { 29 p u b l i c double seite ; // A t t r i b u t : a l l e 3 S e i t e n s i n d g l e i c h 30 p u b l i c Wuerfel ( double einWert ) 31 { 32 super ( einWert , einWert , einWert ) ; 33 } // r u f t d e n K o n s t r u k t o r a u s ’ Q u a d e r ’ m i t d r e i m a l d e r g l e i c h e n Seite auf 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 // p u b l i c d o u b l e s u r f a c e A r e a ( ) . . // p u b l i c d o u b l e v o l u m e ( ) . . . . . . . } wird geerbt wird geerbt p u b l i c c l a s s Kugel extends Geom3D { p u b l i c double radius ; // n u r e i n A t t r i b u t p u b l i c Kugel ( double einWert ) { radius = einWert ; } // . . . und K o n s t r u k t o r p u b l i c double surfaceArea () { return ( 4 * ( Math . PI ) * Math . pow ( radius ,2) ) ; } \\ berechnet die Oberfläche der Kugel p u b l i c double volume () { return 4 / 3 * Math . PI * Math . pow ( radius ,3) ; } \\ berechnet das Volumen der Kugel } p u b l i c c l a s s Tetraeder extends Geom3D { p u b l i c double seite ; // T e t r a e d e r b e s t e h t a u s 4 gleichen − p u b l i c Tetraeder ( double einWert ) eine Attribut { seite = einWert ; } // . . . und K o n s t r u k t o r 61 // s e i t i g e n D r e i e c k e n => n u r 62 63 64 65 66 p u b l i c double surfaceArea () 67 { 68 return ( Math . sqrt (3) * Math . pow ( seite ,2) ) ; 69 } // b e r e c h n e t O b e r f l ä c h e d e s T e t r a e d e r s 70 71 p u b l i c double volume () 72 { 73 return ( Math . sqrt (2) * Math . pow ( seite ,3) / 12) ; 74 } // b e r e c h n e t Volumen d e s T e t r a e d e r s 75 } 76 77 p u b l i c c l a s s Zylinder extends Geom3D 78 { 79 p u b l i c double radius , hoehe ; // z w e i A t t r i b u t e 80 p u b l i c Zylinder ( double wieRund , double wieHoch ) 81 { 82 radius = wieRund ; 83 hoehe = wieHoch ; 84 } // . . . und K o n s t r u k t o r 85 86 p u b l i c double surfaceArea () 87 { // . . . . . . . . . . . . . . . . . . . . . . . . 2 x K r e i s f l a e c h e und M a n t e l (= Umfang x Hoehe ) return (2*( Math . PI * Math . pow ( radius ,2) ) + ((2* Math . PI * radius ) * hoehe ) ) ; } // b e r e c h n e t O b e r f l ä c h e d e s Z y l i n d e r s 88 89 90 91 92 93 94 95 } p u b l i c double volume () { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . K r e i s f l a e c h e x Hoehe return ( ( Math . PI * Math . pow ( radius ,2) ) * hoehe ) ; } // b e r e c h n e t Volumen d e s Z y l i n d e r s Kurze Atempause. Die Klasse Zylinder war in der Aufgabe nicht gefragt aber Eigeninitiative ist immer toll. Das Demo-Program von Jasmin ist etwas ausführlicher mit Kommandozeileneingabe. Wenn man nur ein Atribut eingibt, werden nur Wuerfel, Kugel und Tetraeder (alle mit dem gleichen Wert erzeugt). Bei mehr Atributen kommen dann entsprechend noch Quader und Zylinder hinzu. 96 import java . io .*; 97 98 p u b l i c c l a s s Geom3Demo 99 { 100 p u b l i c s t a t i c void main ( String [] args ) throws Exception 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 { /∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ / Programm i s t e i n e w h i l e − S c h l e i f e mit / f o r −S c h l e i f e , i n d e r Eingabe gesammelt / werden . Eine 0 beendet d i e f o r , z w e i 00 / d i e while −S c h l e i f e = E x i t . ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/ String eingabeString ; // . . . . . . . . . . . . . . z u r P r u e f : 00 = Prog . ende int eingabeInt ; // wenn e i n e Z a h l i n t zahlen [] = new i n t [3]; // dann i n d i e s e s Array do { // H i n w e i s t e x t , i n s b . w i e z u beenden . System . out . println () ; System . out . println ( " Bitte eine Zahlen eingeben , dann ENTER druecken . " ) ; System . out . println ( " wenn keine weitere Zahl erwünscht , dann eine 0. " ) ; System . out . println ( " Programmende mit 00 " ) ; System . out . println () ; BufferedReader b = new BufferedReader (new Inpu tStrea mRead er ( System . in ) ) ; f o r ( i n t maximal = 0; maximal < 3; maximal ++) { // Z a h l e n e i n g a b e , max . 3 Z a h l e n zahlen [ maximal ] = 0; // e n t s p . A r r a y −Pos . Wert N u l l . i f ( maximal == 0) System . out . print ( " Bitte max . 3 Zahlen : "); i f ( maximal == 1) System . out . print ( " Jetzt noch zwei .. : "); i f ( maximal == 2) System . out . print ( " Nur noch eine bitte : "); eingabeString = b . readLine () ; eingabeInt = Integer . parseInt ( eingabeString ) ; i f ( eingabeInt == 0) // N u l l e n werden a u s g e w e r t e t . { // w e n i g e r a l s drei Attribute i f ( eingabeString . length () == 1) break ; // e r w u e n s c h t e l s e System . exit (1) ; // ” 0 0 ” => E x i t } e l s e zahlen [ maximal ] = eingabeInt ; } // f o r i f ( zahlen . length > 0) geomArray ( zahlen ) ; // Z a h l e n w e r d e n a l s Array } while ( true ) ; // u e b e r g e b e n . } // main −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− 136 137 138 /∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 139 I n GeomArray w i r d 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 d i e Anzahl der i n einem Array uebergebenen Attribute , die g e s a m m e l t wurden , a u s g e l e s e n ; 5 K o e r p e r s i n d m o e g l i c h , d a v o n w e r d e n dann p r o fehlendem Attr . e i n e r abgezogen . Dies g e s c h i e h t mit der V a r i a b l e welche , mit d e r a u c h d a s Geom3D−A r r a y k o e r p e r e r s t e l l t w i r d . A r r a y w i r d ebenfalls in A b h a e n i g k e i t v o n w e l c h e m i t d e n Geom3D−O b j e k t e n b e s t u e c k t . Zum S c h l u s s e r f o l g t d i e A u s g a b e a u f d e n B i l d s c h i r m ( n i c h t s o schoen , l e i d e r ) . ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/ p u b l i c s t a t i c void geomArray ( i n t [] maxdrei ) { String [] genau = { " Wuerfel " ," Tetraeder " ," Kugel " ," Zylinder " ," Quader " }; i n t welche = 5; // K r e i s , W u e r f e l , Tetraeder i f ( maxdrei [2] == 0) welche - -; // k e i n Q u a d e r i f ( maxdrei [1] == 0) welche - -; // k e i n e Z y l i n d e r Geom3D koerper [] = new Geom3D [ welche ]; // Geom3D−A r r a y koerper [0] = new Wuerfel3D ( maxdrei [0]) ; // m i t e n t s p r . Objekten koerper [1] = new Tetraeder3D ( maxdrei [0]) ; koerper [2] = new Kugel3D ( maxdrei [0]) ; i f ( welche > 3) koerper [3] = new Zylinder3D ( maxdrei [0] , maxdrei [1]) ; i f ( welche > 4) koerper [4] = new Quader3D ( maxdrei [0] , maxdrei [1] , maxdrei [2]) ; 155 156 157 158 159 160 f o r ( i n t geomAreaV = 0; geomAreaV < koerper . length ; geomAreaV ++) { // A u s g a b e System . out . println () ; System . out . print ( genau [ geomAreaV ] + " / Oberflaeche : " ) ; System . out . print ( koerper [ geomAreaV ]. surfaceArea () + " / Volumen : " ) ; System . out . println ( koerper [ geomAreaV ]. volume () ) ; } // f o r System . out . print ( " Gesamtvolumen : " + sumVolume ( koerper ) + " , Gesamtoberfläche : " + sumSurface ( koerper ) ) ; 161 162 163 164 165 166 167 168 169 } p u b l i c s t a t i c double sumVolume ( Geom3D [] koerper ) { double vol =0; // v a r i a b l e zum S p e i c h e r n des Gesamtvolumens f o r ( Geom3D k : koerper ) { // f ü r a l l e K ö r p e r im Array wird das vol += k . volume () ; // Volumen a u f s u m m i e r t } return vol ; } 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 } p u b l i c s t a t i c double sumSurface ( Geom3D [] koerper ) { double surf =0; // v a r i a b l e zum S p e i c h e r n d e r G e s a m t o b e r f l ä c h e f o r ( Geom3D k : koerper ) { // f ü r a l l e K ö r p e r im A r r a y w i r d d i e O b e r f l ä c h e surf += k . surfaceArea () ; // a u f s u m m i e r t } return surf ; } Wichtig waren in der Demo-Klasse vor allem die beiden Methoden zum Aufsummieren. 3. Größte Teilsumme Schreiben Sie zunächst einen Algorithmus in Pseudocode und dann eine Java– Methode, die für eine Liste von ganzen Zahlen die maximale Summe der Einträge einer zusammenhängenden Teilliste berechnen. Sind alle Werte negativ, so sei das Ergebnis 0, bei {−3, 11, −4, 13, −5, 2} wäre es 20. Begründen Sie die Korrektheit Ihres Verfahrens und was ist die Laufzeit in O–Notation? Lösung von Nils Krane, Christian Krumnow Pseudocode: 1 2 3 4 5 6 7 8 9 10 11 12 Sei array das gegebene Array len <- length ( array ) max <- 0 hilf <- 0 f o r jeden integer i in array i f i + hilf > 0 then hilf <- i + hilf i f hilf > max then max <- hilf else hilf <- 0 return max Zur Korrektheit: Es lassen sich folgende Beobachtungen aufstellen: Eine maximale Summe beginnt nie mit einem negativen Element dies entspricht dem dritten Fall der Fallunterscheidung oben: falls ein negativer Anfang einer Summe gefunden wird wird dieser übergangen (auf 0 gesetzt). Falls ein positives Anfangsstück einer Summe gefunden wird, das größer als alle anderen bisherigen Anfangsstücken war so ist dies die momentan maximale Teillistensumme. Obiger Algorithmus summiert die Glieder des Arrays zu Anfangsstücken wird eine negative Zahl erreicht so wird das in hilf gespeicherte Anfangsstück auf 0 gesetzt, wird eine größere Teillistensumme als bisher bekannt entdeckt so wird diese unter max gespeichert. Zur Laufzeit: Das Array der Länge n wird stets einmal vollständig durchlaufen, wobei die Komplexität der Operationen bei jedem Schleifendurchlauf eine konstante obere Schranke besitzen. Somit ist die Komplexität mit Theta(n) anzugeben. Umsetzung: 1 p u b l i c c l a s s Sum { 2 p u b l i c s t a t i c void main ( String [] args ) { 3 i n t [] array1 ={ -3 ,11 , -4 ,13 , -5 ,2}; 4 System . out . println ( sum ( array1 ) ) ; 5 i n t [] array2 6 7 ={2 , -2 , -5 ,3 ,7 ,2 , -9 ,13 , -10 ,2 ,1 , -8 ,3 , -5 ,6 , -9 ,2 , -1 ,5}; System . out . println ( sum ( array2 ) ) ; } 8 9 p u b l i c s t a t i c i n t sum ( i n t [] arr ) { 10 i n t max = 0; 11 i n t hilf = 0; 12 f o r ( i n t i =0; i < arr . length ; i ++) { 13 i f ( arr [ i ]+ hilf >0) { 14 hilf += arr [ i ]; 15 i f ( hilf > max ) { 16 max = hilf ; 17 } 18 } 19 else { 20 hilf =0; 21 } 22 } 23 return max ; 24 } 25 } 4. Eine Java–Methode Betrachten Sie die folgenden Java–Klassen. Was ist der Output eines Aufrufes der main-Methode in der Klasse Maryland, begründen Sie jeweils Ihre Antwort! 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 p u b l i c c l a s s Place extends Object { Place () { /∗ n u l l c o n s t r u c t o r ∗/ } p u b l i c void printMe () { System . out . println ( " Buy it . " ) ; } } p u b l i c c l a s s Region extends Place { Region () { /∗ n u l l c o n s t r u c t o r ∗/ } p u b l i c void printMe () { System . out . println ( " Box it . " ) ; } } p u b l i c c l a s s State extends Region { State () { /∗ n u l l c o n s t r u c t o r ∗/ } p u b l i c void printMe () { System . out . println ( " Ship it . " ) ; } } p u b l i c c l a s s Maryland extends State { Maryland () { /∗ n u l l c o n s t r u c t o r ∗/ } p u b l i c void printMe () { System . out . println ( " Read it . " ) ; } p u b l i c s t a t i c void main ( String [] args ) { Region mid = new State () ; State md = new Maryland () ; Object obj = new Place () ; Place usa = new Region () ; md . printMe () ; mid . printMe () ; (( Place ) obj ) . printMe () ; obj = md ; (( Maryland ) obj ) . printMe () ; obj = usa ; (( Place ) obj ) . printMe () ; } } md.printMe() md ist eine Referenz auf State und zeigt auf ein Objekt aus Klasse Maryland. Diese Unterklasse Maryland erbt von der Oberklasse State zwar die printMe– Methode, diese wird aber überschrieben. Ausgabe: Read it. mid.printMe() Das ist analog. mid ist Referenz auf Klasse Region, weist auf ein Objekt der Unterklasse State, die aber die printMe–Methode überschreibt. Ausgabe: Ship it. ((Place) obj.printMe() Jetzt ist obj Referenz auf Object, wird aber mit dem Konstruktor der abgeleiteten Klasse Place erzeugt. Die printMe–Methode wurde bei der Erweiterung hinzugefügt, eine solche Methode gibt es in der Oberklasse nicht.Durch das explizite Casting erkennt der Compiler, dass es sich um ein Objekt aus Place handelt und wendet die entsprechende Methode an. Ausgabe:Buy it. ((Maryland) obj).printMe Durch das explizite Casting nach Maryland wird die dortige printMe–Methode ausgeführt. Ausgabe: Read it. ((Place) obj).printMe() obj zeigt jetzt wie usa auf ein in Region konstruiertes Objekt. Trotz des Castings nach Place bleibt obj in der spezialisierten Unterklasse Region, insbesondere was die darin überschriebene printMe–Methode betrifft. Ausgabe: Box it.