Praktische Informatik I – Der Imperative Kern Suchen, Selektieren und Sortieren
Prof. Dr. Stefan Edelkamp
Institut für Künstliche Intelligenz
Technologie-Zentrum für Informatik und Informationstechnik (TZI) Am Fallturm 1, 28359 Bremen
+49-(0)421-218-64007 www.tzi.de/~edelkamp
Suchen, Selektieren und Sortieren
In diesem Abschnitt werden schnelle Verfahren auf geordneten Mengen besprochen, wobei die Effizienz in der Anzahl der Schlüsselvergleiche gemessen wird.
Outline
1 Gleichzeitige Suche von Mini- und Maximum
2 Schnelles Suchen
3 Schnelles Sortieren
4 Kryptogramme
5 Nimm
Zwei auf einen Streich
Dasgleichzeitige Finden vom Minimum und vom Maximumin einem Feld (engl. array) der Größenkann mit wenigen Vergleichen elegant durch Rekursion gelöst werden kann.
Dazu wird das Feld in zwei gleich große Teilegeteiltund das Minimum und Maximum in beiden Teilen bestimmt.
Nach dem rekursiven Aufruf kann man nun dasMaximum beider Maximaund dasMinimum beider Minimaberechnen und an die aufrufende Rekursionsstufeweiterleiten.
DerAbbrucherfolgt, wenn die Aufteilung zu einem Feld mit nur einem Element führt.
Der Einfachheit halber nehmen wir im Programm 1 an, dassneine
Programm 1:Mini- und Maximum-Berechnung.
importjava.util.Random;
public classMinMax {
int[]a;
public classMM{intmin;intmax; };
publicMinMax(intN) { Random r=newRandom();
a=new int[N];
for(inti=0;i<N;i++) { a[i] =r.nextInt(1000);
System.out.print(a[i]+",");
}
System.out.println();
}
MM search(intx,inty) { MM m=newMM();
if(y<=x+1) {m.min=a[x]<a[y] ?x:y;m.max=a[x]<a[y] ?y:x; } else{// y > x+1, recursive call required
MM l=search(x,(x+y)/2),r=search((x+y)/2+1,y);
m.min=a[l.min] <a[r.min] ?l.min:r.min;
m.max=a[l.max] >a[r.max] ?l.max:r.max;
} returnm;
} }
Outline
1 Gleichzeitige Suche von Mini- und Maximum
2 Schnelles Suchen
3 Schnelles Sortieren
4 Kryptogramme
5 Nimm
Partitionierung
Die Partition eines Arrays der Längenbzgl. eines ausgewählten Pivotelementsist eine Aufteilung in zwei Bereiche:
1 eines mit Elementen, die kleiner als das Pivotelement sind
2 eines mit Elementen, die größer als das Pivotelement sind Elemente, die gleich dem Pivotelement sind, können sich beliebig auf die Teillisten verteilen.
Selektion
DieSelektionsaufgabesucht nach demk.-kleinstenElement (z.B. ist die 3.-kleinste Zahl von 5, 1, 3, 7 die 5).
Dabei rekurriert Programm 7 nach dem Partitionierungsschritt.
Für Permutationen von 1, . . . ,nist dask.-kleinste Element in der aufsteigend sortierten Reihenfolge der Zahlen gleichk, d.h., bei der Eingabe 3 sollte die Ausgabe vergleichbar mit 3,6,1,7,8,4,9,5,2,0,
Quickselect3 = 3 sein.
Das mittlere Element in der sortierten Reihenfolge heißtMedian.
Programm 2:Selektion mit Quickselect und Sortieren mit Quicksort.
importjava.util.Random;
public classQuick {
private inta[];
/∗∗
∗Constructor for objects of class Quick
∗
∗@param n number of array elements
∗@param k position k for k−th element selection
∗/
publicQuick(intn,intk) { a=new int[n];
Random r=newRandom();
for(inti=0;i<n;i++)a[i] =i;
for(inti=1;i<n;i++)swap(i,r.nextInt(i));
System.out.println("Quickselect "+select(0,n−1,k));
for(inti=1;i<n;i++)swap(i,r.nextInt(i));
sort(0,n−1);
for(inti=0;i<n;i++)System.out.print(a[i]+" ");
System.out.println();
}
voidswap(inti,intj) {intt=a[i];a[i] =a[j];a[j] =t; }
Programm 3:Selektion mit Quickselect.
public intselect(intleft,intright,intk) { inti,j,v;
i=left;j=right+1;v=a[left];// set pivot do{// partition wrt. pivot
doj−−;while(j>=i&&v<a[j]);
doi++;while(i<=j&&a[i] <v);
if(j>i)swap(i,j);
}while(j>=i);
swap(left,j);// move pivot, end of partitioning if(k==j)returna[k];// element found
returnk<j?select(left,j,k) :select(j+1,right,k);
}
Outline
1 Gleichzeitige Suche von Mini- und Maximum
2 Schnelles Suchen
3 Schnelles Sortieren
4 Kryptogramme
5 Nimm
Quicksort
Das ProgrammQuicksortnutzt den gleichen Partionierungsschritt und rekurriert jedoch in beide Arrayhälften, um das Elementarray
letztendlich vollständig anzuordnen.
Quicksort ist noch immer eines derin der Praxis schnellsten Sortierverfahren
Es gibt oft fertige Bibliotheksfunktionen, wieIntrosort
Diese Verfahren wählen für ein (oder zwei) Pivotelement(e) den Median aus mehreren Elementen aus.
Introsortweicht für spezielle Fälle auf andere Sortierverfahren aus.
Die im Erwartungsfallbeste Wahl für das Pivotelementist der Median aus√
nvielen Elementen.
Programm 4: Sortieren mit Quicksort.
public voidsort(intleft,intright) { inti,j,v;// two indices and one temporary if(right−left> 0) {// interval is non−trivial
i=left;j=right+1;v=a[left];// set pivot do{// partition wrt. pivot
doj−−;while(j>=i&&v<a[j]);
doi++;while(i<=j&&a[i] <v);
if(j>i)swap(i,j);
}while(j>=i);
swap(left,j);// move pivot, end of partitioning
if(j−left<right−i+1) {sort(left,j−1);sort(j+1,right); } else{sort(j+1,right);sort(left,j−1); }
} } }
QuickXSort
Eine Verallgemeinerung istQuickXSort, bei dem in einem Teil des Arrays ein Sortierverfahren X angewendet wird und im anderen Teil rekuriert wird.
DieAufteilungder Elemente geschiehtumgekehrtzu Quicksort.
Da X nur auf den kleineren der beiden Teile angewendet wird, darf X sich Elemente aus dem größeren Teil und somit aufZusatzspeicher zurückgreifen.
Eine gute Wahl für X mit geringer Vergleichsanzahl istMergesort– einem klassischen Divide-and-Conquer Verfahren, das die zu sortierenden Datenrekursiv in kleinere Teile zerlegt
Die sortierten Teile werden dann imReißverschlussverfahrenzu größeren Teilenzusammengefügt(engl. (to) merge), bis eine sortierte
Outline
1 Gleichzeitige Suche von Mini- und Maximum
2 Schnelles Suchen
3 Schnelles Sortieren
4 Kryptogramme
5 Nimm
SEND + MORE = MONEY
EinKryptogramm(engl. Cryptarithm) ist eine mathematische Gleichung, deren Ziffern durch Variablen ersetzt wurden.
AufgabeFinde eine die Gleichung erfüllende Variablenbelegung.
BLAU * ROT VITA * MAX YIN WEG * STADT
--- --- + YANG ---
ANLR WXXX --- DDAET
OINL MWTX TEILT TTGNZ
ALNE WIWG ---
--- WCIG DISTANZ
ANTENNE --- WICHTIG
Das Problem gehört zur Klasse derConstraint-Satisfaction Probleme
Programm 5:Lösung eines Kryptogramms.
public classArithmetics {
publicArithmetics() {
for(intB=1;B<=9;B++) for(intL=0;L<=9;L++)if(L!=B)
for(intA=1;A<=9;A++)if(A!=L&&A!=B) for(intU=0;U<=9;U++)if(U!=A&&U!=B&&U!=L)
for(intR=1;R<=9;R++)if(R!=U&&R!=A&&R!=B&&R!=L) for(intN=0;N<=9;N++)if(N!=R&&N!=U&&N!=A&&N!=B&&N!=L) for(intT=0;T<=9;T++)if(T!=N&&T!=R&&T!=U&&T!=A&&T!=B&&T!=L)
for(intE=0;E<=9;E++)if(E!=T&&E!=N&&E!=R&&E!=U&&E!=A&&E!=B&&E!=L) for(intO=1;O<=9;O++)
if(O!=E&&O!=T&&O!=N&&O!=R&&O!=U&&O!=A&&O!=B&&O!=L) for(intI=0;I<=9;I++)
if(I!=O&&I!=E&&I!=T&&I!=N&&I!=R&&I!=U&&I!=A&&I!=B&&I!=L) if((B∗1000 +L∗100 +A∗10 +U)∗(100∗R+ 10∗O+T) ==
A∗1000000 +N∗100000 +T∗10000 +E∗1000 +N∗100 +N∗10 +E
&& (A∗1000000 +N∗100000 +L∗10000 +R∗1000 + O∗10000 +I∗1000 +N∗100 +L∗10 +
A∗1000 +L∗100 +N∗10 +E==
A∗1000000 +N∗100000 +T∗10000 +E∗1000 +N∗100 +N∗10 +E)) System.out.println("B="+B+" L="+L+" A="+A+" U="+U+" R="+R+" O="+O+" T="+T);
} }
Outline
1 Gleichzeitige Suche von Mini- und Maximum
2 Schnelles Suchen
3 Schnelles Sortieren
4 Kryptogramme
5 Nimm
Zwei Personen: Null Summe?
Viele Löser imZweipersonennullsummenspielwie im Schach beruhen aufMinimax und Alpha-Beta Pruning.
Eingabe istu, Schranken sindα,β. Die Ausgabe ist der ermittelte spieltheoretischer Wert.
Procedure NegmaxAlphaBeta
if(leaf(u))returnEval(u) No successor, static evaluation res←α Initialize valueresfor current frame for eachv ∈Successors(u) Traverse successor list val← −NegmaxAlphaBeta(v,−β,−res) Initialize cut-off value
if(val>res)res←val Updateres
if(res≥β)returnres Perform cut-off
returnres Return final evaluation
Manche Probleme lassen sich aber direkt lösen, z.B.TicTacToeoder Nimm. . .
Streichholz-Ziehen
Nimmist ein Zweipersonennullsummen-Spiel mit binären Ausgang.
ZustandEs gibtk Zeilen mitai,i =1, . . . ,k, Streichhölzern.
ZugDie Spieler nehmen abwechselndl Streihölzer aus einer Reihe j ∈ {1, . . . ,k}, so dassaj−lHölzer verbleiben.
Verlorenhat derjenige Spieler, der nicht mehr ziehen kann.
Retrograde-KlassifikationZustände lassen sich nach und nach in gewonnenundverloreneinteilen (Annahme: Optimales Spiel) Optimale Strategieberuht auf dem XOR-Wert der Binärzahlen der Streichholzzahl
XOR =bin(a1)⊗. . .⊗bin(ak)
Programm 6:Spiellogik im Nimm-Spiel.
voidprint() {
for(inti=0;i<size;i++) {
System.out.print(i+". ("+a[i]+"):\t");
for(intj=0;j<a[i];j++) System.out.print("|");
System.out.println();
}
System.out.println("xor :"+xor());
}
public voidtake(inti,intj) { a[i]−=j;
print();
}
public intxor() { intr= 0;
for(inti=0;i<size;i++) r=r^a[i];
returnr;
}
public booleangameover() { intr= 0;
for(inti=0;i<size;i++) r=r+a[i];
return(r==0);
}
Programm 7:Löser des Nimm-Spiels.
public voidsolve() { intr=xor();
if(r== 0) {
System.out.println("Your move!");
return;
}
System.out.println("My move!");
for(inti=0;i<size;i++) { for(intj=1;j<=a[i];j++) {
if(a[i] >=j) { inttemp=a[i];
a[i]−=j;
if(xor() == 0) {
System.out.println("Taking "+j+" from "+i);
print();
return;
}
a[i] =temp;
} } }
Merke:
Rekursive Algorithmen verfahren nach dem Prinzip
Teile-und-Herrsche(engl. divide-and-conquer) und lösen das übergeordnete Problem durch die rekursive Lösung der Teilprobleme und das Zusammenführen der rekursiv erzielten Lösungen.
Diskussion (untereinander):
Welche Funktionen (Min/Maximum, Auswählen, Bruchrechnen) braucht man wozu?
Welche Funktionen lassen sich iterativ umsetzen?
Für die automatische Generierung von zufälligen Zahlen wurde die Funktionrandgenommen. Sie liefert bei jedem Programmstart die gleichen Zahlen. Wozu ist das nützlich und wie kann man andere Zahlen generieren?