Kurze Einführung in die Informatik

fb03.h.bonn.rhein.sieg.de

Kurze Einführung in die Informatik

Eine kurze Einführung in die Informatik

Irene Rothe

Sommer 2009

Was ist Informatik? Informatik ist das Lösen von Problemen mit Hilfe des Rechners.

Was für Probleme könnten wir haben, die ein Rechnerprogramm für uns lösen soll? Zum

Beispiel die folgenden:

• Wie werde ich schnell sehr reich?

• Wie bekomme ich meine CD-Sammlung sortiert?

• Wie erhalte ich die kürzeste Autofahrstrecke zwischen allen Städten in Deutschland

mit mehr als 20.000 Einwohnern?

• Ein Dieb bricht in einem Kaufhaus ein und will in seinem Rucksack Waren von

maximalem Gesamtwert wegschleppen. Welche Waren soll er einpacken?

• Ist eine beliebige gegebene Zahl eine Primzahl?

• Kommen in π irgendwann mal 99 Siebenen hintereinander vor?

• Kann ein Compiler sich selbst übersetzen?

• Wie berechne ich eine ganzzahlige Lösungen x, y, z der Gleichung

x 4 + y 2 − z + 27x − 8z = 0?

• Wie kann ich auf mein Bankkonto sicher übers Internet zugreifen?

• Wie kann ich auf fremde Bankkonten zugreifen und mir selbst das Geld von anderen

überweisen?

1


Obige Probleme haben unterschiedliche Eigenschaften. Bedauerlicherweise gibt es viele

Probleme, die gar nicht mit einem Rechner lösbar sind. Warum und welche Eigenschaften

des Problems eine Lösung mit dem Rechner zunichte machen, ist ein wichtiges Gebiet

der Informatik (Berechenbarkeit).

Ebenso gibt es eine Menge Probleme, die nicht in vernünftiger Zeit mit dem heutigen

Typ Rechner (mit Strom und Transistoren arbeitend) zu lösen sind (Komplexität). Aber

das ist für manche Probleme sogar gut für uns (Kryptografie).

Am Ende bleiben dann die Probleme übrig, mit denen wir einen Rechner füttern können.

Das sind die Probleme, für die wir Algorithmen finden, was ganz einfach so etwas wie eine

Anleitung ist: eine klare und endliche Beschreibung von Tätigkeitsschritten, die mechanisch

ausgeführt werden können.

• Was machen wir mit diesen Problemen?

• Warten wir bis jemand mehr über diese Probleme herausfindet?

• Oder suchen wir selbst nach einer Lösung und werden berühmt?

• Sind alle Probleme gleich gut lösbar oder kann man sich manchmal mit Annäherungslösungen

behelfen?

Weitere wichtige Fragen tauchen beim Programmieren auf:

• Wird das Rechnerprogramm irgendwann fertig mit Rechnen und gibt ein Ergebnis

aus?

• Stimmt das erhaltene Ergebnis? Kann der Lösung getraut werden? Hat das Programm

korrekt gerechnet?

Bringen wir ein bisschen Ordnung in obigen und allen anderen Problemen und teilen

sie ein in

• nicht lösbare Probleme,

• mit mathematischen Hilfsmitteln aber nicht mit dem Rechner lösbare Probleme,

• lösbare, aber für heutige Rechner nicht bewältigbare Probleme und

• effizient mit einem Rechner lösbare Probleme.

2


1 Nicht entscheidbare Probleme

Problem Programm-Überprüfungssystem

Ein Programm ist ein in einer Programmiersprache formulierter Algorithmus. In Programmen

sind sehr oft Fehler, weil Menschen eben Fehler machen. Deshalb wäre ein Programm-

Überprüfungssystem eine wunderbare Sache.

Gesucht ist eine vollständig automatisches Programmüberprüfungssystem, das jedes

beliebiges gegebenes Programm überprüft und mit der Aussage PROGRAMM RICHTIG

oder einer Fehlerliste endet, wo z.B. auf eine Endlosschleife aufmerksam gemacht wird.

Das Programm-Überprüfungssystem ist selbst ein Programm. Wenn es selbst ein Programm

ist, dann könnte es sich auch gut selbst überprüfen. Das Programm-Überprüfungssystem

soll ja für alle Programme funktionieren. Hier steigt ein ungutes Gefühl auf, dass

es so irgendwie nicht geht. Das fühlt sich an, wie sich selbst an den Haaren aus dem

Sumpf ziehen. Weiterhin könnten wir das Programm-Überprüfungssystem selbst zu einem

neuen Programm umbasteln, dass genau das Gegenteil von dem alten Programm-

Überprüfungsssystem tut. Es läuft unendlich lange (verfängt sich in einer Endlosschleife),

wenn das alte Programm-Überprüfungssystem mit der Antwort RICHTIG endete und

gibt die Aussage RICHTIG aus, wenn eine Endlosschleife entdeckt wurde. Das so entstandene

neue Programm sollte auch von dem Programm-Überprüfungssystem nach Fehlern

untersucht werden können. Wer soll diesen Fehler aber finden? Das ursprüngliche, ultimative

Programm-Überprüfungssystem sicher nicht. Mehr Informationen zu diesem Problem

kann in Büchern der Theoretischen Informatik unter Halteproblem gefunden werden.

Bemerkung: Ein vollautomatisches Programm-Überprüfungssystem wird es also nie

geben. Wenn man aber Einschränkungen an die zu überprüfenden Programme macht, kann

man für einfache Programme Algorithmus-Überprüfungsprogramme bauen, die nach Fehlern

suchen und einige auch finden würden.

Ein Problem, dass nicht entscheidbar ist, ist das folgende Problem:

Problem Diophantische Gleichungen

Gegeben ist z.B. das folgende Gleichungssystem:

x 3 + 5y − 7z = uv 2

u + v − y 4 = z 3 + 4

3


(x − y) 2 − u = v − 1

Gesucht sind ganzzahlige Lösungen x, y, z, u, v, die diese Gleichungen erfüllen.

1970 hat Matijassewitsch mit 22 Jahren gezeigt, dass niemals ein allgemeines Lösungsverfahren

für Diophantische Gleichungen f(x 1 , ..., x n ) = 0 mit ganzzahligen Koeffizienten

und gesuchten ganzzahligen Lösungen gefunden werden kann.

Hier ist noch ein nicht offenes (nicht lösbares?) Problem:

Problem Suche nach Teilfolgen in der irrationalen Eulerzahl e

Es ist eine Zahlenfolge gegeben, und wir möchten wissen, ob diese Folge irgendwo in e

