Probabilistische Algorithmen 2 Java
Aus ProgrammingWiki
Wiki-Quelltext dieser Seite.
<language>Java</language>
===Einführung in proballistische Algorithmen===
[[Einführung in probalistische Algorithmen]]
[[Probabilistische_Algorithmen_2]]
==Zufallszahlen==
===Einführung===
Für probabilistische Algorithmen und Vorhersagen benötigt man Zufällige Ereignisse.
Der Computer ist auf Grund des Determinismus (eine bestimmte Eingabe führt bei identischer Verarbeitung stets zu der selben Ausgabe) ein schlechter Zufallszahlengenerator.
Die Frage, was kann man überhaupt als Zufall bezeichnen?, beantworten wir für uns so: Ein Zustand tritt dann zufällig ein, wenn er nicht vorhersagbar ist.
Im folgenden Beispiel nehmen wir einen Anfangswert (seed) und berechnen daraus unsere "Zufallszahl" ... schon die Aussage Zufallszahl BERECHNEN lässt uns erahnen, dass dahinter "nur" ein deterministischer Algorithmus steckt, der jedoch auf Grund seiner Komplexität einfach nicht so leicht überschaubar ist.
Wir nutzten für den Aufruf die Java-Funktion "Random".
Der Algorithmus, der sich dahinter verbirgt arbeitet in etwa so: Zufallszahl = (sehr_große_Prim * seed + sehr_große_Prim2) modulo 2⁶⁴
Damit kann man nun auch eine "zufällige" Kette von Zahlen erzeugen: Zufallszahl = (sehr_große_Prim * Zufallszahl-1 + sehr_große_Prim2) modulo 2⁶⁴
Nun liefert uns das jedoch zwar eine große Zahl, jedoch jedes mal die gleiche...wenn wir nun jedes Mal eine beliebige Zahl eingeben würden hätten wir ein durchaus unduchschaubares Ergebnis, JEDOCH nur wenn wir das Ergebnis noch nicht kennen. Noch besser währe es, wenn wir nichteinmal die Eingabezahl wüssten...
Der implementierte Algorithmus kann das.
Rufen Sie die Prozedur ohne Eingabeparameter auf.
Der hierbei verwendete seed ist die UNIX-Systemzeit. Das ist ausreichend unvorhersehbar für zum Beispiel Simulationen, reicht jedoch noch lange nicht aus in der Kryptologie.
Der '''prinzipielle Anspruch an Zufallszahlen''' ist: möglicht große bzw möglichst keine erkennbare Periode, gleichförmige Verteilung und keinerlei Zusammenhänge zwischen den gewonnen Zahlen.
Der Anspruch an Zufallszahlen variiert sehr stark je nach Anwendungsbereich. Für ein simples Würfelspiel reicht es schon aus, wenn man den Algorithmus nicht durchschauen kann...dh. die Ergebnisse ausreichend unvorhersehbar und ausreichend gleich verteilt sind. Für Anwendungen in der Kryptografie hingegen ist es unbedingt erforderlich nicht reproduzierbare Zahlen zu erzeugen - also benötigen wir ein nicht deterministisches Verfahren.
Da das ureigenste Prinzip des Computers der Determinismus ist, ist uns dieses nicht möglich.
Vielmehr können wir nur versuchen unseren seed schon sehr beliebig (möglichst zufällig) zu wählen.
Hierfür bietet sich die UNIX-Systemzeit an (ein sich immerhin im Sekundentakt ändernder Wert).
Für Anwendungen die unter allen Umständen unnachvollziehbar bleiben sollen oder eine sehr hohe Taktrate an Zufallszahlen benötigen ist dies jedoch immernoch nicht ausreichend.
Testen sie die Java-Prozedur myRandom, zunächst mit selbst gewähltem seed.
Bei keinem selbst vorgegebenen seed, nimmt die Prozedur myRandom die UNIX-Systemzeit und wir erhalten (für uns) echte unvorhersagbare Zufallszahlen.
<code>
static long myRandom(long seed){
java.util.Random r = new java.util.Random(seed);
return r.nextInt();
}
static long myRandom(){
java.util.Random r = new java.util.Random();
return r.nextInt();
}
</code>
<eval>
myRandom(123456789);
</eval>
...ein kleiner Anreiz zum ausprobieren: java.lang.Math.abs(myRandom()%100);
====Es gibt natürlich auch noch andere Möglichkeiten Zufallszahlen zu erzeugen====
Um für die manche Anwendungsfälle in der Praxis taugliche Zufallszahlen zu erzeugen werden physikalische Zufallszahlengeneratoren verwendet.
Sie nutzten physikalische Prozesse wie elektrisches oder thermisches Rauschen, Bildrauschen von Web-Cams oder Radioaktiven Zerfall.
Ein nettes Beispiel findet ihr hier: http://von-und-fuer-lau.de/randcam.html
===Algorithmen===
[[Las-Vegas-Algorithmen]]
====Monte-Carlo-Algorithmus Gruppe 2====
Ein Monte-Carlo-Algorithmus ist ein Algorithmus welcher immer ein Ergebnis liefert, allerdings mit einer festlegbaren
Wahrscheinlichkeit ein fehlerhaftes Resultat zurückliefert. Mithilfe von Monte-Carlo-Algorithmen lassen sich Probleme
lösen die sonst nur schwer bis unlösbar sind.
=====Multimengenvergleiche=====
<code>
public java.util.List multimenge(int intervall,int anzahl){
java.util.List mmenge=new java.util.LinkedList();
java.util.Random r=new java.util.Random();
if(intervall==0)
return null;
for(int i=0;i<anzahl;i++){
mmenge.add(r.nextInt(intervall));
}
return mmenge;
}
String printmenge(java.util.List list){
String liste="";
liste+=" (";
for(int i=0;i<list.size();i++){
liste+=list.get(i)+" ";
}
liste+=" )";
return liste;
}
</code>
Zum Anfang, was sind überhaupt Multimengen. Multimengen unterscheiden sich von normalen Mengen nur insoweit das jeder Wert <math>1</math> bis <math>n</math> mal vorkommen kann. Viel mehr gibt es dazu auch nicht zu sagen. Nun kommt man zu dem Problem der Äquivalenzprüfung.
Würde man zum Beispiel die Multimengen <math>$M_1=\{1,2,2,3,4,5\}$</math> und <math>$M_2=\{5,4,2,3,2,1\}$</math> auf Äquivalenz überprüfen wollen,
müsste man die beiden Multimengen ordnen und dann auf Äquivalenz prüfen. Dies kostet im Mittel <math>$\mathcal{O}(n log n)$</math>,
besser wäre allerdings<math> $\Theta(n)$</math>, dies vermag ein propabilistischer Algorithmus zu erreichen.
Es wird verabredet das jeder Multimenge <math>$M_m=\{x_1,x_2,...,x_n\}$</math> ein Polynom <math>$p_{M_m}$</math> zugeordnet wird.
Dieses Polynom wird nach folgendem Muster gebildet.
<math>$p_{M_m}(x)=(x-x_1)(x-x_2)...(x-x_n)$</math>
Nun werden folgende Regeln verabredet:
1. <math>$M_1\equiv M_2$</math>, wenn <math>$p_{M_1}(x)\ =\ p_{M_2}(x)$</math> für ''alle'' ganzen Zahlen <math>$x$</math> zutrifft.
2. <math>$M_1\not\equiv M_2$</math>, wenn in 1. beschriebenes Verfahren für mindestens ein <math>$x$</math> fehlschlägt.
Was bedeutet das für uns. Nunja, sobald wir auf ein <math>$x$</math> stoßen, für welches die Funktionswerte der beiden Polynome unterschiedlich sind so ist ziemlich sicher das die Multimengen unterschiedlich sind. Um die Wahrscheinlichkeit zu erhöhen wird der Test für zufällige <math>$x$</math> wiederholt.
Konkret schaut das dann wie folgt aus.
Wir haben die 3 Multimengen''' m1 ,m2 ,m3''' zur Verfügung. m2 ist die Spiegelung zu m1, m3 ist eine gänzlich andere Multimenge
Die Hilfsprozedur '''poly''' liefert den Polynomwert der Multimengen für einen gegebenen Wert.
<code>
java.util.List m1=multimenge(7,6);
java.util.List m2=m1;
java.util.List m3=multimenge(7,6);
int poly(int x,java.util.List nstlist){
int erg=1;
if(nstlist.isEmpty())return 1;
for(int i=0;i<nstlist.size();i++){
erg=erg*(x-(Integer)nstlist.get(i));
}
return erg;
}
</code>
<eval>
System.out.println("Polynomwerte:\nm1 "+poly(3,m1)+"\tm2 "+poly(3,m2)+"\tm3 "+poly(3,m3));
</eval>
Bitte mehrmals ausführen und Rückgaben beobachten.
Wenn man die Prozedur über alle 3 Multimengen laufen lässt bekommt man mit hoher Wahrscheinlichkeit verschiedene Werte.
Es kann aber auch vorkommen das für alle 3 Multimengen der gleiche Polynomwert herauskommt.
Man kann deshalb allerdings noch nicht von einer Äquivalenz ausgehen. Dafür sind mehrere Versuch mit zufälligen Eingabewerten notwendig.
Dafür verwenden wir die Prozedur '''m-aequiv?''' und die Hilfsprozedur '''m-aequiv*?''' welche die Wiederholungen für uns übernimmt.
<code>
boolean aequivalenztest(java.util.List m1,java.util.List m2,int r,int wdh){
java.util.Random rand=new java.util.Random();
int random=0;
for(int i=0;i<wdh;i++){
random=rand.nextInt(r);
if(poly(random, m1)!=poly(random, m2))return false;
}
return true;
}
</code>
<eval>
System.out.println("m1=m2? "+aequivalenztest(m1,m2,4,10)+" m1=m3? "+aequivalenztest(m1,m3,4,10));
</eval>
Und noch eine komplette Ausgabe von Anfang bis Ende.
<eval>
System.out.println("m1 "+printmenge(m1)+"\tm2 "+printmenge(m2)+"\tm3 "+printmenge(m3));
System.out.println("Polynomwerte für den ersten Durchlauf:\nm1="+poly(3,m1)+"\tm2="+poly(3,m2)+"\tm3="+poly(3,m3));
System.out.println("m1=m2? "+aequivalenztest(m1,m2,4,10)+"\tm1=m3? "+aequivalenztest(m1,m3,4,10));
</eval>
=====Fermat=====
Der Aufwand für den Primzahltest nach der "Schulvariante" mit Probeteilern
=====Solovay/Strassen=====
===Ausblick===
====Genetische Programmierung====
[[Schwarmalgorithmen]]
===Aufgaben===
===1===
Implementieren Sie einen Generator für Pseudozufallszahlen nach der Kongruenzmethode. (Buch S. 124)
<math>z_{n}=(a*z_{n-1}+b)mod\ c</math>
mit
<math>z_0=4567\\a=9749\\b=0\\c=2^{18}=262144</math>
(Um einen zufälligen Startwert anzunehemn gibt es zwar auch Sprachelemente in Scheme, jedoch funktionieren diese hier in der Wiki nicht.)
===2===
Setzen Sie sich an einem schönen Tag ins Freie, und beobachten Sie Ameisen bei der Futtersuche. Welche Beziehungen zu dem Ameisenalgorithmus können Sie erkennen? Welche Beispiele aus der Natur können noch einen Anreiz darstellen, sie als Vorbild für einen Algorithmus zu nutzen?
===3===
Entwerfen sie einen Quicksort-Algorithmus, welcher sein Pivot-Element zufällig wählt. Vergleichen sie die effizienz mit der Quicksort-Variante, welche immer das erste Element als Pivot-Element nutzt.
[[Aufgabenverteilung]]
===Quellen===
http://www.riskglossary.com/link/monte_carlo_method.htm
Oliver Lau "Faites vos jeux! - Zufallszahlen erzeugen, erkennen und anwenden" (c't 2009, Heft 2)
http://www.addison-wesley.de/Service/Krueger/kap12006.htm
Schickinger, Steger - Diskrete Strunkturen - Band 2 - Wahrscheinlichkeitstheorie und Statistik
Juraj Hromkovič - Algorithmics for Hard Problems
Juraj Hromkovič - Randomisierte Algorithmen
http://www.m-boehmer.de/pdf/Schwarmintelligenz_Artikel.pdf
Jörg Rothe - Komplexitätstheorie und Kryptologie