• Nenhum resultado encontrado

Einfache und schwierige Probleme

L A TEX

17.3 Einfache und schwierige Probleme

17.2.3 Laufzeitbestimmung II

17-17 Wiederholung der Laufzeitbestimmung. 17-17

boolean containsZero (String s) {

for (int i = 0; i < s.length(); i++) { if (s.charAt(i) == ’0’) {

return true;

} }

return false;

}

T(l) =t1+ (2l+1)t2+ (l+1)t3+lt4+lt5+t6. Mit Hilfe derO-Notation können wir kurz sagenT∈O(l)oder auchT(n) =O(n). Der Aufwand ist alsolinear.

17-18 . Zur Übung 17-18

1. Bestimmen Sie dieO-Klasse des Zeitaufwands von folgendem Programm:

void prefix_sums (double[] vector) { int sum = 0;

for (int i = 0; i < vector.length; i++) { sum = sum + vector[i];

vector[i] = sum;

} }

2. Bestimmen Sie dieO-Klasse des Zeitaufwands von folgendem Programm (schwierig):

double puzzle (double[] vector) { int i = 1;

while (3*i < vector.length) { i = i*3;

}

return vector[i];

}

17.3 Einfache und schwierige Probleme

17.3.1 Einfache Probleme

17-19 Was sind einfache Probleme? 17-19

In derPraxisgelten alle Probleme als gut lösbar, die sich in Zeit bisO(n3)lösen lassen. Die

»versteckten Konstanten«sollten allerdings nicht zu groß sein. In derTheorie gelten alle Probleme als gut lösbar, die sich in ZeitO(nk)lösen lassen für irgendeink.

Beispiele:Effizient lösbare Probleme Binäre und lineare Suche Sortieren

Addieren, Multiplizieren, Dividieren

Matrix-Multiplikation und Matrix-Invertierung Kürzeste Wege finden in Graphen

Berechnen eines Bildes in einer Animation

17 Komplexität von Problemen

Zusammenfassung dieses Kapitels 145

17.3.2 Schwierige Probleme

17-20 Was sind schwierige Probleme? 17-20

Manche Probleme gelten alspraktisch nicht gut lösbar. Für solche Probleme istkein Algo- rithmus mit einer Laufzeit vonO(nk)bekannt.

Beispiel:Das Partitionsproblem

Gegeben sindnZahlen. Kann man sie so in zwei Teilmengen aufteilen, dass die Summen gleich sind?

Es gibt2nMöglichkeiten, die Zahlen aufzuteilen, was einen Algorithmus mit LaufzeitO(2n) liefert. Was ist die Laufzeit desbesten Algorithmusfür das Partition-Problem?

17-21 Wir mögen keine exponentielle Laufzeit. 17-21

Programm 2010

Programmiererin Ada hat den Algorithmus für das Partition-Problems in Maschinensprache implementiert. Auf einem Pentium 4GHz kann sie damit in einer Stunde Eingaben bisn≈ log2(360·109)≈38bearbeiten.

Programm 2020

10 Jahre später gibt es einen Quad-Core Rechner mit 40GHz. Nun kann sie in einer Stunde Eingaben bisn≈log2(4·3600·109)≈44bearbeiten.

Programm 2030

Wieder 10 Jahre später bekommt sie einen Supercomputer mit 10.000 Prozessoren mit je 1000 GHz. Nun kann sie in einer Stunde Eingaben bisn≈log2(3600·1015)≈62bearbeiten.

17-22 Wir mögen wirklich keine exponentielle Laufzeit. 17-22

Original C. Flammarion, public domain. Copyright coloring by Hugo Heikenwaelder, Creative Commons Attribution Sharealike License

Programm 2500

Ada baut einen großen Rechner. Jedes Atom im Universum (≈1080) rechnet in jeder Planck- Zeit (≈5,39·1043s) einen Rechenschritt. Nun kann sie in einer Stunde Eingaben bisn≈ log2(5,39·3600·1080+43)≈423bearbeiten.

Moral

Eingaben mit 500 Zahlen wird manniemals (!)mit dem Algorithmus für das Partition- Problem lösen können.

17-23 Die Komplexitätsklassen P und NP. 17-23