vorkommt. Für die Berechnung von e gibt es Algorithmen, z.B. die Taylorreihenentwicklung

e = ∑ ∞ 1

n=0 = 2.7181.... Wir berechnen also e immer um eine Stelle mehr und testen,

n!

ob die gegebene Folge vorkommt. Ist z.B. die Folge 7182 gegeben, müssen wir e nur bis zur

4. Stelle nach dem Komma berechnen, um zu sehen, dass die gegebene Folge tatsächlich in

e vorkommt. So lange wir wissen, bis zu welcher Stelle wir e berechnen müssen, kann ein

Rechner dies auch in endlicher Zeit für uns erledigen, da wir genau die Anzahl der Berechnungsschritte

zählen können. Bei manchen gegebenen Zahlenfolgen (wie eben) haben wir

Glück, und die Folge kommt sehr weit vorn in e vor. Aber bei anderen gegebenen Folgen

kann das vielleicht sehr lange dauern, z.B. bei der Folge, die aus 99 Neunen hintereinander

besteht. Das Unangenehme an der Sache ist: Wir wissen nicht, wie lange wir auf ein Ergebnis

warten müssen. Da e unendlich viele Zahlen ohne Perioden (Wiederholungen von

Zahlen ab einer bestimmten Kommastelle) nach dem Komma stehen hat, also eine irrationale

Zahl ist, kann die Suche nach Übereinstimmung von e mit der gegebenen Folge sehr,

sehr lange dauern, eventuell auch unendlich lange.

Bemerkung: Der Test, ob eine bestimmte Zahlenfolge eine Anfangsfolge von e ist,

ist sehr wohl lösbar. Ist z.B. die Zahlenfolge 271628 gegeben, berechnen wir e bis zur 5.

Stelle nach dem Komma und überprüfen, ob die Zahlenfolge mit e übereinstimmt. Das

wäre bei obiger Folge der Fall und bei z.B. 272 nicht. Für andere gegebene Folgen müssen

wir vielleicht sehr viele Stellen von e berechnen, aber immer können wir die Anzahl der

Berechnungsschritte genau vorher benennen, nämlich in Abhängigkeit von der gegebenen

Folge. Und das sind immer endlich viele Schritte.

Jedes Problem kann so umgeformt werden, dass die Lösung eine JA- oder NEIN-

Antwort ist (man denke nur an das Kinderspiel, wo man Tiere nur durch Ja/Nein-Fragen

4


errät). Diese Probleme heißen Entscheidungsprobleme. Erhält man garantiert eine Lösung,

die JA oder NEIN lautet, ist das Problem entscheidbar. Erhält man am Ende ein Ja und ein

Weiß-ich-nicht oder weder noch, sind die Probleme semi-entscheidbar oder unentscheidbar.

2 Der Begriff des Algorithmus

Intuitiv sind Probleme berechenbar, wenn wir eine Vorschrift zur Lösung des Problems

aus einzelnen Anweisungen finden, die klar und eindeutig formuliert sind und deren Anzahl

endlich ist. Diese Vorschrift ist ein Algorithmus. Es besteht die Annahme, dass nur

berechenbare Probleme auf einem Rechner gelöst werden können. Bis jetzt hat noch niemand

ein Problem gefunden, dass wir als intuitiv berechenbar empfinden, für das aber keine

Anweisungsfolge gefunden wurde.

Uns interessieren jetzt nur noch Probleme, die mit einem Rechner lösbar sind. Es muss

also ein Algorithmus gefunden werden, so dass der Rechner von Anweisung zu Anweisung

immer weiß, was er als nächstes tun soll. Das dürfen nur endlich viele (aber durchaus sehr

viele) Schritte sein, und am Ende muss ein Ergebnis ausgegeben werden.

So ein Algorithmus wird oft in einer Programmiersprache formuliert. Die Programmiersprache

(Java, C++, C, Perl, php, ...) können nur berechenbare Probleme behandeln.

Algorithmus ist ein intuitiver Begriff, der gleichgesetzt werden kann mit berechenbar

oder von einer Maschine ausführbar. Ein Algorithmus ist ein:

• schrittweises Verfahren zur Bestimmung eines Ergebnisses ausgehend von einer Eingabegröße

oder Eingabegrößen.

• Diese Anweisungsschritte müssen eindeutig formuliert sein, so dass sie der Rechner

ausführen kann.

• Es dürfen nur endlich viele Anweisungen sein, dass heißt, der Algorithmus muss

irgendwann zu einem Ende mit Ergebnis kommen.

5


3 Ineffiziente berechenbare Probleme

Für viele Probleme findet man, so sehr man sich auch anstrengt, nur sehr lang rechnende

(in Abhängigkeit von der Eingabegröße) Algorithmen. Oftmals braucht der Rechner für die

Lösung solcher Probleme länger, als unser Universum existiert (das Universum existiert ca.

10 10 Jahre = 365 ∗ 10 11 Tage = 876 ∗ 10 12 Stunden = 5256 ∗ 10 13 Minuten = 31536 ∗ 10 13

Sekunden, was ca. 3∗10 17 Sekunden sind). So viel Zeit hat niemand, und es scheint nutzlos

zu sein, den Rechner überhaupt zu bemühen.

Man könnte sich auf die Suche nach einem praktikableren, also schnelleren Algorithmus

machen. Aber bei einer bestimmten Art von Problemen scheint das einfach nicht

zu gelingen. Das läßt allerdings nicht die Schlussfolgerung zu, es gäbe nicht doch einen

schnelleren Algorithmus, aber aus irgendwelchen Gründen ist es der Menschheit bis heute

nicht gelungen, einen effizienten Algorithmus zu finden oder zu beweisen, dass es keinen

Zweck hat, nach einem zu suchen, weil es ihn nicht gibt.

Manchmal ist man aber auch froh, dass es bis jetzt keinen effizienten Algorithmus gibt,

z.B. zum Entschlüsseln unserer Online-Banking-Daten. Da wiederum wäre es wohltuend

zu wissen, dass niemand im stillen Kämmerlein einen effizienten Entschlüsselungsalgorithmus

gefunden hat und am Werke ist, in dieser Minute unser Geld abzuheben.

Problem Handelssreisender

Stellen wir uns vor, wir wollen 20 Städte in Deutschland so bereisen, dass wir den kürzesten

Weg zwischen den Städten insgesamt fahren und am Ende wieder zu Hause ankommen.

Für dieses Problem ist kein viel besserer Algorithmus bis heute bekannt, als alle Verbindungsmöglichkeiten

entfernungsmäßig auszurechnen und dann die kürzeste Strecke aus

