Eigenschaften von Algorithmen

Slides:



Advertisements
Ähnliche Präsentationen
Rekursion: Rekurrenz: Algorithmen rufen sich selbst (rekursiv) auf.
Advertisements

Falls Algorithmen sich selbst rekursiv aufrufen, so kann ihr Laufzeitverhalten bzw. ihr Speicherplatzbedarf in der Regel durch eine Rekursionsformel (recurrence,
Mathematik hat Geschichte
Schnelle Matrizenoperationen von Christian Büttner
Rekursion vs. Iteration
LS 2 / Informatik Datenstrukturen, Algorithmen und Programmierung 2 (DAP2)
LS 2 / Informatik Datenstrukturen, Algorithmen und Programmierung 2 (DAP2)
LS 2 / Informatik Datenstrukturen, Algorithmen und Programmierung 2 (DAP2)
Software-Engineering II Eingebettete Systeme, Softwarequalität, Projektmanagement Prof. Dr. Holger Schlingloff Institut für Informatik der Humboldt.
Zusammenfassung der Vorwoche
Suche in Texten (Stringsuche )
3. Berechenbarkeit Wann ist eine Funktion (über den natürlichen Zahlen) berechenbar? Intuitiv: Wenn es einen Algorithmus gibt, der sie berechnet! Was heißt,
Modelle und Methoden der Linearen und Nichtlinearen Optimierung (Ausgewählte Methoden und Fallstudien) U N I V E R S I T Ä T H A M B U R G November 2011.
Terminierung und Deadlocks Enkhbat Daginaa Betreuerin Prof. Heike Wehrheim Totale Korrektheit.
Klicke Dich mit der linken Maustaste durch das Übungsprogramm!
Sortierverfahren Richard Göbel.
Sortierverfahren Richard Göbel.
Übung 6.1Turing-Maschine 1.Machen Sie sich mit der Funktionsweise des Busy Beaver-Programms vertraut Vollziehen sie die 11 Schritte der ersten Turing-Tabelle.
Algorithmus. Ein Kochrezept, zum Beispiel: Kartoffelbrei.
WS Algorithmentheorie 13 - Kürzeste (billigste) Wege Prof. Dr. Th. Ottmann.
WS Algorithmentheorie 02 - Polynomprodukt und Fast Fourier Transformation Prof. Dr. Th. Ottmann.
Dynamische Programmierung (2) Matrixkettenprodukt
1 Vorlesung Informatik 2 Algorithmen und Datenstrukturen (02 – Funktionenklassen) Prof. Dr. Th. Ottmann.
Vorlesung Informatik 2 Algorithmen und Datenstrukturen (02 – Funktionenklassen) Tobias Lauer.
Vorlesung Informatik 2 Algorithmen und Datenstrukturen (02 – Funktionenklassen) Prof. Dr. Th. Ottmann.
WS Algorithmentheorie 08 – Dynamische Programmierung (2) Matrixkettenprodukt Prof. Dr. Th. Ottmann.
Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 2 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.
Kapitel 1 Die natürlichen und die ganze Zahlen. Kapitel 1: Die natürlichen und die ganzen Zahlen © Beutelspacher/Zschiegner April 2005 Seite 2 Inhalt.
EINI-I Einführung in die Informatik für Naturwissenschaftler und Ingenieure I Vorlesung 2 SWS WS 99/00 Gisbert Dittrich FBI Unido
PKJ 2005/1 Stefan Dissmann Rückblick auf 2005 Was zuletzt in 2005 vorgestellt wurde: Klassen mit Attributen, Methoden und Konstruktoren Referenzen auf.
PKJ 2005/1 Stefan Dissmann Zusammenfassung Bisher im Kurs erarbeitete Konzepte(1): Umgang mit einfachen Datentypen Umgang mit Feldern Umgang mit Referenzen.
PKJ 2005/1 Stefan Dissmann Zusammenfassung der Vorwoche Variable stehen für (einen) Wert, der sich im Programmablauf ändern kann. Variablen besitzen einen.
Zusammenfassung Vorwoche
Zahlen mit Zahlen ausmessen
Datenstrukturen, Algorithmen und Programmierung 2 (DAP2)
Heute: Scherenzange zeichnen
Rechneraufbau & Rechnerstrukturen, Folie 12.1 © W. Oberschelp, G. Vossen W. Oberschelp G. Vossen Kapitel 12.
20:00.
© Gabriele Sowada © Gabriele Sowada 2 Manuell Beispiel 1 demonstriert die Vorgehensweise bei der manuellen Programm- Eingabe am.
Effiziente Algorithmen
Materialien zum Informatikunterricht (Pohlig-Häberle)
LS 2 / Informatik Datenstrukturen, Algorithmen und Programmierung 2 (DAP2)
Effiziente Algorithmen
BIT – Schaßan – WS 02/03 Basisinformationstechnologie HK-Medien Teil 1, 11.Sitzung WS 02/03.
Effiziente Algorithmen Hartmut Klauck Universität Frankfurt SS
Effiziente Algorithmen
Black Box Algorithmen Hartmut Klauck Universität Frankfurt SS
Effiziente Algorithmen Hartmut Klauck Universität Frankfurt SS
Polynome und schnelle Fourier-Transformation
Einführung in die Programmierung Wintersemester 2013/14 Prof. Dr. Günter Rudolph Lehrstuhl für Algorithm Engineering Fakultät für Informatik TU Dortmund.
Eigenschaften von Algorithmen
Wiederholte Programmausführung
PROCAM Score Alter (Jahre)
1 (C)2006, Hermann Knoll, HTW Chur, FHO Quadratische Reste Definitionen: Quadratischer Rest Quadratwurzel Anwendungen.
MINDREADER Ein magisch - interaktives Erlebnis mit ENZO PAOLO
1 (C)2006, Hermann Knoll, HTW Chur, FHO Quadratische Reste Definitionen: Quadratischer Rest Quadratwurzel Anwendungen.
ENDLICHE KÖRPER RSA – VERFAHREN.
PHP: Operatoren und Kontrollstrukturen
Schutzvermerk nach DIN 34 beachten 20/05/14 Seite 1 Grundlagen XSoft Lösung :Logische Grundschaltung IEC-Grundlagen und logische Verknüpfungen.
1 Tagesüberblick 2 Lösung Hausaufgabe/Fragen Datei- ein- und ausgabe Schleifen Vergleiche Wahrheit.
1 Albert-Ludwigs-Universität Freiburg Rechnernetze und Telematik Prof. Dr. Christian Schindelhauer Informatik III Christian Schindelhauer Wintersemester.
Korrektheit von Programmen – Testen
Es war einmal ein Haus
Analyse der Laufzeit von Algorithmen
Mehrfachausführungen Schleifen in VB 2010 ee. Programmidee: Der Anwender gibt eine Zahl ein, und das Programm gibt die Mehrfachen dieser Zahl aus (das.
Berechenbarkeit Klaus Becker Berechenbarkeit.
1 10 pt 15 pt 20 pt 25 pt 5 pt 10 pt 15 pt 20 pt 25 pt 5 pt 10 pt 15 pt 20 pt 25 pt 5 pt 10 pt 15 pt 20 pt 25 pt 5 pt 10 pt 15 pt 20 pt 25 pt 5 pt Wie.
1 Medienpädagogischer Forschungsverbund Südwest KIM-Studie 2014 Landesanstalt für Kommunikation Baden-Württemberg (LFK) Landeszentrale für Medien und Kommunikation.
Korrektheit von Programmen – Testen
Wann ist eine Funktion (über den natürlichen Zahlen) berechenbar?
 Präsentation transkript:

Eigenschaften von Algorithmen Klaus Becker 2007

Algorithmen    Zielsetzung: klassische Algorithmen erkunden und dabei zentrale Eigenschaften von Algorithmen klären

Fallstudie: Ägyptische Multiplikation Teil 1 Fallstudie: Ägyptische Multiplikation

Ägyptische Multiplikation Algorithmen werden seit den Anfängen der Mathematik beim Rechnen benutzt. Im "Papyrus Rhind" wird beschrieben, wie in Ägypten Zahlen multipliziert wurden. Im Jahre 1858 kaufte der englische Archäologe A.H. Rhind in Luxor ein aus zwei Stücken bestehendes Papyrus. Erst einige Jahrzehnte später stellte sich heraus, dass das dritte, fehlende Mittelstück sich in einem New Yorker Museum befand. Zusammen hat das Dokument eine Länge von 5,25 m und eine Breite von 33 cm. Es wurde rund 1700 Jahre vor Christi Geburt geschrieben und enthält viele Mathematikaufgaben. Heute heißt dieses Schriftstück Papyrus Rhind. siehe: http://buergernetz.muenster.de/mauritz//matheserver/teller/aegypten/zahl2.html 13 ∙ 12    = 156

Ägyptische Multiplikation Verfahren: Man schreibt die beiden zu multiplizierenden Zahlen nebeneinander. Auf der linken Seite werden die Zahlen jeweils halbiert (Reste abgerundet) und die Ergebnisse untereinander geschrieben, bis man zur 1 gelangt. Auf der rechten Seite werden die Zahlen verdoppelt und untereinander geschrieben. Die rechts stehenden (verdoppelten) Zahlen werden gestrichen, wenn die links stehende Zahl gerade ist. Die Summe der nicht gestrichenen rechts stehenden Zahlen ergibt das gesuchte Produkt. 13 12 13 ∙ 12   6 24  3  48  1  96 156 = 156

Aufgabe Berechnen Sie mit dem beschriebenen Verfahren folgende Produkte: 16*7 = 15*14 = 9*120 = Überprüfen Sie auch, ob die ermittelten Ergebnisse stimmen.

Aufgabe Entwickeln Sie eine algorithmische Beschreibung des Verdopplungs- und Halbierungsverfahren in Struktogrammform. 13  12 6 24 3  48 1  96 156

Aufgabe Warum berechnet dieses merkwürdige Verdopplungs- und Halbierungsverfahren das Produkt von zwei vorgegebenen Zahlen? Versuchen Sie, sich dies klar zu machen. 13  12 6 24 3  48 1  96 156

Algorithmus "Ägyptische Multiplikation" Die ägyptische Multiplikation würde man heute als Algorithmus so beschreiben: Ein Algorithmus ist eine Verarbeitungsvorschrift, die so präzise formuliert ist, dass sie auch von einer Maschine abgearbeitet werden kann.

Ablaufprotokoll Mit Hilfe e. Ablaufprotokolls ("Trace-Tabelle") kann man sich einen ersten Überblick über die Arbeitsweise des Algorithmus verschaffen. Bedingung Anweisung x y p 13 12 p := 0 0 x > 0 (w) x ungerade (w) p := p + y 12 x := x div 2 6 y := y * 2 24 x > 0 (w) x ungerade (f) x := x div 2 3 y := y * 2 48 x > 0 (w) x ungerade (w) p := p + y 60 x := x div 2 1 y := y * 2 96 x > 0 (w) x ungerade (w) p := p + y 156 x := x div 2 0 y := y * 2 192 x > 0 (f) 13  12 6 24 3  48 1  96 156

Mathematischer Hintergrund 13 = 6*2+1 = (3*2+0)*2+1 = ((1*2+1)*2+0)*2+1 = (((0*2+1)*2+1)*2+0)*2+1 = 0*2*2*2*2 + 1*2*2*2 + 1*2*2 + 0*2 + 1 13 * 12 = (1*2^3 + 1*2^2 + 0*2^1 + 1*2^0) * 12 = 8*12 + 4*12 + 0 + 1*12 = 96 + 48 + 12 = 156 Bedingung Anweisung x y p 13 12 p := 0 0 x > 0 (w) x ungerade (w) p := p + y 12 x := x div 2 6 y := y * 2 24 x > 0 (w) x ungerade (f) x := x div 2 3 y := y * 2 48 x > 0 (w) x ungerade (w) p := p + y 60 x := x div 2 1 y := y * 2 96 x > 0 (w) x ungerade (w) p := p + y 156 x := x div 2 0 y := y * 2 192 x > 0 (f) 13  12 6 24 3  48 Der erste Faktor wird implizit in Binärdarstellung umgewandelt. Hierdurch wird die Multiplikation auf Verdoppeln und Halbieren reduziert. 1  96 156

Implementierung des Algorithmus Der Algorithmus "Ägyptische Multiplikation" soll zu Demonstrationszwecken implementiert werden.

Implementierung in PASCAL Wir implementieren den Algorithmus als Delphi-Konsolenprogramm: Schritte: Neuen Ordner anlegen <Datei><Neu..><Konsolen-Anwendung> Projekt speichern Programm eingeben ... var x, y, p: integer; begin // Eingabe write('x: '); readln(x); write('y: '); readln(y); // Verarbeitung p := 0; while x > 0 do if x mod 2 = 1 then p := p + y; x := x div 2; y := y * 2 end; // Ausgabe write('x * y: '); writeln(p); readln; end.

Implementierung in PYTHON Wir implementieren den Algorithmus als Python-Programm: Starten Sie die Python-Shell. Öffnen Sie ein neues Fenster zur Eingabe des Programms: <File> <New Window>. Geben Sie das Programm (hier als Funktionsdefinition) ein und speichern Sie es unter einem geeigneten Dateinamen ab (z. B. mult_aegypt.py). Rufen Sie vom Programmfenster aus die Python-Shell auf: <Run><Run Module>. Lassen Sie Python geeinete Funktionsaufrufe auswerten. >>> mult(13, 12) 156 def mult(x, y): p = 0 while x > 0: if x % 2 == 1: p = p + y x = x / 2 y = y * 2 return p Python-Shell mit Programmaufruf mult1.py

Aufgabe Implementieren Sie den Algorithmus in einer der beiden Sprachen. Ergänzen Sie auch Ausgaben, mit denen man die jeweiligen Zwischenwerte verfolgen kann. In Python geht das ganz einfach (s. u.). Notieren Sie die Testfälle, die Sie betrachtet haben. >>> mult(13, 12) 13 12 0 6 24 12 3 48 12 1 96 60 156 def mult(x, y): p = 0 while x > 0: print x, y, p if x % 2 == 1: p = p + y x = x / 2 y = y * 2 return p mult2.py

Aufgabe Entwickeln Sie ein analoges Verfahren / einen analogen Algorithmus zum Potenzieren. Tipp: "Der Exponent wird schrittweise halbiert (das Ergebnis wird abgerundet) und die Basis schrittweise quadriert. Man streicht die Zeilen mit geradem Exponenten. Das Produkt der nichtgestrichenen rechten Zahlen ist die gesuchte Potenz." (siehe Wikipedia, Stichwort "schnelles Potenzieren")

Korrektheit von Algorithmen Teil 2 Korrektheit von Algorithmen

Korrektheit Der Algorithmus "Ägyptische Multiplikation" scheint immer das gewünschte Multiplikationsergebnis zu liefern. Stimmt das auch? Was heißt hier immer? >>> mult(13, 12) 156 >>> mult(4, 6) 24 >>> mult(0, 5) >>> mult(5, 0) def mult(x, y): p = 0 while x > 0: if x % 2 == 1: p = p + y x = x / 2 y = y * 2 return p

Spezifikation Das gewünschte Verhalten des Algorithmus lässt sich wie folgt beschreiben: vorher: { x = a (nat. Zahl) und y = b (nat. Zahl) } Vorbedingung Algorithmus nachher: { p = a*b } Nachbedingung Eine Spezifikation ist eine vollständige und eindeutige Beschreibung des Ausgangszustands („Zustand vorher“) und des Zielzustands („Zustand nachher“) mit Hilfe einer Vor- und Nachbedingung.

Korrektheit eines Algorithmus Korrektheit bezieht sich immer auf vorher festgelegte Anforderungen. vorher: { x = a (nat. Zahl) und y = b (nat. Zahl) } Vorbedingung Algorithmus nachher: { p = a*b } Nachbedingung Eine Algorithmus heißt (total) korrekt bzgl. einer Spezifikation, wenn er jeden Ausgangszustand, der die Vorbedingung erfüllt, in einen Zielzustand überführt, der die Nachbedingung erfüllt (und dann anhält).

Informelle Korrektheitsüberlegungen 13 * 12 = (6*2+1) * 12 = 6*(2*12) + 12 = 6 * 24 + 12 = (3*2) * 24 + 12 = 3*(2*24) + 12 = 3 * 48 + 12 = (1*2+1) * 48 + 12 = 1*(2*48) + (48+12) = 1 * 96 + 60 = (0*2+1) * 96 + 60 = 0*(2*96) + (96+60) = 0 * 192 + 156 allg.: x * y + p = 13 * 12 Bedingung Anweisung x y p 13 12 p := 0 0 x > 0 (w) x ungerade (w) p := p + y 12 x := x div 2 6 y := y * 2 24 x > 0 (w) x ungerade (f) x := x div 2 3 y := y * 2 48 x > 0 (w) x ungerade (w) p := p + y 60 x := x div 2 1 y := y * 2 96 x > 0 (w) x ungerade (w) p := p + y 156 x := x div 2 0 y := y * 2 192 x > 0 (f) Schleifeninvariante Die gezeigten Berechnungsschritte spiegeln sich im Ablaufprotokoll wider. Es fällt auf, dass die Werte der Variablen x, y und p in einem ganz bestimmten Zusammenhang stehen.

Verifikation der Korrektheit Die Korrektheit des Algorithmus lässt sich auch formal zeigen: (siehe auch: http://www.hsg-kl.de/faecher/inf/algorithmus/korrekt/verifikation/index.php) Vor dem ersten Schleifendurchlauf gilt: x*y+p = a*b (klar, da p = 0) Wir zeigen, dass der Ausdruck x*y+p nach jedem Schleifendurchlauf seinen Wert nicht verändert. Die Werte der Variablen x, y und p nach einem Schleifendurchlauf nennen wir x', y' und p'. Es gilt: x ungerade: p' = p+y 2x'+1 = x bzw. x' = (x-1)/2 y' = 2y x'*y' + p' = (x-1)/2*(2y) + (p+y) = x*y - y + p + y = x*y + p x gerade: p' = p x' = x/2 y' = 2y x'*y' + p' = (x/2)*(2y) + p = x*y + p Nach dem letzten Schleifendurchlauf gilt dann: x*y+p = a*b Da zudem x=0 gilt, folgt: p = a*b

Aufgabe 13 * 12 = (6*2+1) * 12 = 6*(2*12) + 12 = 6 * 24 + 12 = (3*2) * 24 + 12 = 3*(2*24) + 12 = 3 * 48 + 12 = (1*2+1) * 48 + 12 = 1*(2*48) + (48+12) = 1 * 96 + 60 = (0*2+1) * 96 + 60 = 0*(2*96) + (96+60) = 0 * 192 + 156 allg.: x * y + p = 13 * 12 Testen Sie mit Hilfe geeigneter Ausgabeanweisungen die Schleifeninvariante. def mult(x, y): p = 0 while x > 0: print x*y+p if x % 2 == 1: p = p + y x = x / 2 y = y * 2 return p Schleifeninvariante mult3.py

Aufgabe Testen Sie das unten abgebildete Programm. Was klappt hier nicht? def mult(x, y): p = 0 while x >= 0: if x % 2 == 1: p = p + y x = x / 2 y = y * 2 print p return p mult4.py

Termination Achtung: Der Algorithmus berechnet zwar die beabsichtigten Ergebnisse, hält aber nicht, um sie zurückzugeben. >>> mult(7,8) 8 24 56 ... def mult(x, y): p = 0 while x >= 0: if x % 2 == 1: p = p + y x = x / 2 y = y * 2 print p return p Eine Algorithmus heißt terminierend, wenn er bei jedem möglichen Ausgangszustand in endlich vielen Verarbeitungsschritten zum Ende kommt (und Ausgabedaten als Ergebnisse liefert).

Terminationsnachweis Zum Nachweis der (totalen) Korrektheit eines Algorithmus gehört auch der Nachweis der Termination: Der Algorithmus terminiert bei natürlichen Zahlen sicher, da x als Eingabewert eine natürliche Zahl erhält und dieser Wert innerhalb der Schleife stets ganzzahlig halbiert wird. Mit jedem Schleifendurchlauf wird der Wert von x demnach kleiner. Nach endlich vielen Schritten muss also der Wert 0 erreicht werden.

Aufgabe Versuchen Sie, die Korrektheit mit e. geeigneten Invariante zu zeigen bzw. sich die Korrektheit anhand e. typischen Ablaufprotokolls klar zu machen. vorher: x = b (positive rationale Zahl) und y = n (nat. Zahl) nachher: p = bn

Fallstudie: Euklidischer Algorithmus Teil 3 Fallstudie: Euklidischer Algorithmus

Der größte gemeinsame Teiler Algorithmen werden seit den Anfängen der Mathematik beim Rechnen benutzt. Euklid beschreibt ein Verfahren, mit dem man den größten gemeinsamen Teiler von zwei natürlichen Zahlen bestimmen kann. "Wenn CD aber AB nicht misst, und man nimmt bei AB, CD abwechselnd immer das kleinere vom größeren weg, dann muss (schließlich) eine Zahl übrig bleiben, die die vorangehende misst." aus: Euklid, Die Elemente, Herausgegeben von Clemens Thaer, Wissenschaftliche Buchgesellschaft Darmstadt, VII Buch, §2 siehe auch: http://de.wikipedia.org/wiki/Euklidischer_Algorithmus ggt(44, 12) = ggt(32, 12) = ggt(20, 12) = ggt(8, 12) = ggt(8, 4) = ggt(4, 4) = ggt(4, 0) = 4

Wechselwegnahme-Algorithmus Das Wechselwegnahme-Verfahren lässt sich wie folgt algorithmisch beschreiben.

Aufgabe Überprüfen Sie, ob der gezeigte Algorithmus korrekt ist. - Fertigen Sie geeignete Ablaufprotokolle per Hand an. - Implementieren Sie den Algorithmus und testen Sie den Algorithmus. - Zeigen Sie die Korrektheit des Algorithmus. Benutzen Sie die folgende Schleifeninvariante: ggt(x, y) = ggt(a, b)

Aufgabe Der größte gemeinsame Teiler lässt sich eventuell aber auch mit anderen Algorithmen berechnen. Überprüfen Sie die folgenden Algorithmen:

Aufgabe Welche Unterschiede lassen sich im Verhalten der vorgeschlagenen Algorithmen beobachten?

Testen und Laufzeitmessungen Teil 4 Testen und Laufzeitmessungen

Testen Die Überprüfung eines Algorithmus bzw. Programms hinsichtlich Korrektheit kann auf zwei grundsätzlich verschiedene Weisen erfolgen: - Formale Methode (Verifikation): Mittels logischer Herleitungen wird die Einhaltung der Bedingungen an die Zwischen- und Endergebnisse des Algorithmus bzw. Programms nachgewiesen. - Empirische Methode (Testen): Mit bestimmten ausgesuchten Daten, für die das Ergebnis bekannt ist, wird der Algorithmus bzw. das Programm erprobt. Dabei kann lediglich das Vorhandensein von Fehlern entdeckt, nicht jedoch die Fehlerfreiheit nachgewiesen werden. "Der Methode der Programmverifikation stellen sich in der Praxis große Hindernisse entgegen. Bei umfangreichen Programmen ist ein formaler Korrektheitsbeweis der geschilderten Art i. d. R. nicht möglich. Unsere Beispiele betrafen vergleichsweise wenig umfangreiche mathematische Algorithmen, deren Spezifikation seit Jahrhunderten bekannt ist. In der Praxis besteht das eigentliche Problem jedoch im Entwickeln eines geeigneten Modells (...), und es ist zu prüfen, ob das Modell "auf die Wirklichkeit passt". Erst wenn dies gesichert ist, kann die Verifikation beginnen. In der Praxis ändern sich Spezifikationen laufend, das Modell wird abgewandelt. Daher ist man dort auf die - zugegebenermaßen unvollkommene - Methode des empirischen Testens angewiesen." (aus: R. Baumann: Informatik für die Sekundarstufe II, Band 1. S. 185 ff.)

Testdaten Testdaten müssen sorgfältig gewählt werden. Es reicht in der Regel nicht, nur einige wenige Testfälle durchzuführen. Testfälle: x = 44; y = 12  4 (ok) x = 13; y = 5  1 (ok) x = 24; y = 6  6 (ok) Die gewählten Testdaten decken hier nicht auf, dass der Algorithmus bei der Eingabe zweier gleicher Zahlen nicht terminiert.

Teststrategien Regeln zur Wahl von Testdaten: Man wähle sowohl typische als auch untypische Eingabewerte. Besondere Aufmerksamkeit ist auf Sonderfälle und Grenzwerte zu richten. Unter letzteren versteht man Werte, die gerade noch als Eingabewerte zugelassen sind (z. B. größter und kleinster Wert, leerer Text, leere Eingabe usw.). Man sollte auch zufällige Testdaten verwenden, selbst wenn diese nicht sinnvoll erscheinen mögen, denn mit ihnen lassen sich Testlücken aufgrund scheinbarer Selbstverständlichkeiten vermeiden. Regeln zur Testdurchführung: Man notiere vor der Durchführung des Testlaufs, welche Ausgabe das Programm laut Spezifikation liefern soll. Man prüfe nicht nur, ob das Programm tut, was es soll, sondern auch, ob es etwas tut, was es nicht soll (z. B.: hat eine Prozedur unerwünschte Nebenwirkungen?)

vorher festgelegte Testfälle Testen mir Python # ggt1Test1.py def ggt(x, y): while y > 0: if x > y: x = x - y else: y = y - x return x if __name__ == "__main__": print 44, 12, ggt(44, 12) print 7, 13, ggt(7, 13) print 4, 4, ggt(4, 4) print 1, 9, ggt(1,9) >>> 44 12 4 7 13 1 4 4 4 1 9 1 Testergebnisse vorher festgelegte Testfälle

Testen mir Python # ggt1Test2.py def ggt(x, y): """ groesster gemeinsamer Teiler >>> ggt(44, 12) 4 >>> ggt(7, 13) 1 >>> ggt(4, 4) """ while y > 0: if x > y: x = x - y else: y = y - x return x if __name__ == "__main__": import doctest doctest.testmod(verbose=True) >>> Trying: ggt(44, 12) Expecting: 4 ok ggt(7, 13) 1 ggt(4, 4) 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.ggt 3 tests in 2 items. 3 passed and 0 failed. Test passed. vorher festgelegte Testfälle mit den erwarteten Ergebnissen automatisch erzeugtes Testprotokoll

Systematisches Testen Anweisungsüberdeckung: Konstruiere die Testfälle so, dass jede Anweisung mindestens einmal ausgeführt wird. Zweigüberdeckung: Konstruiere die Testfälle so, dass jeder einzelne Zweig im Flussdiagramm mindestens einmal ausgeführt wird. Pfadüberdeckung: Konstruiere die Testfälle so, dass alle Pfade im Flussdiagramm mindestens einmal ausgeführt werden. Die Anzahl der Scheifendurchläufe wird dabei auf max. 2 begrenzt. Eingabe: x, y falsch y > 0? wahr wahr falsch x > y? x := x - y y := y - x Ausgabe: x

Systematisches Testen Testfälle für eine Pfadüberdeckung: kein Schleifendurchlauf: x = 3; y = 0  3 ein Schleifendurchlauf: x = 4; y = 4  4 zwei Schleifendurchläufe: x = 6; y = 3 (w, f)  3 x = 3; y = 6 (f, f)  3 Eingabe: x, y falsch y > 0? wahr wahr falsch x > y? x := x - y y := y - x Ausgabe: x

Aufgabe Überlegen Sie sich geeignete Testfälle, um den Algorithmus "GGT Version 4" zu testen. Führen Sie die Tests mit einer geeigneten Testumgebung durch. Protokollieren Sie (ggf. in der Testumgebung integriert) die Ergebnisse.

Aufgabe Überlegen Sie sich geeignete Testfälle, um den Algorithmus "Ägyptische Multiplikation" zu testen. Führen Sie die Tests mit einer geeigneten Testumgebung durch. Protokollieren Sie (ggf. in der Testumgebung integriert) die Ergebnisse. p := 0 falsch x > 0? wahr wahr falsch x ungerade? p := p + x x := x div 2 y := y * 2

Äquivalenz von Algorithmen Verschiedene Algorithmen, die das gleiches Ein-/Ausgabe-Verhalten zeigen, können sich deutlich im Laufzeitverhalten unterscheiden. Zwei Algorithmen heißen äquivalent, wenn sie bei gleichen Ausgangszuständen jeweils gleiche Endzustände erzeugen. Von zwei äquivalenten Algorithmen heißt der effizienter, der mit weniger Zeit (bzw. mit weniger Speicherplatz) auskommt.

Laufzeitmessung mit Python # ggt1Laufzeit.py def ggt(x, y): while y > 0: if x > y: x = x - y else: y = y - x return x if __name__ == "__main__": import profile profile.run("ggt(768964310867, 34256)") Performanzanalyse mit dem Profiler >>> 4 function calls in 11.452 CPU seconds ... Auszug aus dem Profiler-Protokoll

Unterschiedliche Laufzeiten # ggt1Laufzeit.py def ggt(x, y): while y > 0: if x > y: x = x - y else: y = y - x return x if __name__ == "__main__": import profile profile.run ("ggt(768964310867, 34256)") # ggt4Laufzeit.py def ggt(x, y): while y > 0: h = x % y x = y y = h return x if __name__ == "__main__": import profile profile.run ("ggt(768964310867, 34256)") >>> 4 function calls in 11.452 CPU seconds ... >>> 4 function calls in 0.001 CPU seconds ...

Schleifendurchläufe zählen # ggt1Schleifendurchlaeufe.py def ggt(x, y): z = 0 while y > 0: z = z + 1 if x > y: x = x - y else: y = y - x return x, z if __name__ == "__main__": zahl1 = 768964310867 zahl2 = 34256 (ergebnis, anzahl) = ggt(zahl1, zahl2) print "Zahl 1: ", zahl1, "Zahl 2: ", zahl2, "ggt: ", ergebnis, "Schleifendurchlaeufe: ", anzahl zusätzlicher Zähler >>> Zahl 1: 768964310867 Zahl 2: 34256 ggt: 1 Schleifendurchlaeufe: 22447622

Unterschiedlicher Berechnungsaufwand # ggt1Schleifendurchlaeufe.py def ggt(x, y): z = 0 while y > 0: z = z + 1 if x > y: x = x - y else: y = y - x return x, z if __name__ == "__main__": zahl1 = 768964310867 zahl2 = 34256 ... # ggt4Schleifendurchlaeufe.py def ggt(x, y): z = 0 while y > 0: z = z + 1 h = x % y x = y y = h return x, z if __name__ == "__main__": zahl1 = 768964310867 zahl2 = 34256 ... >>> Zahl 1: 768964310867 Zahl 2: 34256 ggt: 1 Schleifendurchlaeufe: 22447622 >>> Zahl 1: 768964310867 Zahl 2: 34256 ggt: 1 Schleifendurchlaeufe: 9

Aufgabe Untersuchen Sie auch das Verhalten des Algorithmus "ggt - Version 3". Vergleichen Sie diesen Algorithmus mit den beiden anderen ggt-Algorithmen.

Aufgabe Vergleichen Sie die beiden folgenden Algorithmen zum Potenzieren hinsichtlich Laufzeit und Berechnungsaufwand. def pot(x, n): p = 1 for i in range(0, n): p = p * x return p def pot(x, y): p = 1 while y > 0: if y % 2 == 1: p = p * x x = x * x y = y / 2 return p

Aufgabe Der folgende Algorithmus überprüft, ob eine natürliche Zahl eine Primzahl ist. Testen Sie diesen Algorithmus. Versuchen Sie auch, effizientere Algorithmen zu entwickeln. Interessant ist auch das Problem, eine Zahl in ihre Primfaktoren zu zerlegen. Wie könnte ein (effizientes) Verfahren aussehen? def primzahl(n): prim = True if n == 1: prim = False else: i = 2 while i <= n-1: if n % i == 0: i = i+1 return prim

Aufgabe Laufzeitmessungen sind auch mit Delphi möglich. Informieren Sie sich hierüber in den folgenden Materialien: G. Noll: Programmieren mit Delphi. Präsentation (Folie 44). http://informatik.bildung-rp.de/fileadmin/user_upload/informatik.bildung-rp.de/Fortbildung/pps/D-070516-Noll-ProgrammierenMitDelphi.pps