Mark us von Rimscha Algor ithmen kompakt und verständlich
Markus von Rimscha
Algorithmen kompakt und verständlich Lö...
75 downloads
1023 Views
23MB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
Mark us von Rimscha Algor ithmen kompakt und verständlich
Markus von Rimscha
Algorithmen kompakt und verständlich Lösungsst rategien am Compute r 2., überarbeitete und ergänzte Auflage Mit 60 Abbildungen und 35 Tabellen STUDIUM
VIEWEG+ TEUBNER
Bibliografische Information der Deutschen Nationalbibliothek Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nat ionalbibliograf ie; detaillierte bibliografische Daten sind im Internet über abrufbar.
Das in diesem Werk enthaltene Programm-Material ist mit keiner verpftichtung oder Garantie irgendeiner Art verbunden. Der Autor übernimmt infolgede ssen keine Verant wortung und wird keine daraus folgende oder sonstige Haltung übernehmen, die auf irgendeine Art aus der Benutzung dieses Programm-Materials oder Teilen davon ents teht. Höchste inhaltliche und technische Qualität unserer Produkt e ist unser Ziel. Bei der Produktion und Auslieferung unserer Bücher wollen wir die Umwelt schonen: Dieses Buch ist auf säurefreiem und Chlorfrei gebleich tem Papier gedruckt. Die Einschweißfolie besteht aus Polyäthylen und damit aus organischen Grundstoffen, die weder bei der Herstellung noch bei der Verbrennung Schadstoffe freisetzen.
Verlag und Autor weisen darauf hin, dass keine Prüfung vorgenommen wurde, ob die Verwertung der im Buch beschriebenen Algorit hmen und Verfahren mit Schutzrechten Dritter kollidie rt . Verlag und Autor schließen insofern jede Haftung aus.
1. Auflage 2008 2., überarbeitete und ergänzte Auflage 2010 Alle Rechte vorbehalten © Vieweg +Teubner I GWV Fachverlage GmbH, Wiesbaden 2010 lektorat: Christel Roß
I Walburga Himmel
Vieweg +Teubner ist Teil der Fachverlagsgrup pe Springer Seience-Business Media. www.viewegteubner.de Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Jede Verwertung außerhalb der engen Grenzen des Urheberrechtsgesetzes ist ohne Zustimmung des Verlags unzulässig und strafbar. Das gilt insbesondere für vervielfältigungen, Übersetzungen , Mikroverfilmungen und die Einspeicherung und Verarbeitung in elektronischen Systemen . Die Wiedergabe von Gebrauchanamen, Handelsnamen. Warenbezeichnungen usw. in diesem Werk berechtigt auch ohne besondere Kennzeichnung nicht zu der Annahme, dass solche Namen im Sinne der Warenzeichen- und Markenschutz-Gesetzgebung als frei zu betrachten wären und daher von jedermann benutzt werden dürften. Umschlaggestaltung: Künkell.opka Medienentwicklung, Heidelberg Druck und bucbbinderische Verarbeitung: Ten Brink, Meppel Gedruckt auf säurefreiem und chlorfrei gebleich tem Papier. Printed in t he Netherlands ISBN 978 -3-8348-0986-5
Vorwort Sowohl bei de r praktischen Arbeit in de r Software- Entwicklung, als auch im Rah men meiner unter richtenden Täti gkeit werde ich immer wieder gefragt, wie d ie eine oder andere Aufg abe am Rechner möglichst geschickt zu lösen sei. Selbstvers tändlich gibt es ein breites Sortiment an Fachbüchern zu qu asi jedem Thema der Software-Entwicklung, also auch zu Problemlösungsst rateg ten am Compu ter - Algor ithmen eben. Nicht selten umfassen d iese jedoch hunderte von Seiten od er gar meh rere Bände, sind in Englisch gesc hrieben oder konzent rieren sich auf ei nen bestimmten The menbereich wie etwa Verfah ren der Künstlichen Intelligenz oder wiederu m einen Teilas pekt wie Ne urona le Ne tze. Der rote Faden geh t hier all zu schne ll verlo-
ren. Im Ergebnis wü nsc hen sich vie le Software-Ent wickler' einen kom pak ten Leitfaden, u m Probleme selbstständig anzugehen . Zwar sind zah lreiche Verfa hre n für konkrete Au fgabe n be kannt und können im Zweifelsfall in der Literatu r na chgesch lagen werden. Wenn aber eine neue oder sehr spe zielle Herau sforderu ng auftaucht, ist es an der Zei t, selbst eine passende Lösung zu entwickeln. Dazu mu ss die jeweil ige Idee hinter d en existierenden Verfahr en bekannt sein. Deren Details bezüglich einzel ne r Au fgebenstellungen sin d zu nächs t oft ebenso wenig wichtig wie d as letzte Quäntchen an O ptimieru ngspotenzi al. Beides läss t sich meist nur bezogen auf d ie konkrete Au fgabe nu tzen und ist dami t vo n Mal zu Mal neu zu untersuchen . Was möchte ich Ihnen also in d iesem Buch nahe bri ngen? Wir werden un s hier u nterschied liche gr undsä tzliche Strategien ansehen, w ie man am Computer Problem e lösen kann . Wir we rden lernen, wie ma n das schnell und elegan t tu t, und ein Gefü hl da für entwic keln, ob ein Verfahren die passende Lösung für unsere Au fgabe ist oder nicht. Wir we rde n anhan d von einfachen Beispi elen un tersch ied liche Strategien system atisch durchgehen, ihre Vor- und Nac hteile kennen lern en und darau s passend e Anwend ungsgebiete ab leiten. Bewu sst werden wir uns mit Beispielen au s unterschied lichsten Bereichen beschä ftigen, seien es Spiels trategten, Gew innmaximieru ng durch O pt imierung oder Mustererkennu ng mit Hilfe Kü nstlicher Intelligenz. Wir werde n uns keines der hier behan delten Them en in voller Tiefe ansehen - d azu sei auf d ie einsc hlägige Literatur verAus Grü nden der Lesbarkeit wird in diesem Buch nicht zwischen der männl ichen und weiblichen Form unterschieden; es sind jeweils beide Geschlech ter gemeint.
v
Vorwort wiese n. Statt dessen gehen w ir nur so weit ins Detai l, w ie es nö tig ist, um ein konkretes und la u ffähiges Beispiel nac hzuvollziehen. Also wird es auch nich t unser Wu nsch sein, a m Ende eine tau sendseitige Abtip p-Vorlage für alle Lebenslagen in de n Hä nden zu ha lten . Vielmehr möchten wir uns mit dem nötigen Hand werkszeug ve rsorgen, um Prob leme kü nftig selbststä nd ig anzugehen . Am Ende werden wir in der Lage sein, Aufgaben a m Compu ter zu löse n, an denen wir bisher vielleicht ge scheitert sind . Ich wende mich mit di esem Buch an alle, d ie bereits ers te Erfahrungen in de r Programmierung gesa m melt haben und wissen, w ie einfac he Funktionen zu programmieren sind. Wir we rden uns hier einige wich tige Met ho den in For m von Pro grammcode ansehen, ausführlichere Beisp iele finden sich im O n lin e -Bereich d ieses Buchs u nter http://www.viewegteubner.de/on 1i nep1USo Um de n roten Faden nic ht aus d en Augen zu verlieren, werden wir auf viele Fehlerabfragen e te. verz ichten, die zwar sinnvoll wären, den Code abe r unübers ichtlich machen. Mein Dank gilt me ine n Elte rn u nd allen, d ie mich bei m Schreiben d ieses Buches un te rstüt zt haben, insbesondere Robe n . Ich w ünsche Ihnen nun viel Spaß beim Lesen und Erfolg bei der Umsetzu ng! Markus vo n Rimscha Ju li 2008
Vorwort zur zweiten Auflage Ich habe mich se hr üb er d as rege Interesse an d iesem Bu ch gefreut. Dank d es kons truktive n Feedbacks und we rtvoller Anregu ngen konn te ich Tipp fehle r beseitige n und habe einige Passagen und Co de -Beispiele e rgänzt. Ich w ünsche Ihnen weiterh in vie l Spaß beim Lesen und Erfolg bei der Umsetzung! Ma rkus vo n Rimscha September 2009
VI
Inhalt 1 Ein führung
1
2 Arten von Algorithme n
3
2.1 Iterative Algorithmen
5
2.1.1 Sortieren
6
2.1.2 wege im Labyri nt h
8
2.1.3 Bewertung 2.2 Rekursive Algori thm en
12 13
2.2.1 Die Tü rme vo n Hanoi
15
2.2.2 Sortiert..
19
"!'
2.2.3 Schach
21
2.2.4 Fraktale und Bildkompression
25
2.2.5 Bewertung
34
2.3 Dynami sche Algorithmen
37
2.3.1 Fibonacci-Zahl en
37
2.3.2 Bewertung
40
2.4 Heu ristische Algorithmen
41
2.4.1 Sortieren
42
2.4.2 Bewertung
46
2.5 Zufa llsge steu erte Algorith men
47
2.5.1 Metropolis-Alg orithmu s un d Simu lated Annealin g
47
2.5.2 Bewertung
52
2.6 Gen etische Algori thme n
53
2.6.1 Rucksack-Problem
55
2.6.2 Gew innmaximierung
57
2.6.3 Be :ertu ng
60
2.7 Probebili stische Algorithm en
61
2.7.1 Mult ip likationstest
62
2.7.2 Primzahl test
64
2.7.3 Bewertu ng
66
VII
Inhalt 3 Effizienz eines Algorithm us
67
3.1 Wachstum
68
3.2 Bewertung eines Algorithmus
72
3.2.1 Average-Case und w orst-Case
74
3.2.2 Minimaler Aufwand
75
3.3 Laufzeit un d Speicher
77
3.4 Par allele Verar beit un g
78
3.4.1 Paralle le Algori thmen
78
3.4.2 Paralle le Progra mm ierung
80
3.5 Übersicht
90
3.6 Nutzung pr akt isch unlösbare r Probleme
92
4 Wichtige Daten strukturen
4.1 Listen 4.2 Mengen
97 98 100
4.2.1 Sortier te Men gen
100
4.2.2 Unsortierte Mengen
100
4.3 Zuo rdnunge n
102
4.4 Bäume
103
4.5 Graphen
105
5 Künstliche In telligenz 5.1 Maschinelles Lernen
109 113
5.1.1 En tscheidungsbäume
113
5.1.2 Bewertung
128
5.2 Schwa rmintelligenz
129
5.2.1 Ame isen algor ithme n
129
5.2.2 Bewertu ng
140
5.3 Neu ronale Ne tze
141
5.3.1 Hebb'sche Regel
144
5.3.2 Backpr opagation
146
5.3.3 Erwei terungen
149
5.3.4 Bewertu ng
154
Literatu rverzeichnis
155
Stichwo rtv erzeichnis
161
VIII
1 Einführung Manchm al sch reiben wir Softwa re, oh ne u ns genau zu übe rlegen, w ie wir dabei ge nau vorgehe n. Wir arbeiten ins tinktiv, progr ammieren die Lösu ng "e infac h her unter" . Das mu ss nicht unbed ingt schlimm sein, denn es kom mt d urcha us vor, dass ei ne Aufgabe so ei nfach ist, dass wir mit ein we nig Gespür d en richtigen Ansatz wä hlen und u ns tatsächlich keine tiefgründigen Gedanken über d ie Problem lösu ng zu machen brauchen. Die Programmierung selbst ist oft schon schwierig genug: Welche Bibliotheken nu tzen wir? Wie sind d ie Aufrufparameter? Welche Rückgabewerte erhalten wi r? Wie funktioniert d ie Feh lerbeha nd lung? Usw . Und all das, nachd em wir u ns gerade erst widerw illig in eine neue Progr amm iersprache eingea rbeitet hab en, von der neuen Entwicklungsu mgeb ung ganz zu schweigen ... Leider ist d ie Welt aber noch viel komplizierter. Mehr u nd mehr entdecken wir, dass längst nicht nu r d ie Frage "W ie progr am miere ich das?" von Bed eutu ng ist. Ratl os stehe n wir ma nchmal vo r de m grundsätzlichen Problem: " Wie löse ich überh aupt d iese Aufga be?" ... Von der Programmieru ng einmal ganz abgesehen, die uns mit zune hmende r Erfahrung u nd Ken ntni s der verfügbaren Bibliotheken immer weniger Prob leme bereitet. Wir erkennen, dass die erste Idee nicht im mer de r Weisheit letzter Schluss ist, oder kommen überhau pt nicht mehr ans Ziel. Also werde n wir uns im Folgenden zuerst d ie zent rale Frage stellen: Wie löst man Probleme am Compu ter? Wir werd en uns überlegen, was ein Algorithmus überhau pt ist. Anhand einfac her Beispiele we rde n wi r u nterschied lichste Arten von Lösungsverfah ren kennen lernen und dabei schne ll in de r Lage sein, völlig neue Herausford eru ngen zu bewä ltige n. Wir werden sehen, dass sel bst Aufga be n im Hand umd reh en zu lösen sind, die u ns bisher zu r Verzwe iflung gebracht haben . Ist dieser wich tige Schri tt einmal getan, überlegen wir uns, wa nn ein Verfahren gu t ist u nd wann nicht. Was ist überh au pt "gu t" und was ist beispielsweise "schnell"? Ist eine Sekunde schnell? Eine Minu te? Ein Tag? Wann ist ein Algorithmus d ie passende Lösu ng für unser Prob lem u nd wan n müssen wi r nach Alternativen suc hen? Gibt es überha upt Alternativen? Geht es besser? Na chde m wir uns mit d iesen Fragen beschäf tigt haben, werden wi r d ie Lösung n ich t mehr nu r "i rgendwie hinbiegen". Im Ergebnis haben wi r zusä tzlich das ange ne hme Gefü hl, u nsere Sache gu t gemach t zu haben. Mi! diesen Kenntnissen wi rd es uns da nn auch nicht mehr schwer fallen, einige wichtige Date ns trukturen mit ihren Vor- u nd Nac hteile n kennen zu lernen, zu ve r-
1
1 Einführu ng gleichen und d ie jeweils passende Variante für unse re konk rete Anwendu ng zu wäh len. Schließ lich werd en wir es wagen und einen ersten schüchter nen Blick auf eine der Königsd iszip linen der Informatik schlechthin riskieren: Die Künstliche Inte lligen z. Vielleicht hat dieses Thema bisher ein e seh r abschreckende Wirkung au f un s ausg eübt. Sch ließlich muss Künstliche Intelligenz woh l etwas Hochkompliziertes sein. Au fwä nd ige Math ematik, schwierige Programmieru ng ... Oder etwa nicht ? Sollte es möglich sein, berei ts nach einem ersten Überblick über diesen faszinierenden Themenkomplex zu konkreten und greifbaren Resu ltaten zu kommen ?
Online
Zu di esem Buch steh t Ihnen ein Online-Berelch zu r Verfügu ng un ter
http://www. viewegteubner .de/onli neplus
Hier finden Sie An tworten auf die Fragen an den Kap itelenden sowie ein ige Codebeisp iele in Java 1.6. Hierbei hand elt es sich u m Implemen tieru ngen, d ie nur d ie jeweils relevante Idee veranschaulichen sollen. Es sind nich t alle mög lichen Sonderfälle und Fehlersitu ationen beachte t; so bleibt der Code kompakt und einf ach, der rote Fad en geht nicht in Detailfragen verloren.
2
2 Arten von Algorithmen Wir möchten u ns nun mit d er Frage beschäftigen, w ie man Probleme löst. Dazu übe rlege n wir uns zunächst, was ein Algorithmus überhaupt isI IAAl -3]. Ein fach gesa gt handelt es sich dabei um eine Arbeitsanweisung. d ie uns zeigt, wie ei ne Aufgabe zu lösen ist - vorzugsweise am Computer. Für die jeweiligen Anwen d un gsgeb iete gibt es ein breites Spektrum an solchen Verfahren. In unserem alltäglichen Leben kommt ein Kochr ezept d em wohl am nächsten . Eine solche Handlungsan weisu ng soll te einige Eigenscha ften erfüllen: 1.
Ein Algori thmu s is t allgemein gültig. Wir möchten unser Lösungsve rfahre n später au f versch iedene Probleme ansetzen, Im Moment wissen wir noch überh au pt nicht, wie d iese genau au ssehen werden . Das Einzige, wa s wir jetzt schon sagen können , ist, d ass es sich um gleichartige Aufgabenstellungen handeln wird . Deswegen muss ein Algorithmus eine allgemein gül tige Hand lungsan weisung sein, die nicht nu r zu eine m besti mm ten Prob lem, sondern zu allen gleichartigen Aufgaben pass t. Es geht also beispielsweise nicht u m d ie Frage " Wie sortiert ma n 3 Zahlen?" so ndern ganz allgeme in " Wie sor tiert man Zahlen?". Der Algorithmu s soll fun ktionieren, egal ob wir später 3, 7 oder 23847657 zahlen sortieren möc hten .
2.
Ein Algorithmus ist ausführbar. Grundsätzl ich mu ss es möglich sein, d en Algorithmus abzuarbeiten . Dazu mü ssen endlich viele Anweisu ngen eindeu tig, verständlich und in einer kla ren Reihen folge gegeben sein . Für un s Menschen ma g eine in form elle sp rachliche Bes chreibu ng gen ügen, für den Compu ter werden wir uns aber d ie Müh e ma chen mü ssen, u nser Verfahren in eine r Programmiers prache zu formulieren. Eine Anweisung wie " Geh e nach links oder na ch rechts" ist beis pielsweise nicht au sführbar, d enn es ist nicht klar, was tatsäch lich zu tun ist. Wir benötigen eind eutige Vorgabe n, also z.B. " Wenn der Eingabewe rt unge rad e ist, dann gehe nach links, ansonsten nach rech ts" .
3.
Natürlich mu ss ein Algorith mu s zu eine m End e kommen . Diese Forderung klingt vielleicht banal, hat abe r ihre Tücken . Praktisch ge sehen ist ei n Lösungsverfahren natü rlich wertlos, wenn es n icht irgend wa nn mit seiner Berech nu ng fertig wird . Trotzdem werden wi r recht bald Verfahren seh en, d ie ihr em Wesen nach unend lich lange lau fen. Wir we rden u ns also immer Gedanken darüber ma -
3
2 Arte n von Algorithmen chen müssen, wie lange u nser Verfahren rechn et un d ob es ü berhau pt von selbst die Arbeit beendet. Falls d as nicht der Fall sein sollte, bleibt u ns n ichts anderes übrig, als einen Abbruch kün stlich zu erzwingen. Wir werden uns nun einige wichtige Arten von Algo rithmen ansehen u nd uns anhan d einiger Beispiele deren Funktionsweise vor Augen führ en. Die hier getroffene Einteilung wird u ns helfen, die prinzipiellen Un terschied e einzel ne r Verfah ren zu ve rstehen, sie ist aber nicht immer völlig trennscharf, einige Verfahren passen d urchaus in meh rere Kategorien.
IrTlllementierung eines A1gorithroos Wir sollten uns erst mit der konkreten Programmierun g beschäftigen, wenn wir ein Verfahren zur Lösu ng unseres Problems kennen und idea lerweise auch verstanden haben . Tro tzde m so llten wir nie unsere Praxisanwend ung un d unsere Rahmenbed ingu ngen aus de n Augen verlieren. Das schönste Verfa hren ist vollkommen wertlos, wen n wir später nicht im Stande sind, es umzusetzen. Der Algorithmus als solches ist eine allgemein gü ltige Verfah rensbeschreibu ng. un abhängig von ihrer Umse tzung in der einen ode r and eren Programm iersprache. In der Tat gibt es abe r je nach Anwendung Einsch ränkungen, die wir nicht ignorieren sollten. Manc he hardwarenahe En twicklu ngsumgebungen sowie einige sehr einfache, alte Programmier sprachen erlau ben beispielsweise keine rekursiven Funktionsauf rufe (s.u.). Hier soll es uns aber darum ge hen, einen möglichst breit gefächerten Überb lick über Problemlösungsstrategien zu gewinnen. Mit den Widrigkeiten un d Einschränkungen spezieller Programmier sprachen u nd Plattform en möchten wir un s hie r nicht beschäft igen. Deswegen we rden wir alle Verfah ren auf eine m Stan dard-Pe in Java imp lemen tieren .
Allgemeines Problem
, lösen
r-,
Algorithmus
v
programmieren
v
Konkrete Aufgabe
le·8)
xAlt • xNeu:
xNeu
(xAlt + z/xAltl / 2.0:
}
return xNeu: } Code 1: Iterative Beredmung der Quadratwurzel mit dem Newton-Verfahren
5
2 Arten von Algor ith men Das Verfahren führt recht schnell zu eine r sehr gu ten Nä heru ng und prod uziert da bei folgende Werte bei de r Berechnu ng von
12 :
Tabelle 1: Werte des Newton-Verfahrens in den einzelnen ltera tionsschritt:en
z
Berechnun gssch ritt
x
zur Probe:
0
2.0
4.0
1
1.5
2.25
2
1.4166666666666665
2.006944444444444
3
1.4142156862745097
2.0000060073048824
4
1.4142135623746899
2.0000000000045106
5
1.414213562373095
1.9999999999999996
x
Darau s können wir auch d as allgemeine Rezept für ein en iterativen Algori thmus ableiten: .. e, ~
1.
Defin iere Startwerte. mit denen d ie Berechn un g beginnen soll.
2,
Ermittle eine Vorsch rift, wie aus den alten Werten schrittwe ise neu e Werte gewonne n werden, die sich der Lösun g d L"S Prob lem s nä hern ,
3.
Beende d ie Berechnung, falls die ges uch te Lösung gefun den .....-urde od er das Ergebn is d ieser Lösu ng nahe ge nug kommt.
~
2.1.1 Sortieren Ein beliebtes Beispiel , an hand dessen wir u ns die Funktionsweise ein es Algorithmus vera n schaulichen kön nen, ist d as Sortieren. Wir gehen von einem Zahlen feld aus, da s wir in au fsteigender Reihenf olge sortieren möcht en. Ein iterativer Algori thmus nach obigem Rezept fällt u ns sofort ein:
6
1.
Der Startwert ist da s noch unso rtierte Feld .
2.
Eine neu e Lösung erhalten wir, ind em wir zwei ben achbarte zahlen vertau schen, wenn die größere vor der kleine ren steht. Au f diese Weise du rch lau fen wir das ga nze Feld .
3.
Wir beenden das Verfah ren, we nn da s Feld fertig sortiert ist.
2.1 Iterative Algori thme n Das Ergebn is ist Bubble-Sort, d er woh l einfachs te un d beka nnteste Sortleralgortthmus:
votd bubb1eSort(i nt[] zahl. int n) {
boolean sortier t = false: da
{
sor tiert - true: for (int i=O ; t-en -I : i++) {
if (zahl[i] {
}
>
zahl[i +l])
sortiert = false: int puffer = zahl[i] : zahl[i] ~ zahl[i +l] : zahl(i +l] m puffer:
}
}
while(!sortiert) : } Code 2: Bubble-Sort
Das Sortieren eines Feldes von 10 Zahlen ergibt folgend es Bild :
26 26 26 26 26 26 26 26 10 3 .
85 70 28 28 28 28 28 10 3 . 10 .
70 18 63 57 57 53
18 63 57 63 53 10 10 3 3 28 26 . 28 26 . 28
63 57 70 53 10 3 53 53 53 53
57 85 53 10 3 57 57 57 57 57
98 53 10 3 63 63 63 63 63 63
53 . 10 . 3 70 70 70 70 70 70 70
. 10 . 3 . 3 98 85 98 85 98 85 98 85 98 85 98 85 98 85 98 85 98
Abbildung 2: Ablauf von Bubble-Sort
7
2 Arten vo n Algorithmen Wenn wir uns ansehen, wie lan ge das Sortieren von n zufällig gewählten Zahlen d auert, sehen wir, dass es offenbar kein Problem ist, beispielswe ise die Datensätze einer Schulklasse mit 30 Schü lern zu sor tieren. Bei der Kundend atenbank eines Großkonzerns mit Millionen vo n Einträgen wird es aber schon schwieriger: Tabelle 2: Rechenzeit von Bubble-Sort
n = 100 Bubble-Sor t
< 1 ms
1000
10000
100000
1000000
Z ms
610ms
ca. 1 min
ca. 1.5 h
Da das Sortie ren vo n 1000000 Elementen eine durchaus realistische Aufgabe ist, werden wi r nach einem schnellere n Verfahren suchen müssen. Fall uns das nicht gelingen sollte, müssen wir mit unangenehm lan gen Rechen zeiten leben.
2.1.2 Wege im Labynnth Wie findet man in eine m Labyrinth d en kürzesten Weg vo m Start zum Zie l? Diese Frage mag ein interess antes Spiel sei n, ist abe r seh r na he verwandt mit der rech t bedeutsamen Aufgabe, eine gesc hickte Verdrahturig elektronischer Scha ltungen zu finden. Wie finden wi r also d en Weg vom Start S zu m Ziel Z?
Abbildung 3; Irrgarten
8
2.1 Iterative Algori thme n Wir möc hte n un s hie r das Meze-Running-Ver fahren an sehen, d as 1961 vo n C. Y. Lee vorges tellt wurde [Lee61]. Diese r Algorith mus arbeitet in zwei Phasen iter ativ u nd pa sst in unser oben besch riebenes Schema. Im O nllne-Bereich find en Sie eine Beispiel-Im plem entierung u nter
Dn line
http: / /www.viewegteubner .de/onl ineplus
Phase 1 : Fe~r num~eren
1.
Zu m Beginn mark ieren wir den Sta rt 5 mit d em Wer t w = 0 und er klären d ieses Feld damit als besucht. Alle anderen Felder sind noch u nb esucht.
2.
Nun ve rteilen wir " we llena rtig" neue Werte im Labyrinth : Ausgehend vo n ein em akt uellen Wert w vergebe n wir den nächsthöheren Wer t w + 1 an alle noch nicht besuchten Nachba rn von Feldern, sie selbst den We rt w haben. Au f d iese Weise verteilen wi r die Werte I, 2, 3, ...
3.
Wenn wir d as Ziel Z err eicht haben , sind wir fer tig und können die Verga be von Wer ten beenden. Sollten wir keine Felde r mehr find en, denen wir einen Wer t zuord nen können, u nd das Ziel noch nicht er reicht haben, d ann gibt es keinen Weg du rch das Labyrinth. In die sem Fall mü sse n wi r au fgeben.
I I
1111 " 9 . re
~ I' I'
11'0.'.
,, ,
,
1
•
•
Abbildung 4: Lee-Algorithmus: We1lenartige Numme rierung der Felder im Irrgarten
9
2 Arte n von Algorithmen
SI so " 16' 6> 6] ... 0' .. 0' 161" '" " » ln l" l"
"
.
10 "111119
""
.... .... .. .." " " " " " " " " .. .." .." "" ". >0
~ R I~ II I I~ 111 ~ 1~ lu l_I I~I" 10 1I 21
n 11'11'110
~ "lu l" I~ IM
l' 11
1lI11O Ill l11
9 11 ' 0 1' 1_ 10' ,OU1tail
Abbildung 14: Fortschreitend detailliertere Betrachtung
T
Vil'l Pt·ta il
Das mag auf den ersten Blick vielleicht trivial klingen, ist aber wichtig, we nn wir u ns nicht ständig mit unnötiger Detailarbeit überlasten möchten. Wir we rden uns diese Idee nu n an zwei gra fischen Beispielen ansehen, der Gru ndgeda nke findet a ber in prak tisch jede r Form der Dat enverarbeitung Anwendung .
FraktaleGrafiken Der Begri ff der fraktalen ("zerbrochenen") Gebilde wurde 1975 von B. Mandelbrot geprägt [Mand75). Im Gegensatz zu einfacheren Obje kten wie einem Kreis oder einem Dreieck offenbart eine fraktale Figu r immer neue Detail s, ega l wie klein der Ausschnitt ist, den wir un s ansehen. In diesem Sinn verfügen Frak tale also üb er un end lich vie l In formation . Bei der Erstellu ng einer fraktalen Grafik wi rd rekursi v im -
26
2.2 Rekursive Algo rithmen mer mehr Detail erzeugt. Wir erstellen hier eine fra ktale Landschaft über einer quadratischen Gru ndfl äche: 1.
Wir beginnen mit einer quad ra tischen Fläche, deren vier Ecken eine zu fällig gewählte Höhe hab en.
2.
Wen n d ie Auflös ung des Bild es gu t gen ug ist, beenden wir die Unte rteilung. Ansonsten teilen wi r d as Quadra t wieder in vier Quadrate und beginnen von Neuern.
Der zen tra le Teil des Codes bes teh t in der rekursiven Un terteilung unseres Quadr ats. Die maximale Au flösung besteht aus N· N Quadraten, n gib t jeweils d ie aktuelle Seiten länge an und beginnt bei N. x un d y sind die Koordina ten links u nten. Die Punkte we rden im Feld punkte der Größe (N + 1}'(N+ 1) gespeiche rt, in de m zu Beginn die äußersten vier Eckpun kte liegen, de r Rest des Feld es ist null . Die zKoordina te eines Pun kts ents pricht seiner Höhe, wobei jeder neue Punkt der Mittelpunkt sein er Nachbarn ist, um einen zufälligen Wert nach oben oder un ten verschoben.
votd unterteilen(int x. tnt Y , tnt n) {
i f (n == 0) return; Pun kt linksUnten = pun kte[x][y]; Pun kt linksOben = punkte[x][y + n]: Punkt recht sUnt en = punkt e[x + n][y] ; Punkt rechtsOben = punkte[x + n][y + n]: int nHal be = n I 2; if (pun kte[x][y + nHal be] == nu l l ) {
Pun kt p = mittel punkt( linksUnten. l i nksOben); p.zufaelligVerschieben() : pun kte[x][y + nHa lbe] = p;
}
i f (punkte[x + nH al be][y] == nul l ) i f (pun kte[x + n][y + nHalbe] = = nul l) if (pun kte[x + nH al be][y + n] == null) i f (pun kte(x + nHal be][y + nH albe] -= nul l ) unter teilen(x, y. nHalbe); unterteilen(x + nHa lbe. y. nHalbel : unterteilen(x, y + nHalbe, nHa lbel ; unter teilen(x + nHa lbe, y + nHa lbe, nHalbe); ) Code 8: Rekursives Unterteilender Fläche
27
2 Arten von Algor ith men Wir verwend en zunächs t eine geri nge Rekursionstiefe von 4 und erhalten mit sehr wenigen Rechenschritte n eine grob aufgelöste Figu r", Sie vermittelt u ns bereits einen orde ntlichen Eind ru ck von der Stru ktur d er Landschaft, auch wenn noch keine Details zu erkenn en sind:
Abbildung 15: Grobe Darstellung mit 512 Flädlen
Rekursiv erzeugen wir nun immer mehr Details und erhalten dam it ein beliebig fein aufgelöstes Bild. Hier kann es keine gr undsätz lichen Änderungen im Vergleich zur groben Ans icht geben.
Abbildung 16: Detailliertere Darstellung mi t 524288 Flädlen
On li n e
Im Onlt ne-Bereich find en Sie eine Beisp iel-Imple mentierung unter
http : / /www. viewegteubner.de/onl i neplus
Die An wen dung von Frakta len beschrän kt sich nicht auf die Berech nung von Bi ldern, sondern erstreckt sich heu te auf za hlreiche Gebiete [FRI-3). Für die gr afische Da rstellung wu rde n übe r eine r quad ratischen Grund fliiche jeweils zwei Dreiecke gezeichnet.
28
2.2 Rekursive Algo rithmen
Bildkompression Bei den fraktalen Landschaften haben wir reku rsiv Deta ils hinzugefiigt. Nun 5011 uns ei n sehr ähnliches rek ursives Verfahren dabei helfen, unnötige Details wegz ulassen. d .h. uns auf das Wesent liche zu kon zentrieren . Dazu sehen wi r uns d ieses Bild der Größe Pu nkt e beinha ltet.
N =512 an, das 512-512=262144
Abbildung 17: Original mit 262144 Punkten Wir beginnen wiede r mit einem großen Bereich, nä m lich dem ga nzen Bild . Die Entsche idung, ob wir die große Fläche unterteilen, machen wir aber nu n von den Daten abhä ngig: Wenn d ie Helligkeit de r Punkte eines Bereichs im Rahm en eine r vorgegebenen Toleran z d er mittleren Helligkeit de r ganzen Fläche entspricht, da nn verzichten wir auf eine weitere Unterteilung u nd zeichn en eine einfa rbige Fläche mit dem Dur chschn ittswert. Nur bei starken Schwankungen unterteilen wir den Bereich weiter.
29
2 Arten vo n Algor ith men Es ergib t sich folgender Code, der im Feld bi ld der Größe N -N d ie Helligkeitswerte s peichert:
void unter tetlentt nt x. i nt {
v. int n)
double durchschnitt = du rchschnitt( x. y. n) ; double unterschied = mittlereAbwe ichung( durchschnitt. x. y. n) ; i f (unt erschied < GRE NZE) {
for (i nt xx {
E
for (int yy {
x; xx < x + n; =
y; yy
bil d[xx][yy]
=
M
Wir verwenden dabei folgende Variablen: W : Wert de r tra nspo rtieren Waren G : Gewicht der tra nsp or tiere n Waren M : Zul äss iges Maxima lgewicht 2.
Zur Komb ina tion der Gene wä hlen wi r eine Zufallszahl z zwi schen 0 und n . Die ersten z Gene kom men von de r Mutter, die letzten n - z Gene vom Vater.
3.
Mit Wahrscheinlich keit p=1 % mu tiert ein Gen, kippt also von zwischen ja und nei n.
Nun können wir dieses Rucksack-Prob lem wegen des en tstehe nden Aufwa nds nicht auf an de rem Weg lösen, trotzdem möchten wir abe r wissen, wie gut der genetische Ansatz funktionier t. Dazu züchten wir u ns ein künstliches Beispiel, dessen Lösung wir bereits kennen, und testen da ran da s genetische Verfahren: Wir erzeugen 100 schwe re Gü ter mit einem zufä lligen Wert im Bereich [10000...10999) und einem zufälligen Gewicht im Bereich [11000...11999]. Dann erzeugen wir weitere 100 leichte Güter mit eine m zufälligen Wer t im Bereich [11000...11999] un d einem zufä lligen Gewicht im Bereich [10000 ...10999 ).
Als ma ximale Last verwenden wir genau die Summe der 100 leich ten Gü ter . Nun besteht die idea le Lad ung offenba r aus ge nau den 100 leicht en Gütern, u nd wir kön nen un ser Verfahren testen, indem wir 1000 Generationen lang bei einer Pop ula tionsgröße von 50 rechnen. Relevant ist für uns nur d as jewei ls beste bisher au fgetrete ne Cenorn, dessen Fitness wir uns nu n ansehen.
56
2.6 Genetische Algori thme n Wir arb eiten mit eine r Mutati onswahrscheinlichkeit von 1% und erhalten keine optima le Lösung, sondern nur ca. 98,5% der maximal mög liche n Ladung. Das ist au ch nich t we iter verwu nderlich, schlie ßlich we rden s tatistisch gesehen in jed em Schritt zwei Bits d urch Mut atio n verändert. Also rechnen wir un ter sons t gleiche n Bedingu ngen mit einer Mutations-Wah rschein lichkeit von 0.1% und vergleichen d ie Resu ltate. Ein Wert von 1.0 entsprich t dabe i de m Optimum. 1,00
J
0,99 0,91< 0,'"
--
0,96
-
0,95
r-
IJ
1
0,'» I 0,93
--
0,92 0,91 0,90
Abbildung 2B: 1000 Generationen mit Mutationswahrscheinlichkeiten von 1% und 0.1%
Tat sächlich finden wir bei der kleine ren Mutati onswahrschein lichkeit von 0.1% d as Optimum, auch we nn wir anfangs langsam ere Fortschritte ma chen .
2,6,2 Gewinnmaximierung Eine Au fgabe, d ie sich in marktwir tschaft lichen Sys tem en hä ufig stellt, ist d ie Suche na ch de m maximal en Gewinn . Ein Händ ler sieh t sich mit folgender typische r Situ ation konfront iert: Er hai d ie Wah L untersch iedli che Prod ukt e einzukaufen und - ho ffentlich mit Gewinn - we iter zu verkaufe n. Hier hat er jewe ils einen Eink aufs- un d einen Verkaufspreis. Da er nicht unbeschränkt Kapital zur Verfügung hat, mu ss er bei de r Bank Zinsen bezahlen, wenn er meh r Ware kauft, als er momentan bezahlen kan n. Im Beisp iel hai u nser Händler l000€ zur Verfügu ng, für jeden Euro, den er mehr aus gibt, mu ss er 10% Zinse n bezahlen . Zur Au swahl stehe n verschied ene Prod ukte.
57
2 Arten von Algor ith men Tabelle 9: Finkaufspreis und Gewinn für verschiedene Güter
Einkau fspreis
Verkaufspreis
Gewinns panne
Monitor 1
300f
315f
5%
Monitor 2
400f
424f
6%
Die Frage ist natü rücb: Wie soll u nser Händl er sein Kapital einse tzen, um den höchsten Gewinn zu erzielen. Selbs tverständ lich kann der Händl er n ur ganze Stückzahlen kau fen, ansonsten wäre d ie Au fgabe trivial: Nim m das Prod ukt mit der höch sten Gew inn sp an ne, teile das zur Verfügu ng stehe nde Kapital, nimm kein e Schul den auf, da d er Zinssa tz höhe r als d ie Gewi nnsp anne ist. Die Lösu ng wäre: Kau fe 2.5 Monitore vom Typ 2, da nn sind d ie 1O00€ au sgegeben, der Gew inn beträgt ~. Leider können wi r aber keine halben Monitore ha ndeln. Wie gehen wir also vor, we nn nur ganzzah lige Lösungen in Frage kommen ? Die erste Id ee ist möglicherweise, d ie oben ermittelte Lösun g zu runden ? Das führt uns aber interessanterwe ise nicht ans Ziel: Wir kön nen 2.5 au f 2 abrunden od er au f 3 aufrund en, wir können ein weiteres Mod ell vom Typ 1 bestellen od er nicht, in jede m Fall erreichen wir nicht das optima le Ergebni s: Tabelle 10: Möglicher Kapitaleinsatz und entsprechender Gewinn ~
2"
"§ ::E
:c 0
N
•
""
"0.
'", 0
~
c liJ
• "0.
"ö
'", 0
C
c
"~
w
o
~
"w
0>
58
::E N
.~
•
.~
0.
0.
0
0
~
c c
c
w
N
"~
o
~
"5
c liJ
0>
c
c
"~ 0
-•
ec
E 0
~
"w
w
o
C
-c
-
= betrag)
-----------. if (saldo >= betr-aq)
{
80
saldo = saldo return tru e:
betrag:
{
~ saldo
20 z
sal do
betrag;
· 40
ret urn t rue: }
}
else
else
{
{
return f'al se:
}
return ratse:
}
Code 16: Unsynchronisierter Zugriff
Was ist hier passier t? Offenbar ha t Rechner 2 seinen Methodenaufruf etwas später gestartet. Rechner 2 füh rt dann zu r Prü fung. ob genug Geld vorhanden ist, d ie t f Abfrage d urch, nachdem Rechner 1 d ie selbe Abfrage bereits du rch laufen ha t, bevor aber d er Saldo auf 100 - 80 = 20 geän dert wurde. Im Ergebnis werden beide Abbuchengen ausgeführt, beide Methodenaufrufe liefern t rue un d das Konto ist überzogen. Genau das hätte nicht passieren sollen. Wie können wir da s verh indern? Offenb ar muss sich jeder Rechner sicher sein, da ss niemand den Saldo verändert nachdem d ie t f-Abf rage du rch laufen wu rd e und bevor der Saldo entsprechend ange passt ist. Derartige Probleme sind sehr un angenehm, weil sie - im Gegensatz zu klassischen Rechenfeh lern - vom Timing-Verhalten ab hänge n u nd d eswegen oft nicht reprodu zierbar sind.
82
3.4 Parallele Vera rbeitun g Die Lösung besteht offenba r da rin, zusammenhängende Codeteile zu iden tifizieren, d ie nich t u nterbrochen werden d ü rfen. Ein and erer Rechner mu ss also gg f. warten, bis dieser Codeteil abgearbei tet ist. Das ist aber nich t so einfach, denn wir müssen feststell en: Zusammengehörige Codeblöcke müssen groß genug sein, u m d as Proble m zu vermeiden. Zusa mmengehörige Codeblöcke müssen möglichst klein sein, um mög lichst wenig Wartezeit zu erze uge n. Im Ergebnis sin d d iese kritischen Bereiche also so klein wie möglic h, aber so groß wie nötig. Diese Forderung ist nicht immer einfach u mzusetzen. Wenn wir also d ie auszahlen-Methode als kritischen Bereich markieren, mu ss der 2. Rechn er warten, bis der 1. Rechner fertig ist. Nu n arbeitet d ie Methode korrekt.
Rec hner 1
Rechner 2
synchroni zed
boo1ean auszahl ent 1 nt bet rag) bJocki { ~
~SynChrOni zed
60
~ ro l ean auszehlenr tnt betrag)
if (saldo {
saldo
>=
betrag) i f (saldo {
>=
betrag)
saldo
return true : } else { return false: }
sal do = saldo - betrag : return t rue: ) el se { return fa l se : }
Code 17: Synchronisierter Zugriff
83
3 Effizienz eines Algorithmus Leide r sind wir selbst d amit noch nich t am Ende, denn so notwendig Synchro nisierun g auch sein mag, so gefährlich ist sie au ch: Was passiert, wenn Rechner 1 au f Rechner 2 wartet, Rechn er 2 auf Rechner 3 und Rechner 3 auf Rechner 1? Wir haben ein Deadlock und keiner arbeitet meh r weiter. Wir kenn en d iese Situation aus d em Alltag: Wie verh alten wir un s, wenn an einer Kreu zu ng oh ne Vorfahrtsregelung von allen vier Seiten ein Au to kom mt? Offenb ar gilt hier d ie Rechts-vor -Links-Regel, leider sieht aber jeder einen Wagen recht s von sich... Als Menschen sind wir mit Verstand ausgestattet und können sowo hl das Problem erkennen als auch d ie Lösung finden: Wir einigen un s, wer en tgegen aller Regel n zu erst fah ren darf. Wie aber brin gen wir d as de m Rechner bei? Solange es möglich ist sollten wir u ns bemühen, Deadlocks zu vermeid en anstatt sie im Nachhinein zu erkennen un d mit oft kom plizierten Strateg ien wied er aufzu lösen. Sehen wir u ns d ie folgenden Methoden der Klasse
Konto an:
synchronized boolean einziehen(Konto belasteter. int betrag) {
i f (!belasteter.genugGeldCbetrag) return false: belasteter .saldo -= betrag; t his. saldo += betrag: return t rue:
}
synchronized boolean gen ugGeld(in t betrag) {
retur n saldo
>=
betrag;
} Code 18: Code mit Deadlock-Poten tial
Um zu vers tehen, was h ier passier t, erinnern wir un s deren. dass d ie sy nchron! ze dAnweisun g eine ver kü rzende Schreibweise fü r synchroni zed(thi s) { ... } ist. Die Objekte d er Klasse Kon t o ve rwen den sich also selbst als Synchronisationsobjekt, ein durchaus übliches Vorgehen . Machen wir u ns also wied er klar, was hier passiert. Wir betracht en z wei Kon ten, d ie aus unterschied lichen Thr ead s heraus wechse lseitig voneina nder Geld einziehen möchten.
84
3.4 Parallele Vera rbeitung
synchrom zed etnzjehent . .. )
{ if übetes teteI.genugGeld( ... »)
I
synchronized genugGeld( ... ) {
)
}
Code 19: Ein Deadlock tritt auf
1.
Die etnztehen-Methode von Kon to A wird au fgeru fen, d as Geld soll von Konto B eingezogen werden. Gleichzeitig wi rd d ie ei nz tehen-Mcthod e von Konto B aufgerufen, das Geld soll von Kon to A eingezogen werde n.
2.
Die etnztehen-Methode ist sync hrontzed, also spe rren sich die Konten gegen weit ere Zugriffe. Beide Kont en sind jetzt blockiert.
3.
Aus der ei nz i ehen-Metbcde heraus wird die genugGel d-Metbod e d es zu belastenden Kontos au fgeru fen, also ru ft Konto A die qenuqße ld-Methode von Konto B auf und umgekehrt.
4.
Die genugGeld-Method e ist synchronized, also versu chen sich d ie Kont en gege n weitere Zug riffe zu sperren. Da sie bereits blockiert sind, warten sie, bis d ie bestehend e Sperre wieder freige geben wi rd . Das passiert aber nicht, weil das andere Konto ebenfall s wartet.
Diese Beispie le sind zwar konstruiert. Bei rausen d en von An frag en mü ssen wir aber davon ausgehen, d ass jede noch so unwahrscheinliche Verklemmu ng au ftritt. Es ist also ein fach, pa rallel zu pr ogrammieren, aber es ist schwi erig, das richtig zu tun. Keine od er zu kleine Synchrontsationsbereiche führen oft zu Inkonsisten ten und da mit fehlerhaften Daten . Zu große Sync hrorusa ttonsbereiche führen oft zu unnötigen Wartezeiten und da mit zu ine ffizien ten Programmen. Bei jeder Synchronisation ist d ie Gefahr von Deadlocks zu beachten.
85
3 Effizienz eines Algorit hmus
Thread-Pools Nachdem wir gesehen haben, wie schwierig es werde n kann, mit mehreren Threads zu arbeiten, stellt sich d ie Frage: Können wir die se Prob lem e nicht komplett u mgehen? Können wir nicht - wie früh er - alle Aufgaben vollständ ig sequenziell abarbeiten ohne un s Ged an ken um die Synchronisation ode r Deadtocks ma chen zu müssen ? Dies fu nkt ioniert natü rlich nicht immer, aber häu fig treffen wir au f eine ähnliche Situation: Die eigentlichen Arbeit sschritte ließen sich seh r wohl nach einander ausführen. Wen n wir etwa ein Bild bearbeiten, dann verändern wir zuerst d ie Helligkeit und schärfen da s Bild danach. Es hat keinen Sinn, beides gleichzeitig zu tun und einen Teil des Bildes mit den alten Helligkeitswerten zu schä rfen, eine n Teil mit den Neue n. Wir mü ssten un s dann wieder aufwändi g Gedanken machen , ob d iese beiden Arbeitssch ritte sich gegenseitig bee influssen od er ntcht.; Trotzd em können wir aber mei st nich t ohne Multi-Threading programmieren, denn bereits d ie Verwendung ein er grafi schen Oberfläche z wingt u ns, mehrere Threads einz usetzen, ob wir es möchten oder nicht. Die Darstell un g des Fensters, der Bedien elemente etc. geschieh t im Crafik-Dispatcher-Thread . Auch die Methoden, die au f Benutzerei ngaben reagieren, werden folglich in d iesem Thread abgea rbeitet, was in la va/Swing folgendermaßen aussieh t:
public void ac tionPerformed(ActionEven t e) {
// Aufgabe ausführen . .
} Code 20: Aufgabe, die im Grafik-Dispatcher-Thread ausgeführt wird
Es wäre sehr un geschic kt, hier langwierige Arbei ten zu erledigen, weil wi r damit d ie grafische Darstellung blockieren wü rden, also beispielswe ise da s Zeichnen sich gegenseitig ü berlap pender Fenster, die dyna mische An passung des Fenster-Layou ts etc. Ein inte ressantes Konzept, das u ns hier hilft, ist ein Thread-Poo l. der in einer einfachen Form nach folgendem Rezep t aufgebau t wird: _
~ ~
86
1.
Starte ein en Thr ead, in de m später alte Berec hnun gen erled igt werd en. Er verwa ltet eine Warteschlange aller ans tehenden Aufgaben un d arbeitet d iese der Reihe nach ab.
2.
Definiere die einzelnen Aktionen, die über die grafische Oberfläche au sgelö st werden können.
3.
Wenn eine Ak tion au sgelöst wird , wird di ese nich t sofort au sgefüh rt, sondern nur in die Warteschla nge im Th read -Pool einge reiht.
3.4 Parallele Vera rbeitung Im einfachste n Fall kann ein Thread-Pool also da zu dienen, einerseits mit mehreren Th read s zu arbeiten, andererseits z usam m engehörige Aufgaben, d ie sonst synchronisiert werden müssten, sequenziell zu bearbeiten. Dieses Konzep t ist seh r flexibel, kann vielfältig erweitert we rden und steh t in vielen Programmiersprachen bereits im Rahmen der Stand ard- Bibliothek zur Verfügung. Eine einfache Implemen tierung könnte in Java/Swing folgendermaßen aussehen und lässt sich auf andere C raflk-Bibliotheken bzw. Sp rachen übertragen. Die Aktion kan n au f Grafik-Event s rea gieren und kenn t d ie eigen tliche Aufgabe, die sie d urchführen soll:
publ ic class Akt ion implements ActionListener (
public AktionC) {} public void ausführen ( ) {
I/ Die eigent l iche Aufgabe ausführen . . .
}
publ iC void actionPerformed CActi onEvent e) (
ThreadPoo1.gibE;nz t geInstanzC).anmel den (tht s) :
} } Code 21: Die Aktion
Das Erzeu gen einer solchen Aktion für jede mög liche Bcnutzer handlun g mag auf den ersten Blick etwas au fwändig erscheinen, bleibt un s abe r meistens ohnehin nich t erspar t, weil d ie gleiche Aktion oft au f unterschiedlichem Weg gestartet werden kann, etw a per Menü , Tastatur-Kürzel ode r Kontext-Menü . Wir beachten hier, dass beim Au slösen des Crafik-Events - hier also in der actionPer-formed-Methode - d ie Methode ausfüh ren nich t aufgeru fen w ird, sondern d ie Aktion sich led ig lich selbst beim Thread- Pool melde t. Der Ein fachheit halber kann es hier nur einen einz igen Thr ead -Pool geben. Er wartet auf neue Aufgaben, über d eren Ein treffen er mit einem Bvent-Mechenismu s benach richtigt wird , in Java realisieren wir dies über wa t t und not i fy. In einer Endlossch leife we rden dann ggf. anstehende Au fgab en abgea rbeitet.
87
3 Effizienz eines Algo rit hmus
publi c cl ass ThreadPool implements Runnable {
pri va te l inkedl ist aktionen : private static ThreadPool instanz
=
new ThreadPool() ;
private ThreadPool() {
aktionen
~
newli nkedlist() :
Thread t hread = new Th read(this): t hread .start(): }
publ ic static ThreadPool gibEinzigel nstanz() {
return instanz:
}
publiC voi d anmelden CAkt ion aktion) {
synch ronized(this .aktionen) {
try {
if (aktion == nul l ) return;
this .aktionen .addL ast(a ktion): t his.a ktionen. not i fy(): }
catch(Exception e) {} }
}
88
~
3.4 Parallele Vera rbe itung
public void rune) {
whi le tt rue) {
Aktion akt.ton
=
null;
synchron ized(this .aktionen) (
while(this .aktionen. i sEmp ty( )) {
t ry (
t hi s .akt i onen .wai t ( );
}
catchCException e) {} }
aktion
=
this .aktionen.removeFi rs t();
}
sktton . eust ührenc) .
} }
--
~
Im Threed des Thread-Poo l
Cod e 22: Der Thread-Pool
Wen n wir nun tats ächlich alle Au fgaben nach dies em Konzept im Thread- Pool aba rbeiten, wissen wir, d ass keine Datensynchronisa tion mehr nötig ist, u nd hab en uns viel Arbeit erspart. Auch weiter führ end e Aufgabe n, wie et wa separate Threads für Hint ergrund au fga ben lassen sich integ riere n. Ein kleines Problem haben w ir jedoch noch zu beach ten, wenn wir aus unserer Aktion heraus wiederu m gra fische Aktionen starten möchten, also beisp ielsweise eine n For tschri ttsba lken akt ua lisieren oder Elem ente in einen großen Baum aufneh men etc.: Dies sollte im Grafik-Dispatcher-Thread gesc he he n, in de m wir uns jetzt mit volle r Absicht nicht befi nden. Hierfür gibt es in Java/Swin g d ie Befeh le SwingUti 1 it t es .i nvokeAndWai t bzw . Swi ngUti 1i ti es .i nvo keLater sowie en tsprechen de Anweis ungen in an de ren Program miersp rachen .
89
3 Effizienz eines Algorithmu s
3.5 Übersicht Wir haben uns einige grundsätzliche Strategien zur Problem tösung am Comp uter angesehen und gelernt, wie man die Effizienz eines Algorithmu s misst. Damit können wir uns übersichtsartig veranschaulichen, wann wir welches Verfahren einsetzen. Wir fragen uns jeweils, ob ein entsprechender Algorithmus existiert. Falls ja, m üssen wir uns weiter fragen, ob der Ressourcenverbrauch für unsere Anwendung akzeptabel ist, d .h. ob das Verfahren mit vertretbarem Speicherverbrauch in akzeptabler Zeit fertig wird:
ja
Iterativer/reku rsive r A lgorithmus? Ev tl. pa rallelisiert Ressourcen o k?
ja
Probler n g e lö s t, Rec henzeit
He uris tische r Algorit hmu s?
akzeptabel
nein
Las-vegas Algorit hmu s?
Probtern gl'löst, Rl'SSOUrcl'Tl meistens
.,
akzeptabel Manch m a l eine Aussa e!
Lieber im m e r gutl' Näherung oder manchmal Fehler? gu te Nä herune Zufalls- bzw, genetischer Algorithmu s?
ja
Monte-Carfo
Algorithmus? ne in
Problern näheru ngsweise g l'1i'>s t
Abbildung 30: Auswahl des passenden Algorithmus
90
m a n chm al Fl'11le r
ja Proble m meistens gl'l ös t
3.5 Übersicht Diese Übersich t kan n n ich t als ver bind liche, in Granit gemeißelte Regel verstand en werden, sondern als Or ientierungshilfe.
Bei allen Analyse n und teilwe ise du rchaus komplizie rten Verfahren sollten wir aber eines niem als vergessen; ec
Ein Algo rithmus ist n ich t ,.gut" ode r "schk·cht".
-(
Er ist immer nur angemessen für unsere momentan e Aufgabe ode r inadäqu al. Die konk reten Anforde rungen sollten niema ls außer Acht gel assen we rden.
." E 'ti
Wir müssen klä ren, was ge nau u nsere Au fgabe ist, in welchem Umfeld gea rbeitet wir d, we lche Problem größen uns rea listischerweise erwarten usw. Wenn diese Pun kte n icht klar sind, wird es uns meist nicht gelingen, ein passen des Verfahren zu im plemen tieren. Neben der Laufzei t u nd dem Speicherverbrauch eines Algorithmus ist auch der Entwicklungsaufwand w ichtig. Das Schreiben von Software kostet Zeit und Geld. Mona telange Recherchen u nd aufwänd ige Implem entierun gen lohnen sich nu r für entsprechen d wichtige und große Aufgaben. Ein Verfahren sollte nu r so leistu ngsfä hig sein wie nötig - jede zusätzliche Zeile Code ist auc h eine potenzielle Fehlerqu elle. Wir sollten also nicht nu r schwache Lösungen vermeiden, d ie die Aufg abe nicht in den Griff bekommen...
Abbild ung 31: Zu schwache Lösung für schwieriges Problem
... sondern auch nicht u nwi rtschaftlich und Technik-verliebt mit Kanonen auf Spat zen schießen.
l-""ung
Abbildung 32: Zu aufwändige Lösung für einfaches Problem
91
3 Effizienz eines Algorithmus
3.6 Nutzung praktisch unlösbarer Probleme Um was kann es in eine m Kapitel ge hen, das sich mit der "Nu tzu ng praktisch unlösbarer Prob lem e" befasst? Nachdem wi r die Laufzei t eines Verfa hrens einschätze n können und uns an ha nd der Übersich t ein Bild davon gemach t haben, wa nn wir welc he Art von Algorithm us einsetzen, bleibt nur noch eine Frage offen: Was ist zu tun, we nn wir ohne Ergeb nis bleiben? Die Un tersuchungen über d as Wachstum haben uns geze igt, dass ein Algorithmus mit exponentiellem Wachstum oft gena uso gu t ist w ie gar kein Algorith mus: Wir werden n ich t glücklich. Meist können wir nicht d arau f hoffen, d ass wir in u nserer konkreten Anwendu ng zu fälligerweise nu r so kleine Probleme lösen müsse n, dass wir exponentielles Wachs tu m h innehmen können. Auch Sonderfälle, für die wir vie lleicht eine Spez iallös ung kenne n, treten nicht immer auf. Häu fig bleib t d ie Aufgabe praktisch un lösba r. Das ist bezog en auf d iese konkrete Aufgabe zwar schade, mu ss aber nicht unbed ingt zur großen Frustration führen. Interessanterweise können wir uns d ie Unlösbarkeit eines Problems auch zu Nutze machen, eine d er klassischen Anwendunge n hierfür ist die Daten verschlü sselu ng. Das Vorge hen ist dabei immer ähnlich: Daten we rden mit eine m bestimmten Verfahren verschlüsselt, meist u nter Verwendung eines Passwort s. Die zentrale Frage lau tet na türlich: " Ist die Versch lüsselung den n auch sicher?". Sie wi rd meis t mit folgende m Argument bea ntwo rtet: "We nn d ie Verschlü sselu ng ohne Kenntnis des Passworts zu knacken wäre, dann mü sste de r Hacker in d er Lage sein, das XYZ-Problem zu lösen. Dieses Problem ist aber nu r mit unrealistischem Aufwand lösbar". Dur ch gezleltes Dur ch testen aller mög lichen Passwörter kann zwar jedes Verfah ren geknackt werd en. Der mit einer solche n bru te-force-Attacke ("brutale Gewalt") verbu ndene exponentielle Aufwa nd führt aber dazu, d ass d ies meist nicht als Gefah r wah rgeno mmen bzw. als unvermeidbares Risiko hingenommen wird . Verfa hren d er Datenve rsch lüsselu ng we rden also gezie lt so konstru iert, da ss ide alerweise ein Hacken des Algorithmus gleichbede utend mit dem Lösen eines praktisch un lösbaren Proble ms ist. Die Unlösbarkeit eine r Aufg abe wird hier also gerade zu m Argument für das Funktionieren eines Algo rithm us.
Syrrmetrische Verschlüsselung Die trad itionelle Datenve rsch lüsselung mit einem Passwort wird au ch symmetrische Verschlüsselung gena nnt un d wu rde scho n zu Caesars Zeiten eingesetzt. Das heute wo h l wichtigs te Verfah ren ist der Rijndae1-Algorithm u s, der 1998 von J. Daemen u nd V. Rijmen vorges tellt IDaRi98] u nd 2000 vom National Institute of Standards and Technologies (NIST) zu m neuen Advanced Encryption Standard (AES) ge wäh lt
92
3.6 Nu tzung pr aktisch un lösbarer Probleme wur de. Dieses Verfa hren gilt als sehr sicher, arbei tet schnell u nd ist au ßerde m ziemlich einfach zu imp leme ntiere n, soga r in Hard ware. Alle symme trischen Verfahren haben aber eine Schwäche, insbesondere we nn es u m vertrauliche Kommun ikatio n zwischen zwei Partnern geht: Ein ge heimes Passwor t muss ve reinba rt, vertraul ich kommun iziert und d auerhaft gehei m geha lten werden. Egal ob wir nu n Postk ar ten, Brieftauben oder berittene Kuriere einsetzen, die Vereinbarung eines solchen Passworts bleibt eine offene Flanke. Teilnehmer A gemeinsamer Schlüssel, den nUT A und B kennen dürfen
verschlüsselte Daten Kom m unik ation
Teilnehmer B gemeinsamer Schlüssel, den nur A und B kennen dürfen
Abbildung 33: Schematischer Ablauf symmetrischer Verschlüsselung
Asyrrmebisctle Versd1lüsse!ung Aus d iesem Gru nd wu rd e die asymmebische Verschlüsselung entwickelt. W. Diffie und M. E. H ellman gelten als d ie Erfinde r de r asy mmetrische n Verschlüsse lung IDi· He76]. Hier werden Daten ausge tausch t, ohne dass d ie beiden Part ner ein Geheimnis teilen mü ssten. Dazu erhält jeder Teilne hmer ein Paa r aus öffentliche m u nd pri va tem Schlüss el (pub lic/p rivate Key). Der öffentliche Schlüsse l kan n jedem mitge teilt we rde n, den p rivaten Schlüsse l erh ält niem and , insbesondere auch nicht u nser Partner.
,.--::-.,-----,-,-----, Privatsphärc
Te ilneh me r A
Öffentlichkeit verschlüsselte Daten Kommun ika tio n
Privatsphärc Teilnehmer B iiffen tlicheT Schlüsse! B
Abbildung 34: Schematischer Ablauf asymmetrischer Verschlüsselung
Der RSA-Algorithmu s als sehr bek an ntes Verschlüsselungsverfahren wurde 1977 von R Rivest, A. Shamir und L. A. Adlernan vorgestellt [RSA77). Das Verfahren beru ht auf einem Them a, mit dem wir uns bereits beschäftigt haben : Den Primzahlen. Bisher hatten wir uns gefrag t: " Ist eine Zah l n eine Pri mzahl?". Diese Frage konnten wir mit Hilfe d es proba bilis tischen Perm at-Tests rech t or den tlich beantworten . Nu n
93
3 Effizienz eines Algorit hmus soll aber die Frage lau ten: " Wie lautet die Primfa ktorzerlegung einer Zahl n?". n ist also offenbar selbst keine Primzah l, abe r wie können wir sie zerlege n? Bei kleinen Zahlen ist das wie so oft ziemlich ein fach, so find en wir beispiel sweise schnell heraus, d ass 166487393= 12899·12907 ist. Wie aber zerlege n wir eine große Zahl, also beispi elswei se eine Zahl mit 1000 Dezimalstellen? Das ist nich t so einfach, und bisher sind nur Verfahren bekannt, die die ses Problem in exponentiell er Laufzeit lösen, mit ande ren Worten: Bei genügend großen Zahl en ist die se Auf gabe kaum zu knacken" . Dies bietet u ns eine inte ressant e Möglichkeit : Wir suc hen un s zwei Primza hlen u nd mu ltiplizieren die se. Das ist eine einfach e Aufgabe, wir können d azu fertige Pr imzah ltabellen verwenden, ggf. gehe n wir auch das Risiko ein und sind mit Pseudoprimzahlen zuf rieden. Das Ergebnis ist eine sehr große Zah l, sie hat etwa doppelt so vie le Stellen wie unsere beiden Primzahlen. Wir sind aber die einzigen, die die Primfaktorzerlegu ng dieser großen Zah l kennen, für de n Rest der Welt ist es eine kaum lösbare Au fgabe, d iese Zerlegung zu finden. Genau darauf basiert der RSA-Algorithmus. Die Schlüssel we rden folgende rmaßen ermittelt: I.
Wir suchen uns zw ei Primzahlen PI und P2 . Wir verwe nd en beispielsweise PI= 883411 u nd P2=5 48567 .
2.
Wir berechnen P =P I'P 2 . In un serem Beispiel ergibt sich P =484 610122037
3.
Wir berechnen F= (Pl -I )'(P 2-l) . In un serem Beisp iel ergibt sich F=484608690060
4.
Wir wäh len eine Zahl t mit t > 1 u nd t -c F , d ie teiler fremd zu Fist. Es gibt hierzu unterschiedliche Strategien, wir wählen eine Primzahl, die mindestens 1/4 der Dezimal stellen von P hat (RSAW90). In unserem Beispiel wählen wir 1= 1783 .
5.
Wir berechnen die Zahl i als Inverses zu t bzgl. de r Multiplikation modu lo F ,d .h.esmuss gelten (t· i) mod F= 1 In un sere m Beisp iel ergibt sich i = 421008895627 .
Der öffentliche Schlüssel besteh t nu n aus den beiden Zahlen t un d P. Der private Schlüssel besteh t au s de n Zahlen i und P. Nun müssen wir unsere Nachrichten n ur noch in eine Zahl n < P umwandeln und können sehr ein fach Botscha ften für einen bestim mten Emp fänger versch lüsseln, I~
94
Der Maßstab für d ie Größe ist hier nicht der Zahlenwert selbst, so ndern d ie Anz ahl der Bits, d ie für die Darstellu ng de r Zahl nötig sind. Pra ktisch isl das gleichbedeutend mit der Anzah l der Dez imals tellen einer Zahl. Der Zahlenwert selbst wächs t exponentiell m it der Zahl der Bits.
3.6 Nu tzung pr aktisch un lösbarer Probleme d .h. nur er kann sie lesen . Dazu br au chen wir ihn nie getroffen zu haben, und es muss auch kein woh l gehütetes geme insa mes Gehei mn is geben. Um eine Nachr icht n zu verschlüssel n, berechne n wir n ' mod P .wobei t und P der öffentliche Schlüssel des Empfänge rs sind. In unserem Beisp iel ergibt sich für d ie Nachricht 42 der Wert 445742477204. Um eine versc hlüsselte Nach richt v zu en tsch lüssel n, berechnet d er Empfänger mit seine m privaten Schlüssel i u nd P d en Wert v' mod P un d erhält wieder d ie origina le Nach richt. In ähn licher Weise kann der !{$A-Algorithmus auch eingesetzt werden, u m eine Na chrich t zu signi eren, so dass der Emp fänger sicher ist, dass die Nachric ht auch ta tsäch lich vom angeb lichen Absender stammt. Hierzu wird der Send er eine Prüfsum me s< P seiner Nachricht berechne n. In unserem Beispiel verwende n wir die Quersu mme der Ziffern, also 6. Um d ie Nachric ht zu signieren, verschlüsselt der Sender s mit seine m privaten Schlüss el, berechne t also Si mod P . In unserem Beispi el ergibt sich fü r d ie Prüfsumme der Wert 65636526687. Der Emp fänger ent schlüsselt diese verschlüsselte Signa tur v mit dem öffentlichen Schlüssel d es Sende rs, berechn et also v ' mod P . Par allel d azu en tschlü sselt er d ie eigent liche Nach richt und berechnet selbst deren Prü fsumme. Diese muss mit d er eben en tschlüsselten Signatur überein sti mmen.
On li ne
Im Onhne-Bereich finden Sie eine Beispiel-Implement ierung un ter
http ://www.viewegteubner .de/onlineplus
Sicherheitvon RSA Offensicht lich bricht dieses Verfahren zusa m men, sobald es jeman d schafft, P in seine Fakto ren zu zerlegen . Als Bestandteil des öffentlichen Sch lüssels ist P für alle Welt zugängli ch. Das ganze Konzept steht und fällt also mit dem Glauben, d ass große Zahlen n icht mit vertretbarem Au fwan d faktor isiert we rden können. Darau f kön nen wir uns schei nbar verlassen - sehen wir uns folgende Z ahl an": 310741824049004372135075003588856793003734602284272754572016194882320644051 808150455634682967172328678243791627283803341547107310850191954852900733772 4822783525742386454014691736602477652346609 n
Es handelt sich hierbei um die RSA64U- Zahl, eine der Zahlen, deren Faktoristerung im Rahme n der RSA Factoring Challenge von RSA Labora torfes als Wettbewerb ausgeschrieben wa r.
95
3 Effizienz eines Algorithmus Diese Zah l ha t 640 Bits bzw. 193 Dezimalstellen und wurde 2005 vo n F. Bahr, M. Boehm, J. Franke u nd T. Kleinjung zerlegt. Die beiden Faktoren sind 163473364580925384844313388386509085984178367003309231 218111085238933310010
4508151 212118167511579 und
1900871 2816648221 1312685157393541397547189678996851549366663853908802710380 2104498957191261 465571 Die Lösu ng d iese r Aufgabe hat 30 Jah re Rechenzei t auf 2.2GHz Op teron Cl'Us benötigt. Tatsäch lich waren 80 Cl'Us am Wer k, und die Arbeit wu rd e in ca. fünf Mon aten erledigt. Für d as Zerlegen einer etwas größeren Zah l mit 663 Bits bzw. 200 Dezimalstellen benötigte das gleiche Team rund 75 Jahre Rechenzeit auf de n gleichen Cl'Us. Tatsächlich g lauben wir, da ss das Paktortsteren einer n-Bit-Zahl Au fwand verursachen mu ss, der expone n tiell mit n wäc hst. Alle Indizien sp rechen da für. Tatsächlich wissen wi r es aber nicht. Es kon nte bisher nich t bewiesen werden dass es kein Verfahren gibt, Zahlen beisp ielsweise in polynomieller Zeit zu zerlegen . Es ist also d ur chau s d enkbar, da ss neu e Erken n tnisse in der Zahlen theo rie d ie Prirnfaktorzerlegu ng zu ei ner p raktisch lösb aren Aufgabe machen - auch wenn es heu te seh r unwa hrschein lich erschein t. Nu r am Rand e sei hier erwähnt, dass Qua nten computer nach grundsätzlich anderen Mechani sme n arbeiten als alle Rechner, d ie wi r heute übli cherweise vor u ns habe n. Hier sind Aufgaben wie d ie Pakt oristerung oder N P-vollstän dige Prob lem e sehr schne ll lösbar. In pra ktischen Anwend ungen werden die vergleichsweise aufwändigen asy mmet rische n Verfahren oft nicht genutzt, um d ie eigen tlichen Daten zu verschlüsseln. Hierfür wird meist ein sy mmetrisc hes Verfahren eingesetzt, die asymmetrische Verschlüssel ung wird nur benu tzt, um d as dabei verwendete Passwort zu transp ortieren.
96
4 Wichtige Datenstrukturen Nach dem wi r d ie Lau fzeit un terschiedli cher Algorithm en kennen ge lern t haben, können wir nu n entscheiden, welch e Datenstrukturen für u nsere Anwendu ng ange messe n sind . Es soll uns nicht darum gehen, d ie en tsprechenden Stru kturen selbst zu programmieren, wenn sie bereits in modemen Programmiers prachen in der Stand ard -Bibliothe k enthalten sind. Wir möch ten da s Angebot nur richtig nutzen . Wir sehe n uns einige Beispiele der Java-Bibliothek java. uti 1 mit ausgewählten Methoden an . Eine d etailli erte Beschreibung säm tlicher Method en finden wir in der au sführlichen Onlt ne- Dokumentation. Im Folgen de n haben wir eini ge Besonderheiten zu beachten: Je nach Anw end ung möchten wir evtl. auch null in unserer Stru ktur speichern. Wir so llten prüfen ob d ies zu lässig ist. Wenn wir mit mehreren Threads auf die gleiche Datenstruktu r zu greifen, mü ssen diese Zu griffe synchron isier t werden, wie wir bereits gesehen haben . Vor Verwendung d er en tsprechenden Klassen so llten wir unbedingt prüfen, ob die se Synch ron isation bereits innerhalb de r Klasse erled igt ist d .h. d ie Methoden sind thr eed-sa fe - oder ob wir uns selbst u m d ie Synchronisat ion kümmern müssen. Wir sollten auf keinen Fall do ppelten Synchroni satlons -Ov erhead betreiben . Wir erin nern un s, d ass in der 0 (...) -Scbreibweise kon stante Faktoren ignoriert we rden. Wenn wir im Rahmen der 0 (...) -Notetion den bestmög lichen Algorithmus ge fun den ha ben, werden wir uns in eine r prakti schen Progra mmi erung natürlich seh r wohl um solche Faktoren kümmern. Aus die sem Grund sind d ie Implementierungen meist et was komplizie rter als sie rein theoretisch sein mü ssten. Das Ziel ist dabei meisten s, viele O perationen mö glichst schnell durchzuf ühren, nicht ein e einzige. Deswegen wird hier die mittlere Laufzei t einzelner Met hodenaufruf e an gegeben (amortized ru nnin g time). Beispie lsweise bedeutet eine mittlere Lau fzeit von 0 (1) für eine Operation nicht, da ss d iese immer in kons tanter Laufzeit d urchgefü hrt werden kann . Die Aussage ist vielmehr, da ss da s nmalige Durchführen d ieser Operat ion mit O (n ) Aufwand erledigt wird . Im Einzelfall können wir Pech haben und eine au fwändige Neuo rga nisatio n der internen Date nstruktur au slösen, d ie O (n) Au fwand veru rsacht. Wir we rden im Folgenden nicht zwische n der Lau fzeit un d d er mittl eren Lau fzeit unterscheiden .
97
4 Wichtige Datenstrukturen Einige d er vorgeste llten Datenstru ktu ren bieten d ie Möglichkeit, d u rch die ent haltene n Elemente zu iterleren, sie also eines nach dem anderen zu du rchlau fen. Die Reihe nfolge, in de r sie d abei geliefert we rden, häng t von der jewe iligen Klasse ab. Mit n bezeichnen wir hier d ie Anzahl der Eleme nte, die gerade in unserer Struktur gespeichert sind.
4.1 Listen Die wo hl einfachste und oft naheliegendete Datenstru ktur ist eine einfache Liste. Hier werden die Inha lte unsortiert anei na nder gehängt. Zelt
Fahrrad
DVD
CD
Abbildtmg 35: Eine einfache Liste
Als Implementieru ng verwenden wir d ie Klasse ArrayL ist. Tabelle 20: Wich tige Methoden einer Liste und deren Zeitverhal ten
Met hode
Fun ktion
add
Nim mt ein ne ues Elemen t auf
0 {11
get
Liefert das Element an einer vorgegebenen Position
001
contains
Prü ft, ob ein Element entha lten ist
O {o )
Lau fzeit
Die Reihenfolge beim Iterieren entspricht der Reihenfolge, in der die Elemente au fgenomme n wurden.
5tad<s Stacks arbeit en nach dem Prinzi p last-in-first -out (U FO) u nd können als Sonde rform der Liste versta nden werden, bei der neu e Elemente immer ans Ende (bzw. "oben") ange hängt werden und bei denen Elemen te nur am Ende (bzw. "oben'") en tno mmen werden. Ein Stack entspricht genau der Datenstru ktu r. in der die Parameter von Funktion saufrufen abgelegt werden können, insbesonde re bei reku rsiven Method en. Als Implementieru ng verwenden wir d ie Klasse ArrayDeque.
98
4.1 Listen
Tabelle 21: Wichtige Methoden eines Stacks und deren Zeitverhalten
Method e
Funk tion
Lau fzeit
push
Nimm t ein neu es Elem en t auf
0 (11
pop
Liefert da s obers te Eleme nt und entfernt es
O (l j
peek
Liefert da s oberste Element ohne es zu en tferne n
oru
cont ains
Prüft, ob ein Eleme nt en thalten ist
O (n)
Die Reihen folge beim lterieren ent sp richt der Reihen folge, in der d ie Elem ente au fgenomme n wu rden. Warteschlangen Warteschlangen ar beite n nach dem Prinzi p firs t-in-first-out (FIFO) und können ebe nfalls als Sonde rform d er Liste verstan den werden. Hier werden Elemen te immer arn Ende angehängt u nd vo rne en tnommen . Als Implementieru ng verwenden wir die Klasse
ArrayDeque.
Tabelle 22: Wichtige Methoden einer Warteschlange und deren Zeitverhalten
Methode
Funkti on
Lau fzeit
add
Nimm t ein neu es Elemen t am Ende auf
O (l j
po11
Liefert da s erste Element u nd entfernt es
pee k
Liefert da s erste Elemen t ohne es zu entfernen
orn oru
con tains
Prü ft, ob ein Element enthalten ist
O (n)
Die Reihenfolge beim Iterteren entsprich t der Reihenfolge, in der die Elem en te au fgenommen wu rden. Wir sehen, dass Listen sehr performant arbeiten, solange wir die conte: ns-Methode nicht all zu häufig benö tigen.
99
4 Wichtige Datenstrukturen
4.2 Mengen Mengen unterscheid en sich von Listen da durch, d ass ein Elemen t nur ein einziges Mal aufgenommen wird. Dazu we rden Elem en te mit ihrer equal s-Method e auf Gleichheit überprüft. Ggf. sollten wir also d ie equa 1s-Method e gemäß den Anforderungen un serer Anwend ung übe rschreiben.
4.2.1 Sortierte Mengen Als Implementieru ng verwenden wir die Klasse TreeSet. Tabelle 23: Wichtige Methoden einer sortierten Menge und deren Zeitverhalten
Methode
Fun ktion
add
Nimmt ein neu es Element auf
Oflogfn ll
contains
Prü ft, ob ein Element entha lten ist
O (log (n ))
Laufzeit
Die Reihen folge beim lterteren ist immer sor tiert und unabhängig davon, in we lcher Reihenfolge d ie Eleme nte au fgeno mme n wurden. Wir stellen fest, dass d ie Klasse TreeSet keine Met hod e get anbietet, mit der wir ein Element an eine r vorgegebenen Position geliefert bekommen.
4.2.2 Unsortierte Mengen Als Implementierung verwenden wir d ie Klasse HashSet. Tabelle 24: Wichtige Methoden einer unsortierten Menge und deren Zeitverhalten
Met hode
Funk tion
add
Nim mt ein neues Element auf
0 (1 1
contains
Prü ft, ob ein Eleme nt entha lten ist
0 (11
Lau fzeit
Die Reihen folge beim Iterieren ist willkurlieh und hängt von den inte rnen Strukturen ab. Insbesondere kann sich die Reihen folge zw ischen unterschied lichen Dur chläu fen ändern. Bei genauerem Hinsehen sind wir abe r verblüfft: Wie kann die contai ns-Methode in konstanter Zeit arbeiten?
100
4.2 Men gen
Hash-Funktionen Um d ie Fun ktionsweise d es HashSet verstehen zu können, se hen w ir u ns an, wa s ei ne Hash-Fu nktion ist. Die Idee haben wi r bereits beim Bucker-Sort-Verfah ren kennen ge lern t. Wir gehen von beliebigen Daten au s, also von Zahle n, Str ings us w . und berechnen au s die sen Daten eine Za hl, den Hashwert. Wenn wir d ies mit un tersch ied lichen Daten se hr oft durch fü hren, sollten die se Hashwer te idealerwe ise gleichmä ßig verteilt sein und kein e Häufungen au fweisen . Tro tzdem mü ssen natürl ich gleiche Daten den gleichen Hash wert liefern . Ist da s einmal gesc haff t, können wir den be rechn et en Hash wert als Position in einer Liste ver wenden, d .h. wir haben die Position errechn et, ohne suc hen zu müssen: Wenn unsere Datens tru ktur n Elemen te entha lten soll, berechnen w ir einfach den Hasbwert modu lo n und erhalten die end gül tige Position. Wie beim Bucket -Sor tAlgo rithmus mü ssen wir uns dann mit d em Fall befassen , dass unterschiedliche Daten den gleiche n Hashwert er halten und damit d ie gleiche Posit ion. Also wird es ÜberiaufIi sten geben, d ie wir aber ohne großen Zeitaufwa nd verwa lten können. Wie aber ermi tte ln wir beis pielsw eise aus ein em String eine Zahl? Eine sehr ein fache Variante kön nte so a uss ehen: Wir gehen den String Buchstab e für Buchsta be durch, ermittel n die Position a de s Buchstaben im Alphabet sowie die Position s des Buchstabens in unse rem String . Dann hängen wir ein fach a • s und no chmals a hintereinander - hier ist nicht die Addition gemein t, sondern wirklich das Hintereinanderschreiben von Ziffern. So erhalten wir einen Wert pro Buchstaben, die se Wer te addieren wir und erh alten den Hashwert eines Strings" . Hash-Funktionen werd en seh r hä u fig genutz t. Aus d iesem Gr und bietet jedes Objekt eine Standa rd -Implementierung hashCode. Wenn wir unsere Obje kte aber in HashSets o.ä. speichern, sollten wi r d iese Methode mit einer eigenen Implementieru ng überschre iben . Diese kann Rücksicht auf d ie spez iellen Eigenschaften u nse rer Ob jekte nehmen und dadurch wese ntlich pe rformanter ar beiten als d ie Sta ndard-Variante.
..
Diese Method e ist h ier gewählt, weil sie ein fach nac hvollziehbar ist. Für praktische Anwen dungen müssen wi r sorgfältiger da rau f achten, dass d ie Wer te gleichm äß ig verteil t und möglichst ein fach zu berechne n sind .
101
4 Wichtige Daten strukturen Wenn wir nun die Hash we rte von vie r Strings berechnen, d ie in eine Stru ktur gespe ichert werden, die sieben Elemente umfassen kann, ergibt sich folgendes Bild: Strin M Pos ition a des Buchstaben im AI habc t 13 Position s des Buchstilben im Strin 1 a s a hint erei nander cschncbcn 13113 Has hwcrt 5trin Hashwcrt Strin Hashwc rt
.:
5t rin Hashwc rt
I
Edga r
1 2 121
u s 19 21 4 5 6 18318 11411 21521 1%19 84 103 r
k
18 3
11
cercttc 67660
ac ueline 83787
Ed rar
mod 7
20335
1
0
a
J
2
3
4
I J,1C9 uclind
5
6
I Milrkus]
I JeancttcJ
Abbildung 36: Berechnung der Hashwerte und Ermittlung der Position in einem HashSet
4.3 Zuordnungen In Listen werd en beliebige Elemente gespeic hert u nd ein er Nummer zugeo rd net, ihrer Position in der Liste. In vielen Anwen d ungen interessiert uns aber die se Position überhaupt nicht. Wie gehe n wi r also vo r, wenn wir ein Element nich t einer Zah l, sondern einem an deren Elem ent zuordnen möchten? Wir möchten beispie lsweise per Name auf ein Element zugreifen, also über eine n Siring. Auch hier he lfen uns Hash-Fun ktionen we iter . Wir haben jeweil s ein Schlüssel-Element und ein diesem zugeo rd netes Wert-Element. Der Schlüssel wird d an n übe r eine Hash-Fu nktion berechnet.
102
4.3 Zuor dnu ngen Als Implementieru ng verwenden wir d ie Klasse HashMap. Tabelle 25: Wichtige Methoden einer Hash -Zuordnungstabelle und deren Zeitverhalten
Meth od e
Fu nktion
Lau fzeit
put
Speichert ein Paar aus Schlüssel und Wert
0(1 )
get
Liefert den Wert, de r einem Schlüssel zugeordnet ist
O (l)
con tainsKey
Prü ft, ob der Schlüsse l in der Tabelle vorkomm t
0 (1)
con t ainsVa1ue Prüft, ob der Wert in der Tabelle (meh rfach) vorkom mt
O (n)
Die Reihen folge beim Iren eren ist wieder willk ü rlich.
4.4 Bäume Bäume sind eine Datenstruktur. bei der d ie Element e in einer Eltern-Kind -Beziehun g zueinander stehen. Auch d ie sortierte Menge Tr eeSet vervv en de t intern eine Baumstru ktu r. Bäume eignen sich dann, wen n eine hierarchische Ordnun g vorhande n ist und wir au f bestimmte Elemente zu greifen möchten, ohne un nötigerweise alle Elemente zu durchsu chen. Wirbeltiere
Vögel
Fische
Säu getiere
Am hibien
Greifvö el
Abbild ung 37: Unvo llständige Systematik der Tiere
103
4 Wichtige Datenstrukturen Wenn wir u ns beispielsweise für Echsen interessieren, möchten wir u ns sicherlich nicht mit allen möglichen Vögeln, Säugetieren oder Schla ngen beschäftigen. Wir benötigen also eine Datenstruktur . in de r wir schne ll in den rich tigen Bereich na vigieren kön nen. Das Ziel ist dabei, mög lichst große Teilbereiche vollständ ig zu ignorieren, we nn sie für unsere ak tue lle Anfrage ohne Bedeu tung sind. Auf d iese Weise können wir auch in sehr großen Datenbeständen zügig arbeiten. Die Imp lementieru ng ist d abei ga nz einfach, jedes Baumelement kenn t sein VaterElement und seine Kinder. Das Wurzel-Element hat keinen Vater.
class Element {
Element vater = nul l: ArrayList<Element> kinder = newArrayList<Element>() : void fuege Ki ndHinz uCE lement ki nd) {
ki nder .add(kind) : ki nd.vater = this :
} } Code 23: Einfache Implementierung eines Baums
Je nach Anwendung können d ie Kind -Elemen te auch in ei ner anderen Struktu r gespeichert werden, z.B. sor tiert. Die wesentliche O perat ion in eine m Bau m ist d as Suc hen eines Elemen ts, beispielsweise u m es zu lösche n, ihm ein Kindelement hinzu zu fügen usw. Daz u benötigen wir so viele Schri tte wie der Bau m tief ist, bei einem Baum mit ausgeglichener Stru ktur also O (log (n )) Schritte. Je nach Anwend ung können wir eine n bestim mten Kno ten auc h einmal mit O (log (n )) Aufwand suche n, u m dann Operationen wie da s Hinzu fügen vo n Kind -Elemen ten mehrmals mit 0(1 ) Aufwand zu erledigen.
Automatische Umorganisation eines Baums Wenn d ie Organisation unseres Baums eine in ha ltliche Bed eu tun g hat wie im ob igen Beispiel, da nn ka nn eine seh r ungleichm ä ßige Struktur zu stark untersch iedli chen Zug riffszeiten füh ren, im worsr-Case bis zu O ( n) . Wenn der Baum abe r beispielsweise nu r dazu d ien t, d ie Eleme nte sortiert zu hal ten wie etwa im IreeSet, kann er au toma tisch ne u organ isiert werden, wenn er seh r unsy mmetrisch geworden ist.
j (}j
4.5 Graphen
4.5 Graphen Ein Graph beschreibt eine Menge von Elementen, den Knoten, d ie über Kanten miteinande r verbunde n sein können . Die natürliche Anwendu ng solcher Graph en besteht in der Darstellung räuml icher Verhältni sse, w ie z.B, einer Land karte. Kiel
Hambur
Schwertri
Bremen
Bcrlin
Hannover
I'otsdam adcbur
Düsscldorf
Wiesbade Mainz
Erfu rt
carbrückc
Abbildung 38: Einige deutsche Städte
Graph en we rden aber auch gerne genu tzt, um logische Abhängigkeiten darzustellen. Wir haben beispielsweise folgende Freizeitaktivitäten vor: Wir möchten ins Theater gehen, dazu müssen wir aber vorher Kleidung kau fen gehen. Für beides müssen w ir zuerst bei der Bank Geld abheben. Wir möchten auf eine Gebu rtstagsfeier gehen, dazu müssen wir aber vorher ein Geschenk kaufen, wozu wir ebenfalls zur Bank gehen müssen. Wir möchten Freunde treffen, um sie zu fragen, welches Geburtstagsgeschenk passend wä re und um mit ihnen über die neue Wohnung des Gebu rtstagskinds zu reden, d ie wir auf der Feier das erste Mal gesehen haben.
105
4 Wichtige Datenstrukturen Diese Abh ängigkeiten lasse n sich gu t in eine m Gra ph da rstellen, wobei es sich nu n um einen gerichteten Gr aphen handelt, d ie Pfeile zeigen uns, welche Tätigkeit vor welc he r geschehen m uss. Kleidun Geschenk
Geburtsta
Bank
Thea ter
Freu nde
Abbildung 39: Graph mit Abhängigkeiten verschiedener Tätigkeiten
Wir er ken nen h ier so for l das Problem : Die Geb ur tstagsfeier könne n wi r nich t besuchen, weil wir vorher d ie Freu nde sehen möc hten, um ein passendes Ges chenk zu erfragen und gleichzei tig nachher d ie Freund e besuchen möcht en, um über die Wohnung zu red en. Wir m üssen unsere Unterha ltu ng al so au f zwei Visiten verteilen . Es gibt za hlreic he Fragestellungen, d ie in Zusa mmen hang mit Gr aphen interessan t sind, sowie ent sp rechende Verfa hre n [GAl -2J. Je na ch Aufgabenstellung eignen sich un terschied liche Varianten fü r d ie Im plementieru ng eines Gra phen mit n Knoten .
Adjazenz.uste In einer Adjazenz-Liste wird zu jedem Knoten die Liste der vo n ih m erreichba ren Kno ten gespeicher t. Diese Struk tur eignet sich besonders bei Graphen mit wenige n Verbi ndungen.
Knoten
von d iese m Knoten err eichbare Knoten
Ban k
Kleidung, Theater, Geschenk
Geschenk
Geburtstag
Geburtst ag
Freund e
Freunde
Geschenk
Kleid un g
Theater
Th ea te r Abbildung 40: Adjazenz-liste
106
4.5 Graphen Adjazenz-Matrix In einer Adjazenz-Matrix wird ein zweidimensiona les Feld ange legt und für jedes Knotenpaar ve rmer kt, ob eine Verbindung zw ischen ihnen besteh t. nach Bank
von
Gesche nk
Bank
Geb ur tstag Freund e
ja
ja
Theater
ja
ja
Geschenk
ja
Geburtstag Freunde
Kleid ung
ja
Kleidung
ja
Theater Abbildung 41: Ad jazenz-Matrix
Diese Dars tellung hat den Vorteil, dass wir mit ihr d ie komplett e Erreichba rke it im Graph einfach berechnen könne n. Gibt es einen Weg von der Bank zu den Freunden? Gib t es eine n Zy klus, also ei nen Weg von einem Knoten zurück zu sich selbst? Dazu gehen wi r folgendermaßen vor: Die ob ige Matr ix Al sagt uns, von welc hem Knoten wir mit einem Schritt zu welc hem Knoten kommen . Wohin kommen wir abe r mit zwei Schritten? Wie sieh t also d ie Ma trix A 2 aus? Offenbar gibt es einen Weg mit zwei Sch ritten von x nach y , wen n es irgendeinen Kno ten z gibt, de r mit einem Schritt von x erreichbar ist u nd von dem aus w ir wiederu m y erreichen können. Wir testen alle Knoten durch und erha lten auf diese Weise eine 2-SchrittAdjazenz-Matrix. Ents prechend ve rfahren wir für mehr Schritte. Mathematisch entspricht dies einer Matrix multiplika tion, wobei d ie Mat rize n binäre Werte ent halten. Im Folgenden bezeichne t der Punkt d ie binäre Matrixmu ltiplikation und das Pluszeichen + das bitweise ver-oder- n zweier Ma trizen.
Formel 14: Berechnung de r k-Schritt-Adjazenz-Matrix
107
4 Wichti ge Datenstrukturen
I 0 0 1 0 0 1 0 0 0 0 0 1 0
0
0
I
0 0
0
0 0
0 0 0
0 0 0 0 0
I 0 0 I 1 0 0 1 0 0 0 0 0 0 0 1 0 0 I
0
0
0
0
I
0 1 0 0 0 0 1 0 = 0 0 0 0 0 0 1
0 1 0 0 0 0 0 0 0 I
0
0 0
0
0
0 0 1 1 0 0 0 0 0 0 0
0
0
0 0
0 0
0
0
0 0
0 0 0
Formel 15: Berechnung der 2-Schritt-Adjazenz-Matrix durch binäre Matrixmultiplikation
Eine solc he bi när e Matrixm u ltiplika tion benötigt 0 ( n 3) Sch ritte. Wenn wi r also wissen möch ten, vo n welc hen Knoten welche a nderen Knot en in drei Schritten erreichbar sind, be rechnen wir A J =A 2' A l usw. Oft inte ressier en wir uns aber auch d afü r, we lche Knot en überhau pt erreichbar sind, ega l in wie viele n Schritte n. Die Matrix A stellt u ns diese In forma tion zur Verfü gu ng . Wir se hen d abei, dass w ir in eine m Gr ap he n mit n Knoten höchstens n - I Schritte bra uchen u m einen be liebigen Knoten zu erreichen, falls das überh au pt möglich ist. Meh r Sch ritte können nicht nöt ig sein, weil wir sonst einen Knoten meh rfach besuchen würden, also Sch leifen laufen. Wie berechnen wir diese Ma trix? Der Vollständigkeit hal ber de finiere n wir hierzu di e Matrix A o , sie entspricht der Iden tität. Dann be rechnen wir A l=A o+A l , A 2 =A )·A I , A 4 =A 2 ·A 2 usw., d .h. wir multiplizieren das Erge bni s immer wie der mi t sich selbst. Das tu n wir so lange, bis wi r A I< ermittelt haben, wobei k z n c-I sein muss. Nun sind wir fe rtig, denn wenn k z n c-l , d ann ist A I< =A . Wir benötigen also O {log ( n )) Matrixm ultiplikationen zu r Berechnung von A Dies ist insbesondere bei großen Matrizen ein beach tliche r Aufwand , alle rd ings haben wir d ann d ie Erreichbarkeltsinfor matio n seh r elegant griffbereit. Eine effizientere Met hode zur d irekten Berech nu ng vo n A
ist der dynamische
Floyd-Warshall-Algorithmus, d er 1962 vo n R Floyd u nd S. Warshall vorgestellt wu rde [FIWa62 ). Dieses Verfahre n komm t mit 0 ( n 3)
Aufwa nd aus.
N achgefragt .. .
108
1.
Welch e Da tenstru ktu rten) w erden be nö tig t um einer alphabet isch sortier ten Lis te von Strings beliebige Objekte zuz uord nen?
2.
Es gi bt schnelle re Verfa hren zu r Multip lika tion bi närer Ma tr izen, als die Schu lmethode, d ie O ( n.l) Aufwand erzeugt?
5 Künstliche Intelligenz Wir haben uns zahlreiche Arten von Algorithmen angese he n und einige Beisp iele stu diert. Allen bisher betracht eten Verfah ren war jedoc h eines ge meinsam: Sie haben uns zu keine m Erkenntn isgewi nn ve rho lfen.
Wir selbst mussten d ie Aufgabe analysieren u nd verstehe n. Wir selbst mussten u ns Strateg ien überlegen, wie das jeweilige Problem geschickt zu lösen ist. Wir selbst haben dann d ie Lösung program miert. Dabei haben wir zw ar einige algort thmtsche Vorge hensweisen kennen gele rn t, letz tlich haben wir aber d en Compu ter mit unserem Wissen gefü ttert, um ihn dan n die Aufga be lösen zu lassen . Der Compu ter war da bei im mer d ie klassische Rechenm aschine, die fleißig u nsere Anweisungen ab arbeitet, sich abe r nie " in telligen t" ve rhal ten hat. Das soll sich nun ändern . Was genau ist aber " Künstliche Intelligenz" ? Wod urch zeichnet sich ein Algorithmu s aus, d er zu "i ntelligentem" Verha lten d es Rechners führ t? Beim Mensche n ha ben wir zw ar ein rela tiv gu tes intui tives Verständnis d afür, wen wir für "in telligen t" ha lten . Es gibt aber keine genaue wissenschaftl iche Definition des Begriffs " Intelligenz", d ie un s hier helfen wü rde. Wenn aber schon der Begriff " Inte lligenz" nich t exakt zu fassen ist, was so ll dann " Küns tliche Intelligen z" (KI) sein? Als Test, ob eine Maschine in telligent sei, hat A. M. Turing den Turing-Test vo rgeschlagen. Eine Maschine wird dann als intelligent bezeichnet, wenn ein Mensch in ei nem Frage-Ant wort-Spiel d ie Masch ine nicht vom Mensc hen u nterscheiden kann na türlich ohne Sichtkon takt. Hier wird alle rdings unterstellt, d as jemand nur intelligent sein kann, wen n er mit dem Menschen kom mu nizieren kann - und will. Wir we rden hier nicht au f die Suche na ch einer exakten Defini tion gehe n, sondern uns statt dessen mit einem einfachen in tuitiven Vers tän d nis begnügen: Der Computer verhä lt sich dann intelligent, wenn er un s neue Erkenn tnisse liefert od er eine Au fgabe lösen kann, oh ne dass wi r ihm vorher genau gesagt hätt en , wie er d as tun soll. Das Gebiet de r KI ist ein we ites Terrain (KIl l, wir werd en uns hie r ein ige au sgesuch te Beisp iele heraus gre ifen.
109
5 Künstliche In telligenz Symbolische KI
Eine wesen tliche Anwend ung der KI ist d ie Symbolische KI. Hier geh t es da rum, neues Wissen mit Hilfe klar defin ierter Regeln aus berei ts be kan ntem Wissen abz uleiten. Es wird hier mit der formelhaften Dars tellu ng des Wissens gearbeitet - eben mit der Symboli k. Ein sehr simples Beispiel könnte folgende rmaßen aussehen .
Alles was im Tresor lieg t i st wert voll . Go ldmünzen liegen im Tresor . Aus d iesen beide n Informationen kann d ie Erkenntnis abge leitet werden:
Goldmünzen si nd wert vol l . Dahinter steckt de r allgemeine Grundsatz : " Wenn A aus B folgt u nd B wie de rum aus C, dann folgt auch A aus C'. Etwas formaler könnten wir schreiben:
=>
Ding liegt imTresor Ding ist Goldmünze Ding ist Goldmünze
-> ->
->
Di ng ist wertvol l Ding liegt imTresor Ding ist wert vol l
Der wesentliche Vorteil der Sym bolische n KI besteht dari n, dass nach exa kt definie rten Regeln vorgegange n wird, ein Verfahren, d as sehr gu t erforsch t ist. Im Ergeb nis sind die Erkenntnisse jederzei t begründbar und widersp ru chsfrei. Ein Prob lem besteh t jedoch d arin, da ss wir zuerst eine forma le Beschreibu ng unserer Au fgabe benötigen, was sehr schwi erig sein kann . Ferner kommen Verfah ren der Symbo lischen KI oft nich t gu t mit verrauschten Daten, Ausreißern, Heu rlstiken usw. zurecht. Au ch sind symbolische Kl-Systeme n icht unbedi ng t imm er ein fach zu programmieren. Wo liegen also d ie Anwend ungsfä lle der Symb olische n KI? Dem Wesen na ch we rde n wir seh r an übliche mathematische Verge hensweisen erinnert: Wissen wird in form aler Schreibweise d argeste llt u nd nach festen Rege ln ver arbeitet. Wofü r könne n symbolische KI-System e also besser geeignet sein als zum Führen mat he matische r Beweise?
110
5 KÜnstlic he Intelligenz So ist beisp ielswei se d as v ier-Paeben-Problern eine der ersten großen mathematischen Fragen, die mit Hilfe von Computern gelöst wu rden. Es geht dabei um folgende Aufgabe: Wie v iele Farben benö tigen wir mindestens, u m die Länder auf eine r beliebigen Landkarte zu färben? Hier wird von folgenden Bedi ngu ngen ausgegan gen : Die Fläche eines Landes ist zusam menhängend . Es gib t also keine un verbe nd ene n Geb iete, d ie d ie gleiche Farbe erhalten müssen. Sobald zwei Länder eine gemeinsames Stüc k Grenze haben, müssen sie un terschied lich gefärbt werden .
• • ." ....
">
~
Abbildung 42: Lässt sich jede Landkarte mit vier Farben färben?
An d ieser Aufgabe wurde über 150 Jah re lang gearbeitet: F. Guthrie hat 1852 vermu tet, dass vier Farben in jedem Fall genügen, um eine Lan d karte zu färben. Es war sehr einfa ch zu sehe n, d ass d rei Farben nicht immer genügen, es ließ sich jedoch kein Beispiel finden, d as fünf Farben erfordert hä tte. Diese Vermu tung wurde in d er folgen den Zeit meh rfach "bewi ese n" , wobei es teilweise Jahre dau erte, d ie Fehler in d iesen Beweisen zu entdec ken.
11 1
5 Künstliche In telligenz P. Heaw ood kon nte 1898 beweisen, d ass zum indes t fünf Farben imme r ausreichen [Hea18981.
K. Appel und W. Haken setzten einen Compu ter für den Beweis ein und konn ten 1977 zeigen, dass vier Farben tatsächlich immer genügen [ApH( 77). Dies gilt heu te als der ers te große Compu terbeweis . Dazu mussten aber 1936 releva n te Fälle durchgetestet werden, der Beweis war für d en Menschen also kaum nachvollziehbar. Die Anza hl de r relevanten Fälle wurd e schrittweise red uziert, bis N. Robertson, D. Sanders, P, Seymour und R Thomas 1996 " nur noch" 633 problema tische Fälle zu prü fen hatt en [RSST96]. G. Gonthier ging einen anderen Weg und konnte 2004 zusa mmen mit B. Wemer einen formalen Beweis erbringen. Dabei musste der Rechn er nicht mehr eingesetzt wer den, um Einzelfälle zu prüfen. Statt dessen wurde d as formale Beweissystem COQ verwendet [Gont04). Solche compu tergestütz ten math ematischen Beweissysteme bzw. -assistenten, wie sie Co nthter eingesetz t hat, möch ten wir hier der Symbolischen KI zuord nen. Wir sollten uns aber klar machen, welch extrem hohe Hürden überwunden werde n müssen, bevor ein derart gewonnener Beweis akzeptiert wer den kann: Das Grund wissen, mit dem unser symbolisches Kl-System gefü ttert wu rde, muss wide rspruchsfrei und korrekt sein. Die Regeln, nach denen das System neue Erkenntn isse gewinn t, mü ssen ebenfalls widerspruc hsfrei u nd korrekt sein. Säm tliche implementie rten KI-Algorithmen müssen korrek t sein.
Nicht nur die Verfah ren selbst, sondern auc h deren kon krete Program mierung in der jeweils genu tzten Softwa re muss fehlerfrei sein. Spä testens d ieser Punkt bereitet manchmal Probleme bei der Akzep tanz von Com put erbeweisen. d ie wege n ihrer Komplexität für den Menschen meist sehr schwierig nachvollziehbar sind. Wir müssen der Software also fast blind vertrauen. Wir werde n un s nun exemplarisch einige Beispiele intelligenter Algorithmen ansehen, die nicht in den Bereich d er Symbolischen KI fallen . Die Grenze zwischen " intelligenten" und " normalen" Verfahren ist natü rlich fließend, genetische Algorithmen können d urchaus auch als " in telligent" begriffen werd en.
112
5.1 Maschinelles Lernen
5.1 Maschinelles Lernen Die Grundidee des Maschi nellen Lerne ns besteht darin, Wissen aus Erfahrung zu gewinnen [MLl-2 ). Wir geben dem Compu ter also konkrete Beispiektsten, aus denen er eine allgemeine Regel ab leiten soll. Bisher hat der Compu ter eine konkrete A ufgabe gelöst, nachd em wir ihm d ie ollgemeine Lösungsvorschrift d afü r vorgegeben haben . Wir sollte n u ns dabei den fund ame ntalen Un tersch ied zur Symbolischen KI klar machen. Wir arbeiten hier nicht mit gesiche rte m Wissen und klaren Regeln. Wir arbeiten mit Beispielen. Unsere Lernergeb nisse können also nicht besse r sein als d ie Daten, mit de ne n wir arbeiten. Wen n wir etwa d as Freizeitverhalten untersuchen un d bei unseren Testpersonen zufä llig an einige Extremsportler geraten, d ie erst glücklich sind, wen n sie bei 3°C, N ieseiregen und Wind 10-15 km laufen d ürfen, dann ha ben wir woh l kein repräsentatives Ergeb nis zu erwarten.
a
tlt:i
-
-5
bet sptele: double log2tdouble x) ( retu m Math.log(x) I Math. l ogt2.0); } double anzahlKriter ien() { return (double) kr iterien. lengt h: } double anzahlBeispiele() { return (double) beispiele le ngth: } i nt positi onE rgebni so
{
ret urn kr-t terten. length :
}
i nt positionKriterium(String kri t eri um) { tor u nt i .. 0: i < rrtten en.t enctn: i++) i f (kri te rtenjt j .equal srarorecesetkn tertua» retur n t : return kri te r-i en. length: vot d
I
erm ttleAuspraegungenE rgebn;s()
if (auspraegungenErgebnis !- nul l ) retu rn: auspraegungenErgebnis .. new HashSet<Stri ng>(): ror (Stri ng[) tetsptet tetsctete: auspraegungenErgebni s. aoottet spie l [( int) IXJSi tt cnrrcecnt s()]):
void erm; ttleAuspraegungenKriteriumtString kri teri um ) {
122
Hash5et<Str;ng> auspraegungen .. auspraegungenKri tert um.get(kr;ter-' um): if (auspraegungen !- null) return:
5.1 Maschinell es Lernen eusoreecnce nc-tte rtue. put( kri t ert um. new Hash5et 0; for (String auspraegungErgebnis (
for (Stri ng[] befsptel
I
auspraegungenErgebnis)
tetsptete:
Str ing ergebnis - beispiel[(int) posit ionErgebnis()]; if (ergebniS.equalsIgnor€Case(auspraegungErgebnis» haeuftcket tenAus praegungenErgebni s[ tnoexj +- 1.0:
) tnoex....:
return haeuf igkeitenAuspraegungenErgebni s:
123
5 Künstliche In telligenz double i nformatio nsGehalt(double[] faell e) {
i nt l aenge · faelle.length: double summe - 0.0: tor u nt i · 0: i < teence . iH) SURm?
+-
feel Ielt}:
double ergebnis - 0.0: rcr tt nt i - 0: i < laenge: iH)
I
double wahrsche i n l i ch~ ei t - faelle[i] I SURm? : i f (wahrschei nli chkei t auspraegungen • auspraegungenKri tert um.geHkrite ri LITI): return (double) auspraegungen.size():
aesnset-strt rc- auspraegungenKri t erium(St ri ng kr t t erium l {
errni ttleAuspraegungenKri teri um( kri ter -i um) : ret urn auspraegungenKri te r-t l.I1I. geHkri ter-i um ):
double haeufigkeitAuspraegungKriterium(String t rtterun . String auspraegung) {
double wert · 0.0: tor (Stri ng[] tetsctet : tetsct ete:
I
Str ing e - beispiel[positionKriterium( kriteri um)]: i f re ecuatsrcroreceseceoscreecerei wert - 1.0:
return wert :
124
5.1 Maschinell es Lernen
double i nfcrmetj cnssenett Kri t ert um(St ri ng kri tert um) {
Hashtable tnro - new Hashtable<St r ing. Double>(): rcr (St ring[] tets ptet tets ptete: ( Stri ng key • beispiel[positionKrite ri um( kriterium)] + ":" +
beispiel [posi ti onErgebnis( )] :
if (infO.getCKey) -- null) tnro .wt uey. 0.0): tnto. putü ey. i nfO .get( key) + 1. 0) :
double informationsGehalt - 0.0 : for (St ring auspraegung : auspraegungenKrite riLlll( krite rium)) (
double[] t nromettonsrenerte - new ocubteünto. kevseto stzeoj: tnt t noex - 0: tor- (Stri ng key
I
tnrc.eer setr n
if (key.startsWith(auspraegung)) i nformat'i orscener te[ index++] • info .get( key):
i nrcmettonseere 1t +-
haeufig keitAuspraegungKri te rium( kriterium . auspraegung) I anzah1Bei sp'i ele() * tnto rmattonsreba1t( tntomat - onsGehal te) :
ret um tnrooettonseenet t: double i nforma ti onsGewinnKr it eri um(St ri ng kr ite ri um) {
return informatio nsGehalt(haeufigkeit enAuspraegungenErgebnis(» - i nfcrmat'i cnssenert rr-i te ri um (krit eri um):
125
5 Kü nstliche In telligen z
void entschelden() {
-
futig. nur eine
if CanzahlAuspraegungenErgebnis() < 2) return: for (String ergebnis auspraegungenErgebnis) {
Er~nis-A usprä gung
if ChaeufigkeitAuspraegungErgebnis(ergebnis) >:.................___ LI MIT HAEUF IGKEIT * anzah lBeispieleC) Fertig. fa st a lle Ergt.-bnio:r retu-n."
-
...
}
Ausprägcngn gleich
String staerkstesKriterium - nul l: double staerksterInfor mationsGewinn - 0.0: for (String kriterium : kriterien) {
double informationsGewinn = informationsGewinnKriteriumC kriter ium); i f CinformationsGewi nn > staerks terlnformationsGewi nn) staerksterinformationsGewinn = informationsGewi nn: staer kstesKriteriu m = kri teri um : }
}
if CstaerkstesKriteriu m == nul l II anzahlAuspraegungenKri terium(staerkstesKriterium) == 1 II staerkster lnformationsGewinn < LIMIT_INFORMATIONSGEWI NN) return:
-
...
for (String auspraeg ung auspraegungenKriteriumCstaerkstesKriterium)) {
Fertig. keire endeutige Klassitikation
int anzahl NeueBeispiele = 0; to- (String[] beispiel: beisptele) { }
if Cbeispiel[positionKriteriumCstaerkstesKriterium)] .equalsIgnoreCaseCauspraegung) anza hlNeueBeispiele++:
String[][] neueBe ispiele = new String[anzahlNeueBeispiele][(int) (anzah l KriterienC) + 1)] : int index - 0;
126
5,1 Maschinelle s Lerne n
t or (S t ring [] bet sptc! : betspiele) (
if (beispiel[ positionKriteri um (staerkstesKriterium)] ,equalsIgnoreCase(auspraegung) ) for ( f nt i 0: i __
5.2 Schwarmintelligenz Es ist allerdings nicht gara ntiert, d ass wir tatsäch lich d ie kürzeste Rundreise find en. Diese Tou r d urch 71 Städ te" sieh t sch on sehr gut aus:
In de r Tat ist d ie folgende Tou r noch kü rze r, wir erkennen aber mit bloßem Auge sofort, dass am Kreu zungsp unkt zwe ier Wege eine wei tere Verbesserung möglich ist:
/1
\\ c < \'-/r:/j rJ f .> r~ "
~) l
'~ 1
'''-- 1'' ''''''/
"
,
...........
/ \,
c ;]
\
"v
~.}\
v
",
,
~Io-r~_ : .
~.
/
"
\
~
-C7 )
C" "
"'-te--::_
"", '\'j'" ~?''7\' / ,-..1
\
"
CJ
u
Abbildung 52: Offensichtlich verbesserungsfähige Rundreise durch 71 Städte
,.
Wir beach ten hie r, dass 70! .. to ll l '
135
5 Künstliche In telligenz Eine einfache Im ple mentie ru ng könnt e folgend er ma ßen au ssehen:
class W ege {
ArrayLi st amei sen: double markierungen[)[ ] : i nt xcocrotnate l j. yKoordinatet j: i nt ent ternwqen] 1ll: t nt n:
Wege( int n. t nt anzah lAmeisen) { t hi s.n • n: 11 hier Koordirieten und Entfernungen belegen markierungen · new doubl e[n][n): tor (i nt i ·0 : i