diesen einzelnen Entfernungssummen zu suchen. Bei 20 Städten gäbe es 20!/2 = 20 ∗ 19 ∗

18∗...∗2∗1/2 verschiedene Verbindungsmöglichkeiten (man teilt durch 2, weil es bei einer

gefundenen Reise egal ist, wie rum man sie fährt) und damit genauso viele Entfernungen,

was 2432900000000000000/2 Möglichkeiten entspricht. Nehmen wir an, dass unser Rechner

für eine Berechnung 10 −9 Sekunden braucht dann, würde dieses Programm immer noch

1216450000 Sekunden brauchen, was ungefähr 40000000 Minunten = 660000 Stunden =

27500 Tage = 75 Jahre wären. Hier kann uns auch ein 10 mal schnellerer Rechner nicht

viel glücklicher machen. So lange will niemand warten, um sich die garantiert kürzeste

Strecke zwischen 20 Städten berechnen zu lassen. Es gibt einige sehr clevere Algorithmen,

6


die relativ vernünftig bis zu 2000 Städten arbeiten, aber um Telefonnetze u.ä. zu berechnen

ist eine Eingabengröße von 2000 viel zu wenig, um wirklich für die Praxis nützlich zu sein.

Eine Anwendung für das gleiche Problem: Stellen wir uns vor, ein Bohrer soll Lötlöcher

auf einer Leiterplatte bohren, wobei wir natürlich gerne hätten, dass der Bohrer so wenig

Zeit wie möglich verschwendet, um sich von Bohrposition zu Bohrposition zu bewegen. Da

kommt schnell eine Bohrlochzahl von mehreren Tausenden zusammen. Man behilft sich in

solchen Fällen mit Approximationsalgorithmen, mit denen man gute Näherungslösungen

berechnet. Diese sind weit besser, als wenn man zufällig die nächste Position des Bohrers

auswählt. Bei diesen Approximationsalgorithmen haben wir aber eben keine Garantie,

dass sie die beste Lösung berechnet. Es ist möglich, dass eine bessere Reihenfolge für die

Bohrungen gefunden werden kann.

Problem Rucksackpackungen

Ein Dieb bricht in ein Kaufhaus mit 1000 Waren ein. Er hat sich einen sehr großen Rucksack

mitgebracht von einer bestimmten Größe. Er will nun in diesen Rucksack Diebesware

von maximalem Wert einpacken. Er muss also aus den 1000 Waren welche auswählen, so

dass sie insgesamt in seinen Rucksack passen, dass aber der Wert der Waren auch hübsch

hoch ist, um nicht zu sagen, so hoch wie möglich. Sicher kann er erst mal auf alle Waren

verzichten, die sowieso nicht in seinen Rucksack passen, danach bleiben vielleicht noch

500 Waren übrig. Welche wählt er aus? Er könnte vorher zu Hause ein Programm laufen

lassen, dass diese Waren für ihn auswählt. Aber hier muss er mit Enttäuschung feststellen,

dass das Programm sehr lange braucht, und das Kaufhaus bis dahin vielleicht schon pleite

ist. Der ineffiziente Algorithmus würde einfach alle Möglichkeiten für Warenkombinationen

durchprobieren. Also eine der 500 Waren beliebig wählen und aus den Waren, die noch

in den Rucksack passen würden, zufällig weitere wählen und jeweils den Gesamtwert berechnen.

Das wirkt ähnlich aufwendig wie das Handelsreisendenproblem. Programmierbar

ist dieser Algorithmus ohne Frage, aber man hätte nur Lust, auf Ergebnisse für ein sehr

kleines Kaufhaus zu warten.

7


4 Wie bestimmt man die Laufzeit eines Algorithmus?

Ein Algorithmus besteht aus einzelnen Anweisungen. Diese Anweisungen werden einmal

oder mehrmals ausgeführt. Wie oft sie ausgeführt werden, ist abhängig von der der Eingabe.

Hat man einen speziellen Rechner im Auge, kann man in seinen technischen Daten

nachlesen, wie lange sein Prozessor für eine Anweisung braucht. Dann muss man also

nur alle im Falle der Ausführung des Algorithmus ausgeführten Anweisungen zählen und

multipliziert sie mit der Zeit, die eine Anweisung dauert.

Bemerkung: Es wird angenommen, dass jede Anweisung konstante Rechenzeit braucht.

Wie entscheiden wir, was ein effizienter und was ein ineffizienter Algorithmus ist?

Im täglichen Leben haben wir über die Jahre ein Gefühl entwickelt, welche Geldanlage

wir von den folgenden auswählen würden:

Geld Geld Geld

t t t

Abbildung 1: Entwicklung von Geldanlagen.

Bei Geldanlagen vergeht die Zeit und wichtig ist für uns das Geld, das sich vermehrt.

Deshalb ist die Zeit die Eingabe und das Geld die Ausgabe der Geldentwicklungsfunktion.

Bei Algorithmen ist die Zeit die Größe, die uns als Ergebnis interessiert und die Eingabe

sind positive ganze Zahlen, z.B. Anzahl der zu ordnenden Bücher, Anzahl der Waren im

Kaufhaus, Anzahl von Städten, Größe des Zahlenschlüssels bei einer Verschlüsselung, ...

Von welchen Algorithmen der folgenden würden wir die Finger lassen, da sie irrsinnig

lange rechnen?

Das linke Bild zeigt die Laufzeit eines ineffizienten Algorithmus und das ganz rechte

Bild die vernünftige Laufzeit eines effizienten Algorithmus. Bei drei Algorithmen, die

Dinge sortieren sollen, hätte man beispielsweise folgende Laufzeiten in Abhängigkeit von

der Anzahl der zu sortierenden Dinge (z.B. Bücher oder Namen):

8


t t t

n n n

Abbildung 2: Aufwand von Algorithmen.

Anzahlderzu 10 50 100 1000

sortierendenDinge

Laufzeitfunktion : logn 1 1, 69 2 3

(linkesBild)

Laufzeitfunktion : n 10 50 100 1000

(mittleresBild)

Laufzeitfunktion : n 2 100 2500 10000 1000.0000

Laufzeitfunktion : 2 n 1024 10 15 10 30 riesig

(rechtesBild)

Laufzeitfunktion : n! 3.628.800 3.10 64 riesig riesig

Rechnen wir das auf einen realen Rechner um, sind die Algorithmen mit Laufzeit 2 n und

n! vollkommen unpraktikabel.

Wo ist die Grenze? Ab welcher Laufzeitfunktion sagen wir, dass der Algorithmus ineffizient

ist?

Die theoretische Informatik hat definiert, wenn die Laufzeitfunktion als Polynom dargestellt

