Informatik A Zusammenfassung 1. Daten und Zahlen 2-er Komplement Komplexitätsklassen Variablen Konstantenbezeichner Rechnen mit Binärzahlen Gleitkommazahlen 2. Java Kontrollstrukturen Algorithmus Methode Obkektorientierte Programmierung Binden (statisch, dynamisch) 3. Endliche Automaten formale Definition Bsp.: Binärzahl durch 3 teilbar ? 4. Rekursion Türme von Hanoi 5. Halteproblem 6. Sortieren Komplexität-Übersicht SelectionSort BubbleSort/ShakerSort MergeSort QuickSort BucketSort/RadixSort HeapSort Heap 7. Abstrakte Datentypen Liste Keller Schlange (binärer) Baum Suchbaum AVL-Baum Traversierungen Intervallinorder Mengen verwalten Spielbaum 8. Hashing Ofenes Hashing Geschlossenes Hashing Laufzeit 9. Graphen gerichteter/ungerichteter Graph Implementationen Adjazenzmatrizen Adjazenzlisten Typische Probleme für Graphen Traversieren, Hamiltonkreis, Traveling Salesman All-pairs-shortest-path, Single-source-shortest-path Topologisches Sortieren, Chinese Postman 10. Suchen Tiefensuche Breitensuche 11.Wichtige Algorithmen Codierung ganze Zahlen im 2-er Komplement 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 7 6 5 4 3 2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 Komplexitätsklassen: log n n n * log n n2 n3 2n n! binäre Suche lineare Suche schlaues Sortieren dummes Sortieren Gleihungssystem lösen alle Teilmengen alle Permutationen Variablen: Konstantenbezeichner Was sorgt für Speicherverwaltung? einfache Datentypen (float, char, int) Konstante Schlüsselwort „final“ Datentyp legt fest: Wertbereich Operationen Eingabe (char) in weiterverwendbare Zahlen (int) umwandeln: Jedem Zeichen ist ein ASCII-code zugewiesen Char c; (int) c liefert ASCII-Nummer in integer form Negative Zahl berechen: Gegeben –x : Finde +x : Inverieren : Addiere 1 : -4 0100 1011 1100 Bei Addition: Überlauf beachten! Trick: Vorzeichenbit verdoppeln, müssen nach Addition identisch sein: 00111 7 + 00001 1 01000 Ergebnis undefiniert 00011 3 11011 -5 11110 -2 Ergebnis ok! [[ Gleitkommazahlen ]] Darstellung: x = m * 2e Standart: float, double Verfahren: x = 0, 1, 0, 1, 1, 13,5 6875 3750 7500 5000 0000 = 1,6875 * 23 verdoppeln verdoppeln verdoppeln verdoppeln Reduzierte Mantisse: [1 0 1 1] = 0.6875 Exponent: [0 1 1] = 3 0 Vorzeichen 00000011 Exponent 10110000000000000000000 Reduzierte Mantisse Multiplikation: Addition: Exponenten addieren Mantisse multiplizieren Exponenten angleichen Mantisse addieren 12 * 20 = 3 4 (1,5 * 2 ) * (1,25 * 2 ) = (1,5 * 1,25) * (23 * 24) = 1,875 * 27 = 12 + 20 (1,5 * 23) + (1,25 * 24) (0,75 * 24) + (1,25 * 24) (0,75 + 1,25) * 24 240 = = = = 32 JAVA Kontrollstrukturen: Regeln dynamischen Ablauf der Anweisungsfolge eines Programms durch Bedingungen, Verzweigungen und Schleifen Abzählreim: Algorithmus = endlich lange Vorschrift, bestehend aus Einzelanweisungen Häufig benutzte Algorithmen Methode (Klassen- oder Objektbezogen) Schlüsselwort „static“ Klassenmethode Aufrufmechanismus: call-by-value Objektorientierte Programmierung: Klasse Objekt Konstruktor Datenfelder Vererbung Überschreiben Überladen Methoden Auf Konstruktor der Oberklasse zugreifen class Hund() Hund hasso Hasso = new hund() Hasso.gewicht = 42 class Schlittenhund extend Hund { } Hasso.sitz() super() Binden: Wenn mehrere Methoden mit gleichem Namen und unterschiedlicher Parameterliste existieren, ist die Methode überladen. Statisches Binden bezeichnet den Vorgang, einem Objekt zur Übersetzungszeit den Programmcode fest zuzuordnen; dynamisches Binden bezeichnet den Vorgang, einem Objekt zur Laufzeit den Programmcode zuzuordnen. Referenzen modellieren Zeiger, Verweise Endliche Automaten: 5-Tupel A = (S, ∑, δ, s0, F) - S == endliche Zustandsmenge ∑ == endliches Eingabealphabet δ: S x ∑ S == Überführungsfunktion s0 € S == Startzustand F € S == Endzustände Typische Überführungsfunktion im Code: z = delta[z] [ (int) eingabe – (int) ‘0‘] Beispiel: „ist Binärzahl durch 3 teilbar?“ Überführungsfunktion: Eingabe Zustände ↓ r0 r1 r2 r0 ist Start und Zielzustand 0 1 r0 r2 r1 r1 r0 r2 Java-Code Zustandüberführung mit einem Feld: Int [ ] [ ] delta = { (0,1), (2,0), (1,2) }; Zustand: s 0 1 2 Eingabe durchgehen: For (int i=0; i < zeile.length; i++) { Wende Überführungsfunktion an: s ist Zustand switch(zeile[i]) { case `0‘: s = delta[s] [0]; break; case `1’: s = delta[s] [1]; break; default……… } Je nach dem ob also ne 0 oder 1 als Eingabe kommt gehe in unserem Feld in den jeweiligen Zustand (in die jeweilige Zeile) // delta[2] [0] 2. Reihe 0.te Spalte [[ Suchen ]] Lineare Suche == Kompletter Durchlauf eines Arrays bis gefunden Worst case: O(n) 𝑛 Average case: O( ) 2 Binäre Suche == Suche in geordneter Reihe, immer Mitte der noch zu durchsuchenden Reihe betrachten und entscheiden ob rechts oder links weitergesucht werden muss. Immer: O( log n ) Rekursion: Erneuter Aufruf einer Methode aus dem Methodenrumpf heraus Typischerweise werden dabei die aktuellen Parameter so modifiziert, dass die Problemgröße schrumpft, damit nach mehrmaligem Wiederholen dieses Prinzips schließlich kein weiterer Aufruf erforderlich ist und die Rekursion abbrechen kann. Jeder rekursive Algorithmus lässt sich auch iterativ implementieren [[ Türme von Hanoi ]] n Scheiben mit abnehmender Größe liegen auf Startort A Sie sollen in derselben Reihenfolge auf Zielort C zu liegen kommen Die Regeln für den Transport lauten: 1.) Jede Scheibe muss einzeln transportiert werden 2.) Es darf nie eine größere Scheibe auf einer kleineren liegen 3.) Es darf ein Hilfsort B zum Zwischenlagern verwendet werden funktion bewege (Zahl i, Stab a, Stab b, Stab c) { falls (i > 0) { bewege(i-1, a, c, b); // n-1 von Start nach Zwischen verschiebe oberste Scheibe von a nach c; bewege(i-1, b, a, c); // n-1 Scheiben von Zwischen nach Ziel } } n-1 von Start nach Zwischen erste Scheibe von Start nach Ziel n-1 Scheiben von Zwischen nach Ziel Beweis durch Induktion Laufzeit: 𝑂(2𝑛 − 1) Induktionsverankerung: f(0) = 0 = 20 − 1 Induktionsschritt: Sei bis n − 1 bewiesen: f(n) = 2 · f(n − 1) + 1 = 2 · (2n−1 − 1) + 1 = 2n − 2 + 1 = 2n – 1 Rekursionsgleichung Induktionsannahme Halteproblem Beh.: Es gibt kein Programm, welches entscheidet, ob ein gegebenes Programm, angesetzt auf einen gegebenen Input, anhält. Beweis durchWiderspruch Annahme: Es gibt eine Methode haltetest in der Klasse Fee: public class Fee { public static boolean haltetest (char[]s, char[]t) { // // // // liefert true, falls das durch die Zeichenkette s dargestellte Java-Programm bei den durch die Zeichenkette t dargestellten Eingabedaten anhaelt; liefert false, sonst } } Dann lässt sich folgendes Java-Programm in der Datei Quer.java konstruieren: import AlgoTools.IO; public class Quer { public static void main(String[] argv) { char[] s = IO.readChars(); if (Fee.haltetest(s,s)) while (true); } } Sei q der String, der in der Datei Quer.java steht. Was passiert, wenn das Programm Quer.class auf den String q angesetzt wird ? D.h. java Quer < Quer.java 1. Fall: Hält an haltetest(q,q) == false Quer angesetzt auf q hält nicht! 2. Fall: Hält nicht haltetest(q,q) == true Quer angesetzt auf q hält an! Also kann es die Methode haltetest nicht geben! SORTIEREN Motivation für Sortieren: 1. Häufiges Suchen Einmal sortieren, dann jeweils log n Aufwand. 2. Tritt ein Element in zwei Listen L1, L2 auf? Sortiere L1 · L2, dann nach Doppelten suchen! Eine Obere Schranke bezieht sich auf einen konkreten Algorithmus......wie lange dieser braucht um das Problem zu lösen Untere Schranke sagt aus wieviele Durchläufe jeder Algorithmus für ein Problem mindestens durchlaufen muss. Also eine untere Schranke heisst, es kann keinen Algorithmus geben, der das Problem in einer schnelleren Laufzeit lösen kann O(n * log n ) ist untere Schranke für sortieren durch vergleichen, weil ein binärer Baum mit n! Blättern hat mindestens die Höhe [[ SelectionSort ]] jeweils das kleinste nach vorne O(n2) [[ BubbleSort ]] Jeweils unterschiedliche Paare vertauschen O(n2) Variation: ShakerSort: einmal von vorne, einmal von hinten 𝒏 𝟒 ∗ 𝒍𝒐𝒈 𝒏. [[ MergeSort ]] Divide&Conquer: sortiere 1. Hälfte, sortiere 2. Hälfte und vereinige Merge-Methode nimmt jeweils das kleinere aus beiden Mengen bis eine leer, dann Rest der anderen anhängen. O(n * log n) [[ QuickSort ]] Partitioniere die Folge in eine elementweise größere und eine elementweise kleinere O(n * log n) Quicksort: im ungünstigsten Fall O(n2) kann verhindert werden durch Bestimmung des Medians. (aber wenig sinnvoll) [[ BucketSort ]] Sortieren durch Verteilen auf einzelne Fächer (Buckets), Alphabet muss bekannt sein! O(n) [[ RadixSort ]] Sortieren von Strings über endlichen Alphabet, durch mehrmaliges anwenden von BucketSort O(n) [[ HeapSort ]] Benutze Heap und entferne immer wieder die Wurzel (kleinstes Element) bis leer Binärer Baum == entweder leer oder er besteht aus einem Knoten, dem 2 weitere Knoten zugeordnet sind Vater, Söhne, Wurzel, Blätter Heap == bis auf die letzte Ebene vollständiger binärer Baum, die einzelnen Knoten enthalten Schlüssel. Der Schlüssel eines Knotens ist stets kleiner als der seiner Söhne O(n * log n) Heap Implementation? Array [[Heap]] Ein Heap ist ein binärer Baum (Konstruktion von Heap O(n)) Der kleinste Schlüssel bildet die Wurzel des Baumes und alle Nachfolger sind größer. Insert: neues Element an das Ende der Heaps Element, solange nach oben schieben bis es in der Wurzel steht oder der Schlüssel des Vaters kleiner als der des Elementes selbst ist. delete: Wurzel entfernen und an seine Stelle das am Ende des Heaps befindliche Element setzen. Element solange nach unten schieben, (mit kleinstem Schlüssel tauschen) bis es Blatt ist oder die Kinder größere Schlüssel als das Element selbst besitzen. get: einfach Schlüssel aus Wurzel holen. O(1) (nur Minimum !) In welcher Reihenfolge werden die Elemente in dem Array gespeichert? der Breite nach Wurzel: Nummer 0 linker Sohn von Knoten i: 2*i +1 rechter Sohn von Knoten i: 2*i + 2 Welche Methoden sollte ein Interface Heap ankündigen? Object min( ) void delete( ) void insert(Object) Im obigen Array implementiert .....heapify durch tauschen im Array Kann man gut ein bestimmtes Element im Heap suchen oder herauslöschen? nein Wie wird der Heap aufgebaut? Warum ist Laufzeit hierfür O(n), obwohl eigentlich n mal mit Aufwand O(log n) repariert werden muss? mehr Knoten in unteren Ebenen [[ Externes Sortieren ]] Sortieren auf Medien mit sequentiellem Lese- und Schreibzugriff durch wiederholtes Mischen von bereits sortierten Folgen Lauf == monoton nicht fallende Teilfolge ADT == Abstrakter Datentyp (Datenstruktur + Operationen) Interface == Schnittstelle, die nur abstrakte Methodenköpfe und Konstanten enthält, muss implementiert werden [[ Liste ]] Liste == Folge von Elementen mit einem sog. Aktuellen Element Operationen: empty( ) endpos( ) reset( ) advance( ) elem( ) insert(Object x) delete ( ) Liefert true, falls leer Liefert true, wenn Liste abgearbeitet Das erste Listenelement wird zum aktuellen Der Nachfolger des akt. wird zum aktuellen Liefert das aktuelle Element Fügt vor das aktuelle Element ein Element ein; das neu eingefügte wird zum aktuellen Element Löscht das aktuelle Element [[ Keller ]] Keller == Folge von Elementen mit einem sog. Top-Element (Last in – First out) Operationen: empty( ) push(Object x ) top( ) pop( ) Liefert true, falls Keller leer ist Legt Element auf Keller Liefert oberstes Element Entfernt oberstes Element [[ Schlange ]] Schlange = Folge von Elementen mit einem sog. Front-Element (First in – First out) O(log n) ? Operationen: enq(Object x ) deq( ) front( ) empty( ) Fügt x hinten ein Entfernt vorderstes Element Liefert vorderstes Element Liefert true, falls leer [[ Baum ]] (binärer) Baum = … ist entweder leer oder besteht aus einem Knoten, dem ein Element und 2 binäre Bäume (Söhne) zugeordnet sind Operationen: empty( ) left( ) right( ) value( ) Liefert true, falls Baum leer ist Liefert linken Teilbaum Liefert rechten Teilbaum Liefert Wurzelelement was implementieren Bäume? Menge Die Höhe eines Baumes hängt logarithmisch von de Anzahl seiner Elemente ab [[ Suchbaum ]] (links < rechts) O(log n) Suchbaum Suchbaumoperationen: lookup(Compareable x) insert(Compareable x) delete(Compareable x) sucht x in Menge liefert Comparable-Objekt falls gefunden liefert null sonst versucht x in Menge einzufügen liefert true, falls erfolgreich liefert false sonst versucht x aus Menge zu entfernen liefert true falls erfolgreich liefert false sonst public Comparable lookup(Comparable x) Beginnt bei Wurzel Solange Knoten vorliegt Vergleiche x mit Knoteninhalt Dann je nach Vergleich entweder In linken Teilbaum oder rechten Teilbaum weitermachen [[ AVL-Baum ]] (ausgeglichen) O(log n) AVL-Baum == ausgeglichener Suchbaum (Höhen seiner Söhne unterscheiden sich um höchstens 1) bal(x) = höherechts – höhelinks Höhe eines AVL-baums ist ≤ 1,45 * log n also höchstebns 45% größer als erforderlich Einfügen: höchstens 1 Rotation Löschen: höchstens x Rotationen mit x = Anzahl der Knoten auf dem Weg zur Wurzel O(log n) Suchen: O(log n) [[ Traversierungen ]] Rekursive Implementierungen inOrder( knoten ): if knoten.links ≠ null then inOrder( knoten.links ) print( knoten ) if knoten.rechts ≠ null then inOrder( knoten.rechts ) preOrder( knoten ): print( knoten ) if knoten.links ≠ null then preOrder( knoten.links ) if knoten.rechts ≠ null then preOrder( knoten.rechts ) postOrder( knoten ): if knoten.links ≠ null then postOrder( knoten.links ) if knoten.rechts ≠ null then postOrder( knoten.rechts ) print( knoten ) Iterative Implementierung von Traversierungen mit Zuhilfenahme von Kellern Intervallinorder: (abfangen) Intervall-inOrder( knoten ): if knoten.links ≠ null + if knoten.links > intervallanfang inOrder( knoten.links ) if knoten < intervallende { print( knoten ) if knoten.rechts ≠ null then inOrder( knoten.rechts ) } Intervallpreorder Tiefensuche im Intervall // Preorder iterativ Tiefensuche.... then Mengen verwalten Was für Sachen muss man den mit Daten so machen wenn man sie verwaltet? speichern, wiederfinden, löschen Angenommen, ein Kunde will die Implementation einer Menge, wie realisiere ich das? Hashing oder Suchbaum. [[ Spielbaum ]] Spielbaum == Baum mit zwei Typen von Knoten: Minimum und Maximum Wert eines Blattes durch statische Stellungsbewertung Wert eines min-Knotens: min der Söhne Wert eines max-Knotens: max der Söhne Zur Implementation: der Baum ist nicht binär, daher hat jeder Knoten einen Verweis auf eine Liste von Söhnen [[ Hashing ]] Offened Hashing == Einträge werden in einem Array von Buckets verwaltet, mehere Elemente können also den selben Hashwert belegen. Bei N Buckets und n Einträgen enthält jede Liste durchschnittlich n N Buckets Geschlossenes Hashing == Beschränkte Anzahl von Plätzen Verwaltung durch ein Comparable[] inhalt und ein byte[] zustand verschiedene Sondierfunktionen zum Behandeln von Kollisionen Falls 𝑦 = 𝑓(𝑥) schon belegt ist, so suche für x einen Alternativplatz - Lineares Sondieren: y, y+1, y+2, y+3, ..... - Quadratisches Sondieren: y, y+1, y+4, y+9, ..... - Rehashing: y, y+f2(x), ...... Laufzeit: n = Anzahl der gespeicherten Objekte, N = Anzahl der Speicherpositionen, 𝑛 = α = Auslastungsfaktor 𝑁 erfolglose Suche: erfolgreiche Suche: 1 1− 𝛼 −ln(1− 𝛼) 𝛼 = 5,0 (für α = 0,8) = 2,01 (für α = 0,8) In 2 Schritten wird von 1.000.000 Elementen aus einer 1.250.000 großen Tabelle das richtige gefunden (AVL benötigt ezwa 20 Vergleiche) Bei höherem α steigt die Anzahl der Schritte stark an (exponentiell?) [[ Graphen ]] Gerichteter Graph: G = (V,E) E V (Knotenmenge), V x V (Kantenmenge) Kanten sind einseitig Hier: V = {a,b,c,d} E = {(a,c),(a,b),(c,b),(c,d),(d,b),(b,d)} Ungerichteter Graph: G = (V,E) E V (Knotenmenge) P2(V) = 2-elementige Teilmenge von V Kanten können über eine Kostenfunktion gewichtet sein c: E {Reele Zahlen} Mit Graphen können binäre beziehungen zwischen Objekten modelliert werden. Die Objekte werden durch die Knoten, die Beziehungen durch die Kanten modelliert. Implmentationsmöglichkeiten: (1) durch Adjazenzmatrizen: - Platzbedarf: O( |𝑉|2 ) - Zugriff auf einzelne Knoten in konstanter Zeit möglich - Kein effizientes Verarbeiten von Nachbaknoten - sinnvoll bei dicht besetzten Graphen und bei Algorithmen die wahlfreien Zugriff auf eine Kante benötigen Unbewertete Kanten: Bewertete kanten: (2) durch Adjazenzlisten: - Platzbedarf: O( |E| ) - kein effizienter Zugriff auf Kanten möglich - sinnvoll bei dünn besetzten Graphen und bei Algorithmen die auf Nachbarn eines Knotens zugreifen müssen [[ Typische Probleme für Graphen ]] Traversieren: jeden Knoten genau einmal besuchen (Hamilton-Path) All-pairs-shortest-path-Problem: günstigster Weg für jede Start-Ziel-Variante n x n Matrix in Polynomialzeit lösbar Multiplikation zweier n x n-Matrizen O(n3) oder hoch2,7 Single-source-shortest-path-Problem: Von einem bestimmeten Knoten kürzester Weg zu allen Knoten in Polynomialzeit lösbar Weg von a nach f: bestens 10 Sequenz: a b d c e f Laufzeit: O( |E| * log(|V|) ) auf Grund von Heapsortierungen (Anwendung) Hamilton-Kreis: Jeden Ort besuchen, Rückkehr an Ausgangsort Laufzeit: O(2n) Travelling Salesman: Günstigster Hamilton-Kreis Topologisches Sortieren: Nummerierung der Knoten, dass jeder Knoten eine Zahl ∈ {0, … , 𝑛 − 1} erhält Suche 𝑓 ∶ 𝑉 → {0, … , 𝑛 − 1} mit 𝑓(𝑥) < 𝑓(𝑦) falls (𝑥 , 𝑦) ∈ 𝐸 Laufzeit: O( |E| ) jeden Knoten einmal ansehen konstant Chinese Postman Jeden Weg (Kante) einmal ablaufen (Eulerweg) Laufzeit: O( nk ) [[ Tiefensuche ]] 1. Bestimme den Knoten an dem die Suche beginnen soll 2. Expandiere den Knoten und speichere alle Nachfolger in einem Keller 3. Rufe rekursiv für jeden der Knoten in dem Stack Tiefensuche auf o Falls der Keller leer sein sollte, tue nichts o Falls das gesuchte Element gefunden worden sein sollte, brich die Suche ab und liefere ein Ergebnis Tiefensuche im Intervall 1. Bestimme den Knoten, an dem die Suche beginnen soll, und bestimme die maximale Suchtiefe 2. Prüfe, ob der aktuelle Knoten innerhalb der maximalen Suchtiefe liegt o Falls nein: Tue nichts o Falls ja: 1. Expandiere den Knoten und speichere alle Nachfolger in einem Keller 2. Rufe rekursiv für jeden der Knoten des Keller Tiefensuche im Intervall auf und gehe zu Schritt 2 Tiefensuche auf Baum mittels Keller: 1. Baum in Keller 2. Baum aus Keller 3. Wurzel pop return 4. Rechter Sohn? Ja Keller 5. Linker Sohn ? Ja 3. bis nichts mehr im Keller Laufzeit: entspricht preorder [[ Breitensuche ]] 1. Bestimme den Knoten, an dem die Suche beginnen soll, und speichere ihn in einer Warteschlange ab. 2. Entnimm einen Knoten vom Beginn der Warteschlange und markiere ihn. o Falls das gesuchte Element gefunden wurde, brich die Suche ab und liefere „gefunden“ zurück. o Anderenfalls hänge alle bisher unmarkierten Nachfolger dieses Knotens, die sich noch nicht in der Warteschlange befinden, ans Ende der Warteschlange an. 3. Wiederhole Schritt 2. 4. Wenn die Warteschlange leer ist, dann wurde jeder Knoten bereits untersucht. Beende die Suche und liefere „nicht gefunden“ zurück. Laufzeit: Breitensuche auf einem Baum mit Hilfe einer Schlange 1. Baum in Schlange 2. Baum aus Schlange 3. Wurzel deq return 4. Linker Sohn ? Ja Schlange 5. Rechter Sohn? Ja Schlange 6. Gehe zu 2. bis nichts mehr in Schlange Unterschied: Markieren mus nicht gemacht werden, da im Baum keine Schleifen möglich sind. Beliebiger Knotennachbar bei Graph [[ wichtige Algorithmen ]] Collatz: while (x != 1) { if(x%2==0) x/=2; else 𝑥 = 𝑥 ∗ 3 + 1; counter++; } // wenn x gerade: x = 𝑥 2 sonst: 𝑥 =𝑥∗3+1 Fakultät: int fak = 1; for (i=1; i <= n; i++) fak *= rekursiv: static int fak(int n) { if(n==0) return 1; else return n * fak(n-1); } GGT while(y!=0) z=x%y; x=y; y=z; } { rekursiv static int ggt(int x, int y) if(y==0) return x; else return gt(y,x%y); } { i; Dezimal Dual Positive ganze Zahlen: while(x!=0) { if(x%2==0) System.out.println(“0“); else System.out.println(“1“); x/=2; } obacht bits werden rückwärts generiert Mantisse: for(int i = 0; i<23; i++) { f*=2; if(f>=1) { System.out.println(“1“); f--; } else System.out.println(“0“); } Sieb des Eratosthenes int n = 1000 //willkürliche Obergrenze boolean[] sieb = new boolean[n]; for(int i=2; i<n; i++) sieb[i]=true; for(int i=2; i<n; i++) { if(sieb[i]) System.out.println(i); for(int j = i+1; j<n; j+=i) sieb[i]=false; //streiche alle Vielfachen der gefundenen Primzahl } } Binäre Suche int[] a = {1,2,3,4,5,6,7,8,9}; int mitte, links, rechts; mitte = links = 0; rechts = a.length-1; while(links<=rechts) { mitte = (links + rechts) / 2; if(a[mitte]==x) break; //falls gefunden: fertig if(a[mitte]<x) links=mitte+1 //zu weit links? else rechts = mitte-1; //zu weit rechts? } //Sprung in die Mitte, weiter suchen links oder rechts. Dort wieder Sprung in die Mitte Fibonacci static int fib(int n) { if(n<=1) return 1; else return fib(n-1)+fib(n-2); } // 1 1 2 3 5 8 13