In derTheoretischen Informatik, speziell in derKomplexitätstheorie, untersucht man, welche Probleme einfach und welche schwierig sind. Dieeinfachen Probleme(solche, die sich in Zeit O(nk)lösen lassen) bilden dieKlasse P. Nimmt man noch eine Reiheanscheinend schwieriger Problemehinzu, so erhält man dieKlasse NP. Die Frage, obP=NPgilt, ist die wichtigste ungelöste Frage der Theoretischen Informatik.

Zusammenfassung dieses Kapitels

17-24

I

O-Klasse 17-24

DieO-KlasseO(g)ist die Menge aller Funktionenf: N→N, für die es eine Konstantecund

eine Konstanten0gibt, so dass für allen>n0gilt f(n)≤c·g(n).

Man ignoriert alsokonstante Faktorenund(zu) kurze Eingaben.

I

Die wichtigstenO-Klassen und ihre Inklusionsbeziehungen

O(1)(O(log3n) =O(log2n) =O(logn)(O(√ n)

(O(n)(O(nlogn)(O(nlog2n) (O(n2)(O(n3)

(O(2n)(O(3n).

Hierbei gilt alles bisO(n3)als »noch vertretbar«.

146 17 Komplexität von Problemen Übungen zu diesem Kapitel

Übungen zu diesem Kapitel

Übung 17.1 O-Klassen bestimmen, mittel

Für zwei Bitstrings (Strings aus Nullen und Einsen) gleicher Länge ist ihr Hamming-Abstand definiert als die Anzahl von Positionen, in denen sich die Strings unterscheiden (siehe Übung 14.4).

Die folgende Java-Methode berechnet für eine Liste vonmBitstrings der Längenden maximalen Hamming-Abstand für Paare von Bitstrings aus der Liste.

public static int compute_max_distance

(String[] list_of_bitstrings) { int maxdist = 0;

// Anzahl der Bitstrings

int m = list_of_bitstrings.length;

// Länge der Bitstrings; Annahme: alle gleich lang int n = list_of_bitstrings[0].length();

for (int i = 0; i < m; i++) { for (int j = i+1; j < m; j++) {

// alle Paare (i,j) mit i < j checken int counter = 0;

// zählt Positionen mit unterschiedlichen Einträgen for (int k = 0; k < n; k++) {

// Schleife berechnet Abstand der Strings i und j if (list_of_bitstrings[i].charAt(k) !=

list_of_bitstrings[j].charAt(k)) { counter++;

} }

if (counter > maxdist) {

maxdist = counter; // Erhöht maxdist,

} // wenn neuer Abstand größer

} }

return maxdist;

}

Analysieren Sie die Laufzeit des Programms in Abhängigkeit von den Parameternnundm. Geben Sie eine passendeO-Klasse für die Laufzeit an.

Übung 17.2 Laufzeiten experimentell bestimmen, schwer

Prüfen Sie experimentell die Laufzeit der Methode aus der vorherigen Übung in Abhängigkeit der Parameternundm. Das folgende Programm dient diesem Zweck:

class HammingTest{

public static String[] generate_bitstrings(int n, int m) { String[] list_of_bitstrings = new String[m];

for (int i = 0; i < m; i++){

// Liste ab 0 mit Bitstrings füllen String bitstring = "";

for (int k = 0; k < n; k++) {

if (Math.random() > .5) { // zufällig 0 oder 1 bitstring = bitstring + "0";

} else {

bitstring = bitstring + "1";

} }

list_of_bitstrings[i] = bitstring;

}

return list_of_bitstrings;

}

/* Hier muss compute_max_distance eingefügt werden. */

public static void main(String[] args) {

int n = Integer.parseInt(args[0]); // Länge der Bitstrings int m = Integer.parseInt(args[1]); // Anzahl der Bitstrings String[] list_of_bitstrings = generate_bitstrings(n,m);

17 Komplexität von Problemen

Übungen zu diesem Kapitel 147

long startTime = System.currentTimeMillis();

int max = compute_max_distance(list_of_bitstrings);

long runningTime = System.currentTimeMillis() - startTime;

System.out.println("Die Laufzeit für n= " + n + " und m= " + m + " beträgt etwa "

+ runningTime + " Millisekunde(n).");

} }

Die Methodegenerate_bitstringserzeugt zufällig Bitstring-Listen. Die Eingabeparameter sind n, die Länge der Strings, undm, die Anzahl der Strings.

Die Methodemainerzeugt für die eingelesene Stringlänge und -anzahl eine zufällig erzeugte String- liste und berechnet dann maximalen Hamming-Abstands für die Liste. Es wird (eine Annäherung an) die Rechenzeit fürcompute_max_distancegestoppt.

Die so ermittelte Rechenzeit für festesnundmkann durchaus schwanken. Erweitern Sie das Programm so, dass es die durchschnittliche Laufzeit für (zum Beispiel)20Durchläufe ermittelt. Lassen Sie das Programm für ausreichend viele passende Werte fürnundmlaufen. Wird die Laufzeitabschätzung aus der vorherigen Aufgabe bestätigt?

148 18 Einführung zur Rekursion

18-1 18-1

Kapitel 18

Einführung zur Rekursion

Kasparov versus Deep Blue

18-2 18-2

Lernziele dieses Kapitels

1. Das Konzept der Rekursion verstehen

2. Rekursive Methoden implementieren können für einfache Probleme

Inhalte dieses Kapitels

18.1 Rekursion 149

18.1.1 Der Begriff . . . 149 18.1.2 Java-Syntax der Rekursion . . . 150

18.2 Beispiele 150

18.2.1 Fakultät . . . 150 18.2.2 Fibbonacci-Zahlen . . . 151 18.2.3 Die Türme von Hanoi . . . 151 18.2.4 Binäre Suche . . . 152

18.3 *Wechselseitige Rekursion 152

18.3.1 Tic-Tac-Toe . . . 152 18.3.2 Schach . . . 153

Übungen zu diesem Kapitel 153

Worum es heute geht

Worum es heute geht

In einem Betrieb macht die Chefin nicht alles selbst. Die Kunst, einen Konzern zu führen, ist zudelegieren. Wenn ein Konzern eine neue Strategie sucht – sagen wir zur Vermark- tung von Klingeltönen –, wird die Chefin sicherlich erstmal ihren Assistenten einspannen, der eine solche vorbereiten soll. Dieser wiederum macht natürlich auch nicht die gesamte Arbeit im Konzern, sondern findet schnell eine Abteilung und damit einen Abteilungsleiter, der ihm helfen soll. Dieser wiederum hat seinen Stab, der Zugriff auf die Fachkräfte hat, die ganz am Ende die Arbeit einem Praktikanten geben, der am Wochenende seine Freundin bezirzt, ihm doch ein paar Ideen zu nennen, wie man Klingeltöne besser verkaufen könnte.

Die geniale Idee der Freundin, das Geschäft mit Klingeltönen aus ethischen Gründen und aus Gründen der akustischen Hygiene einfach einzustellen, wandert dann die ganzen Hier- archiestufen wieder nach oben, jedesmal vielleicht leicht verändert, bis die Konzernchefin auf den nächsten Aktionärsversammlung die neue Firmenstrategie vorstellen kann, sich in Zukunft mehr auf die Vermarktung von Telefonen mit Vibrationsalarm zu konzentrieren.

Ganz ähnlich funktioniert Rekursion. Der Computer soll ein Problem lösen, aber anstatt dieses direkt zu lösen, wird erstmal jemand anderes (eine Methode) mit der Lösung des Problems beauftragt. Diese Method delegiert das Problem wieder, dann wird es wieder de- legiert und so fort. Hierbei gibt es Folgendes zu beachten:

Bei einer Rekursion wird in der Regel gar nicht an »andere Leute« delegiert, sondern eine Methoderuft sich selbst auf.

Das wäre natürlich reichlich nutzlos und würde zu einer unendlichen Kette von Selbst- delegationen führen, wenn nicht bei jeder Delegation das Problemetwas einfacher wür- de.

Wenn das Problem durch mehrfaches (manchmal millionenfaches) Delegieren trivial geworden ist, muss man die Rekursionabbrechen und das Problem direkt lösen. Wenn man ein rekursives Programm zum ersten Mal sieht, wird man sich des Gefühls nicht erwehren können, dass hier »geschummelt« wird. Anscheinend löst man ein Problem, indem

18 Einführung zur Rekursion