werden kann, also n k mit k >= 1 und n als Eingabegröße, dann spricht man von

einem effizienten Algorithmus. Die Laufzeitfunktionen können z.B. wie folgt aussehen:

n 2 , log n, n 3

Die Eingabegröße n bleibt in der Basis und läßt bei steigender Eingabegröße die Laufzeitfunktion

nicht gar zu schnell wachsen. Das ist natürlich Geschmackssache, da z.B. n 1000

auch eine sehr lange Laufzeit bedeutet.

Ist die Laufzeitfunktion aber exponentiell, also k n mit k > 1 und n als Eingabe, dann

9


ist der Anstieg der Funktion bei wachsender Eingabegröße noch viel rasanter als eben, und

wir haben einen ineffizienten Algorithmus vor uns. Die Laufzeitfunktionen können, dann

z.B. wie folgt aussehen:

2 n , n!, 1.41 n , 2 √ n

Die Eingabegröße n geht in den Exponenten ein und läßt deshalb die Laufzeit noch viel

schneller wachsen.

Wie man die Laufzeit für sehr komplizierte Algorithmen mathematisch berechnet oder

bestmöglich abschätzt, ist Aufgabe der Algorithmik.

Für alle effizient lösbaren Probleme hat die theoretische Informatik die Klasse P definiert.

Für einige ineffizient lösbaren Probleme wurde die Klasse NP definiert. Sie spielt

eine besondere Rolle. NP enthält alle effizient lösbaren Probleme und alle effizient aber

nichtdeterministisch lösbaren Probleme. Ein nichtdeterministisch lösbares Problem besitzt

nur eine Art von einem Algorithmus. An bestimmten Stellen gibt es in der Problemlösungsvorschrift

mehrere Möglichkeiten, wie weitergerechnet werden kann. Damit kann natürlich

kein Rechner arbeiten, der braucht am Ende eines Schrittes nur eine Möglichkeit für den

nächsten Schritt. Will man diesen Nichtdeterminismus loswerden, muss man alle Möglichkeiten

an den Verzweigungsstellen hintereinandersetzen und landet bei einem superpolynomialen

Algorithmus. Der ist ineffizient, aber dafür ein Algorithmus. NP steht also für

nichtdeterministische Polynomialzeit. Ob in NP wirklich Probleme liegen, die nicht auch

in polynomialer Zeit berechenbar sind, ist bis heute nicht bewiesen. Zur Zeit gibt es darin

jede Menge Probleme, für die noch kein effizienter Algorithmus (ein Algorithmus, der

in polynomialer Zeit deterministisch rechnet) gefunden wurde, wie das Handelsreisendenproblem,

das Rucksackproblem und noch viele andere Probleme mehr. Dies ist eins der

berühmtesten offenen Probleme der theoretischen Informatik:

Ist P = NP ?

Das heißt soviel wie: Gibt es für alle bis jetzt nur ineffizient lösbaren Probleme aus NP

nicht doch einen effizienten Algorithmus?

Oder kann das Gegenteil gezeigt werden, dass es unmöglich ist, effiziente Algorithmen

für Probleme aus NP zu finden, womit dann P ≠ NP gelten würde.

Besonders interessant ist, dass es in der Klasse NP Probleme gibt, die effizient (in

polynomialer Zeit) in einander überführt werden könnnen. Das heißt, fände man einen effizienten

Algorithmus für eins dieser Probleme, würde dieser alle anderen Problem dieser

Art auch lösen. Diese Unterklasse von NP nennt man die NP-vollständigen Probleme.

10


Zum Beispiel ist das Rucksachproblem und das Problem des Handelsreisenden so ein Fall.

Könnte man das Problem des Handlesreisenden effizient lösen, kann man dieselbe Algorithmusidee

für das Rucksackproblem verwenden und für viele andere Problem mehr.

Weiter gibt es noch solch widerborstigen Probleme, wo weder gezeigt werden kann,

dass sie NP-vollständig sind, noch ist bis jetzt ein effizienter Algorithmus bekannt. So ein

Problem ist das

Faktorisierungsproblem

Gegeben sei eine Nichtprimzahl N, und gesucht sind 2 Primzahlen (natürliche Zahlen, die

nur durch sich selbst und 1 teilbar sind) p und q mit N = p ∗ q.

Bemerkung: Bis jetzt war die Eingabe n die Anzahl von Elementen, z.B. Städte, oder

Waren. Beim Faktorisierungsproblem oder weiter unten beim Primzahlproblem ist die Eingabe

eine Zahl. Das Schwierige des Problems hängt von der Größe der natürlichen Zahl

ab. Die Größe der Zahl wird umgerechnet in die Anzahl der Bits (0 und 1), die die Zahl in

Binärdarstellung an Speicher in Anspruch nimmt. Diese Anzahl ist nun die Eingabegröße.

Ein ähnlich schweres Problem war das Primzahlproblem, dessen Komplexität lange

offen war.

Primzahlproblem

Für eine gegebene natürliche Zahl soll festgestellt werden, ob sie eine Primzahl ist oder

nicht. Ein ineffizienter, sehr alter und berühmter Algorithmus ist das Sieb des Eratosthemes:

• Beginnend bei 2 streicht man jede zweite der 2 folgenden natürlichen Zahlen.

• Danach streicht man ab der 3 jede dritte Zahl,

• ab der 5 jede fünfte Zahl (die 4 wurde schon gestrichen, als man jede zweite Zahl der

2 folgend wegstrich) und immer so weiter.

Am Ende bleibt eine Folge von Primzahlen übrig. Die Laufzeit dieses Algorithmus ist superpolynomial,

also ineffizient. Primzahlen sind z.B. in der Kryptografie sehr wichtig, wie

wir im nächsten Abschnitt sehen werden. Zahlen testet man auf die Primzahleigenschaft

11


heutzutage mit probabilistischen Algorithmen (benutzen im Laufe ihrer Rechnung Zufallszahlen),

die effizient rechnen, aber für eine gegebene Zahl nur mit Sicherheit sagen, dass

sie keine Primzahl ist, ansonsten nur mit sehr hoher Wahrscheinlichkeit sagen, dass sie

eine Primzahl ist. In extrem wenigen Fällen wird von einer Zahl behauptet, dass sie eine

Primzahl wäre, aber eigentlich gar keine ist. In der Praxis sind alle zufrieden, weil der

Algorithmus sehr schnell rechnet. Theoretisch äußerst überraschend war es, als 2002 M.

Agrawal, N. Kayal und N. Saxena sehr elegant bewiesen, das der Primzahltest doch ein effizient

