IT 2 - WS 2016/2017 Übungsblatt 7 Abgabe: 27.1.2017 Beispiel 7.1 [freiwillig: RekursiveF.java] Implementieren Sie die Klasse ueb81.RekursiveF mit einem parameterlosen Konstruktor und der Methode public int f ( int m , int n ) , die folgende Funktion f berechnet: • f (m, n) = 0 wenn m ≤ 0 oder n ≤ 0, • f (m, n) = f (m − 1, 2n) − f (m − 1, n) wenn m, n > 0 und n ungerade ist, • f (m, n) = f (m, n/2) + m ∗ n wenn m, n > 0 und n gerade ist. Implementieren in dieser Klasse weiters die Methode public String [] allPrimes1000 () , die ein String-Array zurückgibt, das (in beliebiger Reihenfolge) genau alle Strings "(m,n)" mit 1 ≤ m, n ≤ 1000 enthält, für die f (m, n) eine Primzahl ist, z.B. also "(1,2)" und "(2,1)". Beispiel 7.2 [* ParallelMergeSort.java] Gegeben ist die Klasse parsort.geg.MergeSort mit einem parameterlosen Konstruktor und den Methoden public void sort ( int [] a , int start , int ende ) , public final void merge ( int [] a , int start , int mitte , int ende ). Der Aufruf sort(a,start,ende) sortiert den Teil a[start],...,a[ende-1] des Arrays aufsteigend. Dabei muss 0 <= start <= ende <= a.length gelten. Der Aufruf merge(a,start,mitte,ende) fügt die Elemente der bereits aufsteigend sortierten Teile a[start],...,a[mitte-1] und a[mitte],...,a[ende-1] so zusammen, dass der gesamte Teil a[start],...,a[ende-1] aufsteigende sortiert ist. Dabei muss 0 <= start <= mitte <= ende <= a.length gelten. (Hinweis: Das Schlüsselwort final bei der Methode merge() bedeutet, dass diese Methode in einer Unterklasse nicht überschrieben werden kann.) Schreiben Sie eine Unterklasse parsort.ParallelMergeSort von parsort.geg.MergeSort mit dem Konstruktor public Pa rallel MergeS ort ( int numThreads ). In der Unterklasse soll die Methode sort() so überschrieben sein, dass sie beim Sortieren die im Konstruktor angegebene Anzahl von Threads möglichst gut ausnutzt. Bei Verwendung von 4 parallelen Threads soll sich die Laufzeit mindestens halbieren, sofern genug Prozessoren zur Verfügung stehen. Eventuell wird es notwendig sein, im Paket parsort auch eine Unterklasse der Klasse Thread zu definieren. Geben Sie diese gemeinsam mit der Klasse ParallelMergeSort ab. Beispiel 7.3 [freiwillig: Versand.java] Eine Anzahl von Produkten soll in Kisten verpackt werden. Dabei können höchstens 2 Produkte in eine Kiste gepackt werden. Weiters darf die Summe der Gewichte der Produkte in einer Kiste ein maximal zulässiges Gewicht nicht übersteigen. Alle Produkte sollen in die kleinstmögliche Anzahl von Kisten verpackt werden. Implementieren Sie die Klasse ueb43.Versand mit der Methode public ArrayList < Kiste > einpacken ( ArrayList < Produkt > produkte ) , die eine Liste von Produkten in Kisten verpackt und die resultierende Liste von Kisten zurückgibt. In der Klasse global.Produkt ist eine Methode public double getGewicht () implementiert, die das Gewicht des Produkts zurückgibt. In der Klasse global.Kiste gibt es eine Methode public static double getMaxGewicht () , die das maximal zulässige Gewicht für eine Kiste zurückgibt. Weiters gibt es zwei Konstruktoren, die ein oder zwei Produkte in eine Kiste packen und die resultierende Kiste zurückgeben: public Kiste ( Produkt p ) , public Kiste ( Produkt p1 , Produkt p2 ). Zum Beispiel könnten Produkte mit Gewichten 0.12, 0.14, 0.42, 0.83, 0.91, 1.00, 1.16, 1.27, 1.28, 1.33 bei einem maximal zulässigem Gewicht von 1.5 wie folgt optimal verpackt werden: Kiste 1: 1.00, 0.42 Kiste 2: 1.16 Kiste 3: 0.14, 1.27 Kiste 4: 0.91 Kiste 5: 1.28, 0.12 Kiste 6: 0.83 Kiste 7: 1.33 Im Allgemeinen gibt es mehrere Möglichkeiten, die Kisten optimal zu packen. Beispiel 7.4 Für diese Aufgabe werden Dominosteine als int-Arrays der Länge 2 dargestellt. Schreiben Sie eine Klasse Anordnung mit der Methode List < int [] > getAnordnung ( List < int [] > dominosteine ) , die eine Anordnung ergebnis aller übergebenen Dominosteine zurückgibt, sodass für i=1,...,ergebnis.size()-2 eine Zahl von ergebnis.get(i) in ergebnis.get(i-1) und die andere Zahl von ergebnis.get(i) in ergebnis.get(i+1) vorkommt. Wenn es keine solche Anordnung gibt, dann soll die Methode null zurückgeben. Beispiel: Die Anordnungen {3,4},{4,6},{6,1} und {3,4},{3,2},{4,2},{4,4} sind korrekt, während es für die Dominosteine {3,4},{3,2},{3,1} keine korrekte Anordnung gibt. Beispiel 7.5 [Lagerplanung.java] Schreiben Sie eine Klasse ueb.Lagerplanung mit dem Konstruktor public Lagerplanung () und der Methode public int [] planen ( int anzBereiche , int anzPlaetze , int maxStuecke , int [] stueckArtikel ) die die Belegung eines Lagers berechnet. Das Lager besteht aus anzBereiche vielen identischen Bereichen, die jeweils über anzPlaetze viele Lagerplätze verfügen. Die einzulagernden Artikel sind im Array stueckArtikel angegeben, wobei stueckArtikel[i] angibt, wie viele Stück vom Artikel mit Nummer i eingelagert werden sollen. Jedem Artikel soll ein Lagerbereich zugeordnet werden, also eine Nummer 0... anzBereiche-1. Einem Lagerbereich dürfen nicht mehr Artikel zugeordnet werden als Lagerplätze vorhanden sind. Weiters dürfen in einem Lagerbereich insgesamt (summiert über alle Lagerplätze) nicht mehr als maxStuecke viele Stücke gelagert werden. Die Methode planen soll eine zulässige zuordnung von Artikeln zu Lagerbereichen zurückliefern, sodass zuordnung[i] den Lagerbereich angibt, der dem Artikel i zugeordnet ist. Gibt es keine zulässige Zuordnung, so soll die Methode null zurückliefern. Aufgabe 7.6 [Communicator.java] (Die Klasse wird automatisch geprüft.) Schreiben Sie die Klasse threads.Communicator, die die Kommunikation zu Nachrichtenempfängern unterstützen soll. Das Interface threads.geg.Receiver mit der Methode public void receive ( String message ) , ist gegeben. Diese Methode erlaubt es, eine Nachricht an einen Receiver zu senden. Die Klasse Communicator soll über einen parameterlosen Konstruktor verfügen, sowie über die Methoden public String signUp ( Receiver aReceiver ) , public void send ( String identifier , String message ) , public void sendAll ( String message ). Die Aufrufe dieser Methoden aus mehreren Threads sollen vom Communicator synchronisiert werden: • Mit signUp(aReceiver) wird ein Receiver beim Communicator angemeldet. Die Methode liefert eine eindeutige Bezeichnung für den Receiver zurück. Ist der Receiver schon angemeldet, dann wird wieder die zuvor vergebene Bezeichnung zurückgegeben. • Durch send(identifier,message) soll message mittels aReceiver.receive(message) an den durch identifier bezeichneten Receiver gesandt werden. Allerdings soll aReceiver.receive(message) nur aufgerufen werden, wenn kein anderer Aufruf send(identifier,message1) für diesen Receiver und kein Aufruf sendAll(message2) aktiv ist. Falls ein solcher Aufruf aktiv ist, soll solange gewartet werden, bis aReceiver.receive(message) durchgeführt werden kann. Wenn identifier keine korrekte Bezeichnung ist, dann soll eine RuntimeException mit dem Text "Invalid identifier!" geworfen werden. • Mit sendAll(message) wird die Nachricht mittels aReceiver.receive(message) an alle angemeldeten Receiver gesendet. Allerdings wird die Nachricht erst gesendet, wenn kein anderer Aufruf sendAll(message1) und auch kein Aufruf send(identifier,message2) aktiv ist. Solange die Nachricht nicht gesendet werden kann, soll gewartet werden. Solange ein Aufruf wartet, soll kein send(identifier,message2) aktiv werden können (außer er ist schon aktiv). sendAll(message) Aufruf Aufgabe 7.7 Gegeben sind • die Klassen Layout und Produkt, • die Klasse Fabrik mit dem Konstruktor Fabrik ( Layout einLayout ) , der eine Fabrik mit dem angegebenen Layout erzeugt, und der Methode Produkt fertige () , die eine Fertigung simuliert und das gefertigte Produkt zurückgibt, • sowie folgendes Interface: interface Zufallsgenerator { boolean istFehlerhaft (); } Implementieren Sie die Unterklasse FabrikMitFehlern von Fabrik mit dem Konstruktor FabrikMitFehlern ( Layout einLayout , Zufallsgenerator zufall ) und überschreiben Sie die Methode fertige() so, dass null zurückgegeben wird, wenn zufall.istFehlerhaft() als Ergebnis true liefert, und ansonsten das in der Oberklasse gefertigte Produkt. Schreiben Sie auch eine Klasse Bernoulli, die Zufallsgenerator implementiert und über den Konstruktor Bernoulli ( double prob ) verfügt, sodass istFehlerhaft() mit Wahrscheinlichkeit prob als Ergebnis true liefert.