lösbares Problem ist, also in P liegt. Der Primzahlbeweis soll so einfach und genial

sein, dass man sich fragen könnte, ob nicht auch für andere ineffiziente Algorithmen doch

noch effiziente Algorithmen gefunden werden könnten.

5 Nützliche ineffizient berechenbare Probleme

In der Kryptografie werden zum Verschlüsseln von Nachrichten Algorithmen benutzt, deren

Entschlüsselungsalgorithmen für nicht legale Interessenten bis heute nur ineffiziente

Laufzeiten haben. In der modernen Kryptografie geht es darum, dass sich 2 Menschen

Nachrichten schicken wollen, ohne dass jemand, der alles mitlesen kann, sie versteht. Eine

Person A, nennen wir sie ALICE, will an eine andere Person B, z.B. BOB, eine verschlüsselte

Nachricht schicken, die nur BOB entschlüsseln kann.

Allgemeiner Verschlüsselungsablauf

SENDER

EMPFÄNGER

Klartext

Geheimtext

ALICE

BOB

+ Schlüssel − − − − − − − > + Schlüssel

+ Algorithmus + Algorithmus

− − − − − − −

− − − − − − −−

= Geheimtext = Klartext

12


Die Sicherheit eines Kryptosystems darf nicht von der Geheimhaltung des Algorithmus

selbst abhängen, sondern nur von der Geheimhaltung des Schlüssels.

Wie einigt man sich über den Schlüssel? Die Paradoxie besteht darin, dass der Schlüssel

selbst auch ein Geheimnis ist. Wie kann ohne ein Treffen von ALICE und BOB ein Schlüssel

festgelegt werden, mit dem BOB an ALICE Nachrichten schicken kann, die niemand anderes

verstehen kann?

Die Diffie-Hellman-Vorhängeschloßidee

ALICE will eine geheime Nachricht an BOB schicken:

1. ALICE legt eine Nachricht in eine Eisenkiste, verschließt diese mit einem Schlüssel

und behält diesen.

2. ALICE schickt die Eisenkiste mit der Post.

3. BOB hängt ein eigenes Vorhängeschloß an die Kiste und behält den Schlüssel zu

seinem Schloß.

4. BOB schickt die Kiste zurück an ALICE mit der Post.

5. ALICE nimmt ihr Schloß von der Kiste ab und schickt die Kiste mit der Post an BOB

zurück.

6. BOB muß nun nur noch sein eigenes Vorhängeschloß öffnen und kann die Nachricht

lesen.

Kein Schlüssel wurde ausgetauscht und trotzdem konnte niemand anderes außer BOB die

Nachricht von ALICE lesen!

Bemerkung: Das Päckchen musste allerdings dreimal hin und her wandern.

Gucken wir uns dieselbe Idee mit Zahlen zur Schlüsselvereinbarung zwischen ALICE

und BOB an: ALICE will mit BOB einen gemeinsamen Schlüssel festlegen mit Hilfe der

Funktion 7 x mod11

1. ALICE wählt eine Zahl (z.B. 3), nennt sie A und hält sie geheim.

2. BOB wählt eine Zahl (z.B. 6), nennt sie B und hält sie auch geheim.

13


3. ALICE berechnet mit ihrer Zahl A 7 A mod11 = 7 3 mod11 = 343mod11 = 2 = α.

4. BOB berechnet mit seiner Zahl B 7 B mod11 = 7 6 mod11 = 117649mod11 = 4 = β.

5. ALICE schickt α an BOB.

6. BOB schickt β an ALICE.

7. ALICE nimmt BOBs Zahl β und berechnet β A mod11 = 4 3 mod11 = 64mod11 = 9

8. BOB nimmt ALICEs Zahl α und berechnet α B mod11 = 2 6 mod11 = 64mod11 = 9

9 ist der gemeinsame Schlüssel, den beide nun zum Verschlüsseln von Nachrichten benutzen

können. Niemand sonst kann diesen Schlüssel in vernünftiger Zeit berechnen, auch

wenn er α, β und 7 x mod11 kennt, wenn A und B sehr groß gewählt wurden.

Bemerkung: Auch hier wurden Nachrichten dreimal hin und her gesendet, bis der

Schlüssel zustande kam.

Was hat es mit der Funktion Y X modZ auf sich? Diese Funktion ist eine sogenannte

Einwegfunktion, d.h. sie ist fast nicht umkehrbar. Es ist allerdings offen, ob es Einwegfunktionen

wirklich gibt. Vorstellen kann man sich das wie bei der Telefonbuchsuche, wenn man

nach einer bestimmten Telefonnummer suchen würde, um die Adresse zu erfahren. Das ist

ein sehr aufwendiges Vorgehen, weil im Grunde genommen das Telefonbuch von Anfang

bis Ende durchsucht werden müsste. Die Funktion Y X modZ heißt diskreter Logarithmus.

Ein anderer Kandidat einer Einwegfunktion wird im RSA-Verfahren (benannt nach den

Erfindern R. Rivest, A. Shamir, L. Adleman) zum Verschlüsseln von Nachrichten verwendet.

Hierbei wird die Idee ausgenutzt, das jeder in einen Briefkasten etwas durch den Schlitz

werfen kann, aber nur der mit dem Schlüssel den Briefkasten öffnen kann. Das Verfahren ist

ein asymmetrisches Verfahren und gehört zur Public-Key-Kryptografie. Zum Verschlüsseln

und Entschlüsseln werden verschiedene Schlüssel benutzt, einer davon ist allen bekannt, also

öffentlich. Diesmal wird das Faktorisierungsproblem ausgenutzt.

Das folgende Beispiel erklärt den Verlauf des RSA-Verfahrens.

RSA-Verfahren

BOB will an ALICE die wichtige Nachricht m = 2 (Text wird durch Zahlen codiert, z.B.

kann 2 bedeutet, dass ALICE das Heiratsangebot annimmt) verschlüsselt schicken:

14


1. ALICE wählt zwei Primzahlen p und q und berechnet n = p ∗ q, z.B. p = 3 und

q = 11, womit n = 3 ∗ 11 = 33 ist.

2. ALICE berechnt eine Zahl d wie folgt d = (p − 1)(q − 1). Für unser Zahlenbeispiel

ist d = 2 ∗ 10 = 20.

3. ALICE wählt weiterhin zwei Zahlen e und f so, dass e ∗ f/d den Rest 1 hat, z.B.

e = 7 und f = 3, weil 21/20 den Rest 1 hat.

4. ALICE veröffentlich die beiden Zahlen n und e als ihre öffentlichen Schlüssel, mit

Hilfe derer man ihr geheime Nachrichten schicken kann. Die Zahl f ist ihr privater

Schlüssel, den sie niemals herausgibt.

5. BOB besorgt sich diese beiden öffentlichen Zahlen n und e (also 33 und 7) und

verschlüsselt seine Nachricht m wie folgt: m e /n. Den Rest dieser Berechnung, bezeichnet

mit k, schickt er an ALICE. Für unser Zahlenbeispiel ist k der Rest von

2 7 /33, also 29.

6. ALICE entschlüsselt die geheime Nachricht von BOB mit ihrem privaten Schlüssel

wie folgt: k f /n. Der Rest ist die Nachricht, die BOB ihr verschlüsselt schicken wollte,

also 29 3 /33 = 24389/33 = 739 mit Rest 2, was die Nachricht m ist.

Im wirklichen Leben sind die privaten und öffentlichen Schlüssel viel größer, mindestens

im Bereich von 10 500 . Die Primzahlen werden gewählt, indem irgendeine Zahl zufällig

gewählt wird und überprüft wird, ob sie eine Primzahl ist. Ist sie keine, wird der Vorgang

wiederholt. Es gibt genügend viele Primzahlen. Bis zur natürlichen Zahl n gibt es ungefähr

1/log n Primzahlen, was bedeutet, dass ungefähr nach log n vielen Versuchen (was wirklich

nicht viel ist) eine Primzahl gefunden ist.

Bemerkung: Hier ist es nicht nötig, dass Nachrichten mehrmals hin und her wandern

müssen, um einen geheimen Schlüssel zu beschließen. Beim online-Banking wird dieses

Verfahren benutzt, um einen Schlüssel für eine symmetrische Verschlüsselung für den weiteren

Bankverkehr zwischen Kunden und Bank fest zu legen.

15


6 Effiziente berechenbare Probleme

Effizient lösbare Probleme sind Probleme, für die Algorithmen gefunden werden können,

die in polynomialer Zeit ein Ergebnis berechnen. Die Eingabegröße geht bei der Rechenzeit

in die Basis der Laufzeitfunktion ein und nicht in den Exponenten. Weiterhin haben

solche Polynomialzeitalgorithmen die schöne Eigenschaft, dass sie beliebig hintereinander

ausführbar und kombinierbar sind und immer noch ein Polynomialzeitalgorithmus am

Ende entsteht (Abgeschlossenheit unter Addition, Multiplikation und Komposition).

Bemerkung: Für kleine Eingabegrößen können ineffiziente Algorithmen dennoch sinnvoll

sein.

6.1 Sortieren

Sortieren ist ein grundlegendes Problem in der Informatik. Man sagt, Rechner verbringen

25% ihrer Zeit damit. Die Aufgabe besteht darin, gegebene Elemente in die richtige

Reihenfolge zu bringen. Es gibt bei n gegebenen Elementen n! verschiedene Reihenfolgen

der Elemente, wobei nur eine die richtige (gewünschte) Reihenfolge ist. Das Ergebnis

beim Sortieren - eine sortierte Liste - wird z.B. bei der Suche benutzt. Alle Suchalgorithmen

gehen von einer sortierten Liste aus. Die Dinge müssen jeweils einem Schlüssel

zugeordnet sein, und für die Schlüssel muss es eine klar definierte Ordnung geben (numerisch/alphabetisch).

Aber es müssen nicht alle n! Fälle der verschiedenen Sortierungen auf

die eine richtige durchsucht werden. Sortieren von Elementen ist ein effizient lösbares Problem.

Es gibt sehr viele effiziente Algorithmen, manche sind besser und andere schlechter.

Zum Beispiel benutzen die meisten Menschen einen von beiden folgenden Algorithmen

zum Sortieren von Skatkarten.

Sortieren durch Auswählen (Selection)

1. Man wartet, bis alle Karten ausgeteilt sind und nimmt dann den ganzen Stapel mit

einmal auf.

2. Dann sucht man jeweils den größten Kartenwert, und tauscht diese Karte an die letzte

Stelle.

3. Diese letzte Karte ist dann schon an der richtigen Stelle. Dasselbe macht man dann

16


mit den übrigen Karten. Die nächst kleinere Karte wird an die vorletzte Stelle getauscht

und immer so weiter.

Wie lange diese Art des Sortierens braucht, hängt natürlich sehr von der Sortierung der

Karten ab.

Beispiel - Selection-Sort: gegebene Zahlenfolge: 9 5 1 6 2 4 3

1. Runde - größte Zahl nach rechts tauschen: 3 5 1 6 2 4 9

2. Runde - nächstgrößere Zahl an die vorletzte Stelle tauschen: 3 5 1 4 2 6 9

3. Runde: 3 2 1 4 5 6 9

4. Runde: 1 2 3 4 5 6 9

Sortieren durch Einfügen (Insertion)

1. Man nimmt die erste ausgeteilte Karte sofort auf die Hand.

2. Jede weitere ausgeteilte Karte fügt man je nach Reihenfolge entweder vor die Karte,

die man schon auf der Hand hält, oder dahinter.

3. Hat man mehr als eine Karte auf der Hand, fügt man jede weitere Karte entweder vor

die erste Karte aller Karten auf der Hand, oder hinter die letzte Karte oder zwischen

2 Karten, je nachdem in welche Lücke die neue Karte gehört.

4. Das macht man so lange, bis alle Karten aufgenommen wurden.

Beispiel - Insertion-Sort: gegebene Zahlenfolge: 9 5 1 6 2 4 3

1. Runde - Hinzunahme der 9: 9

2. Runde - Hinzunahme der 5: 5 9

3. Runde - Hinzunahme der 1: 1 5 9

4. Runde - Hinzunahme der 6: 1 5 6 9

5. Runde - Hinzunahme der 2: 1 2 5 6 9

17


6. Runde - Hinzunahme der 4: 1 2 4 5 6 9

7. Runde - Hinzunahme der 3: 1 2 3 4 5 6 9

Eine total einfache Art, Dinge zu sortieren, führt der Bubble-Sort-Algorithmus.

Sortieren durch Vertauschen: Bubble-Sort

1. In einer gegebenen Liste von zu ordnenden Elementen tauscht man beginnend beim

ersten Element 2 benachbarte Elemente, wenn das erstere Element größer als das

folgende ist.

2. Dies macht man solange, bis keine Vertauschungen mehr nötig sind.

Der Name kommt von der Tatsache, dass sich verschieden große aufsteigende Blasen

( ”

Bubbles“) in einer Flüssigkeit quasi von alleine sortieren, da größere Blasen die kleineren

Dieser Algorithmus ist sehr simpel, aber nicht schnell. Im besten Fall (best

”überholen“.

case) liegt eine sortierte Liste vor, was nach einem Durchlauf festgestellt werden kann. Der

schlechteste Fall (worst case) wäre eine rückwärts sortierte Liste, wo bei jedem Durchlauf

das kleinste Element nur eine Position nach vorne rückt.

Beispiel - Bubble-Sort: gegebene Zahlenfolge: 9 5 1 6 2 4 3

1. Runde: 5 1 6 2 4 3 9

2. Runde: 1 5 2 4 3 6 9

3. Runde: 1 2 4 3 5 6 9

4. Runde: 1 2 3 4 5 6 9

Der am häufigsten verwendete, sehr schnelle Sortieralgorithmus ist der Quicksort-Algorithmus.

Quicksort-Algorithmus

1. In der Liste der zu sortierenden Elemente wird ein beliebiges Element als Referenzelement

festgelegt, z.B. das letzte Element in der gegebenen Liste, wir nennen es

R.

18


2. Danach wird von links nach rechts in der Liste nach einem Element gesucht, dass

größer als das Referenzelement R ist. Dieses merkt man sich, als z.B. A.

3. Nun wird von rechts nach links nach einem Element in der Liste gesucht, das kleiner

als das Referenzelement R ist, aber rechts von A in der Liste liegt. Findet man so ein

Element, so nennen wir es B.

4. Hat man zwei Elemente A und B gefunden, tauscht man sie aus und beginnt wieder

bei Schritt 1.

5. Konnte man nur A finden, tauscht man A gegen das Referenzelement R aus. Das

Referenzelement steht nun schon genau an der richtigen Stelle in der Liste. An dieser

Stelle wird die Liste geteilt. Man erhält eine Liste mit den Elementen vor dem

Referenzelement und eine Liste mit den Elementen nach dem Referenzelement.

6. Mit diesen beiden neuen Listen beginnt man jeweils wieder bei Schritt 1 und fährt

solange damit fort, bis man bei Listen mit einem oder keinem Element angelangt ist.

Die Technik der Aufteilung der Liste von Elementen in zwei Teillisten heißt Teile- und

Herrsche. Günstig ist es, wenn immer ungefähr zwei gleich große Teillisten entstehen.

Die Technik, dass auf die zwei entstandenen Teillisten immer dasselbe Prinzip angewendet

wird, heißt Rekursion.

Beispiel - Quicksort: gegebene Zahlenfolge: 9 5 1 6 2 4 3 (Referenzelement ist fett

dargestellt)

• Ausgangsliste: 9 5 1 6 2 4 3

1. Tausch von 9 und 2: 2 5 1 6 9 4 3

2. Tausch von 5 und 1: 2 1 5 6 9 4 3

3. Es konnte nur A=5 gefunden werden, kein B, das rechts von A liegt. Deshalb

wird das Referenzelement 3 gegen 5 ausgetauscht: 2 1 3 6 9 4 5

• Das ehemalige Referenzelement 3 steht jetzt an der richtigen Stelle in der Liste. Die

Ausgangsliste wird nun zum ersten Mal geteilt bei 3.

• Es entstehen zwei Teillisten: 2 1 und 6 9 4 5

19


• Linke Teilliste: 2 1 (Tausch von 2 und 1 und man ist fertig)

• Rechte Teilliste: 6 9 4 5

1. Tausch von 6 und 4: 4 9 6 5

2. Es konnte nur A=9 gefunden werden, kein B, deshalb wird das Referenzelement

5 gegen 9 ausgetauscht: 4 5 6 9

• Die 5 steht jetzt an der richtigen Stelle in der Liste. Die Liste wird wiederum beim

ehemaligen Referenzelement 5 geteilt.

• Es entstehen wiederum zwei neue Teillisten: 4 und 6 9

• Die linke Teilliste ist eine Einerliste, es ist also nichts mehr zu tun.

• Die rechte Teilliste ist auch schnell erledigt: 6 9.

• Somit wurde die gesamte Liste sortiert: 1 2 4 5 6 9

Berechnet man die Laufzeit von diesen Algorithmen bei einer durchschnittlich vermischten

gegebenen Liste von Elementen, kommt der Quicksort-Algorithmus am besten

weg. Die Laufzeit wird beschränkt von der Funktion O(n log(n)), die also mit zunehmenden

Elementen der zu sortierenden Liste nur langsam wächst.

6.2 Suchalgorithmen

Gesucht wird immer in sortierten Listen. Hier zwei Beispiele für einen Suchalgorithmen:

Sequenzielle Suche

1. Gestartet wird mit dem ersten Element der Liste.

2. Ist dies nicht das gesuchte Element, überprüft man das nächste Element in der Liste

solange, bis das gesuchte Element gefunden wurde.

Hat man Pech, sucht man bei einer Liste von Elementen bis zum Ende, das heißt, das

gesuchte Element kam gar nicht vor. Hat man Glück, ist das gesuchte Element gleich das

erste.

Sucht man einen Namen in einem Telefonbuch, macht man das üblicherweise wie folgt:

20


Binäre Suche

1. Man wählt das mittlere Element der Liste und

2. prüft, ob der gesuchte Wert in der vorderen oder in der hinteren Hälfte der Liste liegt,

also ob der gesuchte Wert kleiner oder größer als dieses mittlere Element ist.

3. Liegt das gesuchte Element in der vorderen Hälfte, so geht man wie eben beginnend

mit Schritt 1 mit der vorderen Hälfte als Ausgangsliste vor.

4. Genauso geht man vor, wenn das Element in der hinteren Hälfte liegt.

5. Dies wiederholt man solange, bis das gesuchte Element gefunden wurde oder klar

ist, dass es gar nicht in der gegebenen Liste vorkommt.

7 Algorithmenentwurfstechniken

Da ist ein Problem und gesucht ist ein Algorithmus. Wie findet man einen Algorithmus?

Das einfachste ist immer die Suche nach der sogenannten HOLZHAMMERLÖSUNG. Ist

die gefunden und der Rechner tut, was diese Lösung vorschlägt, können wir immer noch

nach besseren, d.h. effizienteren Algorithmen, suchen. Dabei kann der Holzhammeralgorithmus

wunderbar zum Testen gut sein.

Aber auch die Suche nach dem einfachsten Algorithmus kann sehr schwer sein.

Einige Techniken, um Algorithmen zu entwerfen, sind folgende:

• Greedy-Algorithmen

• Rekursive Algorithmen

• Teile und Herrsche

• Backtracking

7.1 Greedy-Algorithmen

Alltagsbeispiel

Unser ganzes Leben läuft in der Regel nach der Greedy-Methode ab. Wir treffen vernünftigerweise

immer die im Moment optimalst erscheinende Entscheidung für unseren nächsten

21


Lebensschritt. Da die Zeit nicht zurückdrehbar ist, sind dies Schritte, die nicht rückgängig

zu machen sind, auch wenn die damalige Entscheidung sich als Fehlentscheidung entpuppt.

Anderes typisches Beispiel:

Auf Geldbeträge unter 1 EUR soll Wechselgeld herausgegeben werden. Zur Verfügung stehen

ausreichend viele Münzen mit den Werten 50, 20, 10, 5, 2, 1 Cent. Das Wechselgeld

soll aus so wenig Münzen wie möglich bestehen, z.B.

78 Cent = 50 + 20 + 5 + 2 + 1.

Greedy-Prinzip:

• Wir nehmen jeweils immer die größte Münze aus 50, 20, 10, 5, 2, 1 Cent, so dass noch

nicht der Zielbetrag erreicht wird und ziehen diesen Münzbetrag von dem Zielbetrag

ab.

• Das machen wir solange, bis der Restbetrag gleich Null ist.

Eine Kassiererin geht genau nach diesem Algorithmus vor. Unsere deutsche Münzeinteilung

ist so gewählt, dass der Algorithmus immer die beste Lösung berechnet. Angenommen

wir hätten die Münzen mit Werten 11, 5, und 1 gegeben und der Zielwert sei 15.

Obiger Greedy-Algorithmus würde folgendes Ergebnis berechnen:

15 = 11 + 1 + 1 + 1 + 1

Die beste Lösung, also die mit den wenigsten Münzen, ist aber: 15 = 5 + 5 + 5

Greedy-Algorithmen berechnen nur ein lokales Optimum.

Greedy-Algorithmenschritte allgemein:

• Wahl derjenigen Alternative unter mehreren gegebenen, die aktuell optimal erscheint

diese Wahl kann nie mehr rückgängig gemacht werden

• man braucht eine Bewertungsfunktion, um die beste Alternative für die Lösung des

Teilproblems zu finden

22


Der Vorteil solcher Algorithmen sind kurze Laufzeiten, da der Lösungsraum auf einem

wohldefinierten und geradlinigen Pfad durchlaufen wird.

Der Nachteil ist, dass die erhaltene Lösung nicht die beste Lösung sein muss.

Dieses Algorithmusprinzip wird für ineffizient lösbare Probleme verwendet, um Annäherungslösungen

zu berechnen, z.B. zur Berechnung einer Rundreise für den Handelsreisenden

oder zur Berechnung einer Rucksackpackung für den Kaufhausdieb.

7.2 Rekursive Algorithmen

Beispiel Turm von Hanoi:

Gegeben sind 3 Stäbe, wobei auf Turm 1 n Scheiben unterschiedlicher Größe liegen. Die

Stäbe 2 und 3 sind leer. Ziel ist es, alle Scheiben von Stab 1 nach Stab 3 zu bewegen. In

einem Arbeitsschritt darf die oberste Scheibe eines Scheibenturms entfernt und oben auf

einen anderen Turm gelegt werden. Niemals darf eine große Scheibe auf einer kleineren

liegen.

Arbeitsanleitung der alten Weisen im Kloster von Hanoi:

Tragt zuerst alle Scheiben bis auf die untereste zu dem Turm 2. Zu diesem Zwecke

bedient euch des gleichen Verfahrens wieder. Dann könnt ihr die letzte Scheibe zu ihrem

Zeile, Turm 3 tragen. So dies getan ist, holet die anderen Scheiben vom Turm 2 und bringt

sie zu ihrem Ziele, auch hierzu könnt ihr wieder die nämliche Methode verwenden.

Rekursive Algorithmen sind also Algorithmen, die sich selbst aufrufen. Solche Algorithmen

werden sehr typisch im Zusammenhang mit dem Teile und Herrsche Prinzip verwendet.

7.3 Teile und Herrsche

Alltagsbeispiel

Der Quick-Sort-Algorithmus aus dem vorherigen Kapitel ist ein typischer Vertreter vom

Teile und Herrsche Prinzip. Man benutzt diesen Entwurf besonders da, wo das Problem

durch seine Größe schwierig wird.

23


Prinzip:

• Teile: Rekursive Rückführung auf identisches Problem mit kleinerer Eingabemenge.

• Herrsche: Wenn das Problem klein genug ist, löst man das Problem.

Teile und Herrsche Schritte allgemein:

1. Teile das gegebene Problem in mehrere getrennte Teilprobleme auf,

2. Löse (Beherrsche) diese Teilprobleme einzeln und

3. Setze die Lösungen des ursprünglichen Problems aus den Teillösungen zusammen.

4. Wende dieselbe Technik auf jedes der Teilprobleme an, dann auf deren Teilprobleme

usw., bis die Teilprobleme klein genug sind, dass man eine Lösung explizit angeben

kann.

Es ist wichtig, dass jedes Teilproblem von derselben Art ist wie das ursprüngliche Problem,

so dass es mit demselben Algorithmus gelöst werden kann.

7.4 Backtracking-Algorithmen

Alltagsbeispiel

Backtracking benutzt man im Alltag, wenn man auf der Suche nach einer Adresse ist via

einer ungenauen Wegbeschreibung. Während der Suche hat man Momente, wo man realisiert,

dass eine bestimmte eingeschlagene Route nicht zur Adresse führen wird. In der

Regel geht man dann den Weg soweit zurück, bis man zu einem Punkt kommt, wo man

einen neuen Versuch in eine andere Richtung unternimmt. Das wird man dann solange so

durchziehen (außer man fragt jemanden nach dem Weg), bis man bei der gesuchten Adresse

angelangt ist.

Das Backtracking-Prinzip erlaubt also, Teillösungen rückgängig zu machen, wenn diese

nicht zur Lösung des Gesamtproblems führen, und neue Alternativen auszuprobieren. Dies

geschieht über rekursive Aufrufe. Teillösungen können bis zu einer bestimmten Schritttiefe

rückgängig gemacht werden, das heißt, man kann Sackgassen wieder verlassen.

Typische Einsatzgebiete von Backtracking:

24


• Labyrinth-Suche

• Spielprogramme (Schach)

• Planungsprobleme, Konfigurationen

Auch wenn es noch so viele Entwurfstechniken gibt, braucht man bei der Suche nach

einem Algorithmus Intuition und Kreativität.

25

Weitere Magazine dieses Users
Ähnliche Magazine