4 Spezifikation und Verifikation Program testing can at best show the presence of errors, but never their absence. —Dijkstra, http://www.cs.utexas.edu/users/EWD Beware of bugs in the above code; I have only proved it correct, not tried it. —Donald E. Knuth, http://www-cs-faculty.stanford.edu/∼knuth/faq.html Da Computerprogramme auch zur Steuerung kritischer Prozesse eingesetzt werden, ist die Korrektheit von Programmen nötig. Korrektheit von Programmen bezieht sich dabei auf: • syntaktische Korrektheit, • semantische Korrektheit, sofern das Programm als syntaktisch korrekt erkannt wurde. Die Korrektzeit eines Programms bezieht sich auf die Zusicherung von Programmeigenschaften. Ein erster Schritt zur Erstellung korekter Programme ist die Spezifikation der Programmeigenschaften und ggf. anschließende Programmverifikation. Bei der Spezifikation wird genau festgelegt, was das Programm leisten soll. Bei der Verifikation wird bewiesen, dass die Spezifikation korrekt ist. 4.1 Formale Spezifikation Formale Spezifikation ist die Voraussetzung für formale Verifikation. 2 Spezifizieren einer Funktion sqrt(x), die einen Wert w liefert, der die folgenden Eigenschaften für alle Eingaben x ≥ 0 hat: 1. |w2 − x| < , 2. w ≥ 0. x 4.1.1 Formale Spezifikation von Funktionen Mittels Aussagen- oder Prädikatenlogik werden Vor- und Nachbedingungen formuliert, die für f jeweils erfüllt sind. • Syntax: Signatur angeben: f: E →A 1 • Semantik: – Der Definitionsbereich Df ⊆ E wird durch ein charakterisierendes Prädikat P eingeschränkt: Df = {x | x ∈ E ∧ P (x)} P heißt Voraussetzung (precondition, Vorbedingung). – Das Ergebnis y = f (x) wird durch ein Prädikat Q(x, y) charakterisiert. Q heißt Effekt (postcondition, Konsequenz, Nachbedingung) 2 Fortsetzung von w = sqrt(x)): Signatur: sqrt : R → R Precondition: (bspw.) P (x) ≡ x ≥ 0 Postcondition: (bspw.) Q(x, y) ≡ |y 2 − x| < ∧ y ≥ 0 Formalsprachliche Formulierung beispielsweise mittels formaler Spezifikationssprache oder evtl. deklarativer Programmiersprache, z.B. mit Haskell: sqrt :: Float -> Float sqrtP x = x >= 0 sqrtQ x y = abs (y * y - x) < eps && y >= 0 x Für ein automatisiertes Beweisverfahren ist die formale Beschreibung der Effekte von elementaren Anweisungen nötig. 4.1.2 Formale Spezifikation von Anweisungen und Programmabschnitten Die Semantik einer Anweisung (bzw. eines Programmabschnitts) S wird durch einen Anfangszustand z angegeben, der vor Ausführung der Anweisung gültig ist, sowie einen Endzustand z 0 , der nach Ausführung gültig ist. z, z 0 ∈ Z mit Z als Menge aller Zustände. S: Z → Z • Als Precondition wird ein charakteristisches Prädikat P für den Anfangszustand angegeben: P (z) • Als Postcondition wird ein Prädikat Q für das Ergebnis des Zustandsübergangs von z nach z 0 angegeben: Q(z, z 0 ). 2 Die Anweisung S operiere auf einem Feld int[] a = new int[n], das mindestens eine positive Zahl b enthält. Nach Ausführung von S sollen alle Feldelemente gleich b sein. Beachte: Irgendein positives b genügt! • Der Zustandsraum A wird durch alle Kombinationen der möglichen Werte der Feldelemente a[i] (i = 0 . . . n − 1) bestimmt. 2 • Semantik: – Precondition: P (a) ≡ ∃n−1 i=0 a[i] > 0 – Postcondition: n−1 0 Q(a, a0 ) ≡ ∃n−1 i=0 (a[i] > 0 ∧ ∀k=0 a [k] = a[i]) x 4.1.3 Eigenschaften von Spezifikationen Es bezeichnet (P, Q) die Spezifikation einer Anweisung, eines Programmabschnitts oder eines Algorithmus S. P bestimmt die erlaubten Zustände vor Ausführung von S. Q bezeichnet die Effekte, die S nach sich zieht. • Z (Zustandsmenge) • (P, Q) (Voraussetzung P und Effekt Q) Man möchte später beweisen, dass der Algorithmus S korrekt arbeitet (die Spezifikation erfüllt). Man unterscheidet • Partielle Korrektheit: Wenn S terminiert, dann liefert er ein korrektes Ergebnis, d.h. hat den gewünschten Effekt. • Totale Korrektheit: Der Algorithmus S terminiert und liefert ein korrektes Ergebnis. Mögliche Sonderfälle: • (true, Q): S (terminiert und) bewirkt Q ohne Voraussetzung“. ” • (P, true): Unter der Voraussetzung P bewirkt S irgendetwas“. ” • (P, P ): P ist Invariante unter S, d.h. S ist ohne Wirkung bzgl. P . Auszuschließende Sonderfälle: • (false, Q): Voraussetzung ist nicht erfüllbar. • (P, false): Effekt ist nicht erfüllbar, S terminiert nicht. Hierzu zwei Definitionen: Definition. Eine Spezifikation (P, Q) heißt konsistent, wenn 1. ∃z ∈ Z : P (z), d.h. P ist erfüllbar, P 6≡ false, 2. ∀z ∈ Z : (P (z) ⇒ ∃z 0 ∈ Z : Q(z, z 0 )), d.h. wenn P erfüllt ist, ist Q erfüllbar. PQ (z) ≡ ∃z 0 ∈ Z : Q(z, z 0 ) heißt schwächste Voraussetzung (weakest precondition) für Q. 3 Definition. Eine Spezifikation (P, Q) heißt vollständig, wenn es zu jedem Zustand z, der die Voraussetzung P erfüllt, genau einen Effekt Q gibt, der aus der Ausführung von S resultiert: ∀z ∈ Z : P (z) ⇒ |{z 0 : z 0 ∈ Z, Q(z, z 0 )}| = 1. 2 Z = R, (P, Q) = (z > 0, |z − z 0 2 | < ) ist konsistent, denn • es existiert ein z ∈ R mit z > 0, √ • für jedes z > 0 erfüllt z 0 := z sogar |z − z 0 2 | = 0. P ist eine schwächste Voraussetzung PQ (z) ≡ ∃z 0 ∈ Z : |z − z 0 2 | < , allerdings ist z > 0 √ unnötig stark, da z ≥ 0 bereits genügt. (P, Q) ist nicht vollständig, denn ± z sind erlaubt, ferner alle Näherungswerte im Intervall (z 0 − , z 0 + ). x Definition. Eine Spezifikation (P1 , Q1 ) heißt stärker als (P2 , Q2 ), in Zeichen (P1 , Q1 ) ⇒ (P2 , Q2 ), wenn Q1 ⇒ Q2 ∧ P1 ⇐ P2 . Das bedeutet: Eine Anweisung, die der Spezifikation (P1 , Q1 ) genügt, genügt auch (P2 , Q2 ). Beachte: Q1 ⇒ Q2 muss für alle möglichen Zustände erfüllt sein, d.h. Q1 ⇒ Q2 := ∀z,z0 ∈Z Q1 (z, z 0 ) ⇒ Q2 (z, z 0 ). 4.2 Formale Verifikation Problemstellung: Gegeben ist eine Spezifikation (P, Q) und Implementierung S. Gesucht ist ein formalisierbarer (und ggf. automatisierbarer) Beweis, dass S die Spezifikation (P, Q) erfüllt. Idee: Die Wirkung eines Algorithmus, Programmabschnitts oder Programms S wird durch eine möglichst schwache Vorbedingung (weakest precondition), gegeben durch eine logische Formel Q, und eine möglichst starke Nachbedingung, gegeben durch eine logische Formel R, charakterisiert. Notiert in der Korrektheitsformel: {P }S{Q} lies: Wenn die Bedingung P für einen Anfangszustand z erfüllt ist, dann gilt nach Ausführung von S die Bedingung Q für den Endzustand z 0 , genauer: ∀z, z 0 ∈ Z : P (z) ⇒ wird S in Zustand z gestartet, gilt nach Beendigung von S Q(z, z 0 ). {P }S{Q} wird auch als Zusicherung (assertion) bezeichnet für S bezeichnet. Beachte: 1. Es gilt: {P }S{Q} ∧ ((P, Q) ⇒ (P 0 , Q0 )) ⇒ {P 0 }S{Q0 }. Anmerkung: Dies ist eine andere Notation der weiter unten eingeführten Konsequenzregel (CONS). 4 2. {P }S{Q} schließt nicht aus, dass sogar {P 0 }S{Q0 } mit (P, Q) ; (P 0 , Q0 ) gilt, d.h. (P 0 , Q0 ) ist stärker als oder nicht vergleichbar mit (P, Q). Wie bereits geschrieben, unterscheidet man partielle und totale Korrektheit. Hierzu zwei Definitionen: Definition. Eine Korrektheitsformel {P }S{Q} heißt partiell korrekt, wenn jede terminierende Berechnung von S, die mit der Voraussetzung P startet, im Effekt Q terminiert. Definition. Eine Korrektheitsformel {P }S{Q} heißt total korrekt, wenn jede Berechnung von S, die mit der Voraussetzung P startet, terminiert und in ihrem Endzustand Q erfüllt. Vorgehen bei der Verifikation: Jedem zusammengesetzten Programmteil wird eine Ableitungsregel zugeordnet, mit deren Hilfe sich aus den Vor- und Nachbedingungen seiner Komponenten die Vor- und Nachbedingung dieses Programmteils gewinnen lässt. Die Axiome und Ableitungsregeln für elementare und zusammengesetzte Anweisungen einer Sprache • bilden eine axiomatische Semantik der Sprache und • erlauben die formale Verifikation von Programmeigenschaften (Floyd 1967, Hoare 1969ff. Hoare-Kalkül“). ” I Literaturempfehlungen: — C.A.R. Hoare: An Axiomatic Basis for Computer Programming. Communications of the ACM 12/10 (1969) 576-588, 583. — K.R. Apt, E.-R. Olderog: Programmverifikation. Springer-Verlag, Berlin, 1994. — E. Fehr: Semantik von Programmiersprachen. Springer-Verlag, Berlin,1989. 4.2.1 Übersicht Gegeben sei eine Sprache mit folgender Grammatik: S ::= skip | u := t | S1; S2 | if B then S1 else S2 | while B do S. Informelle Beschreibung der Semantik der Sprachelemente: skip ist die leere Anweisung, u ist eine Variable, t steht für einen Ausdruck, B für einen Booleschen Ausdruck. In einer Zuweisung müssen u und t von demselben Typ sein. S1; S2 ist die Sequenz, d.h. S2 wird unmittelbar nach S1 ausgeführt. . . (u.s.w. in kanonischer Weise) Es wird noch folgende Vereinfachung der bedingten Anweisung definiert: if B then S1 ≡ if B then S1 else skip. 5 {P }skip{P } (SKIP) Zuweisungsaxiom: {P [u := t]} u := t {P } (ASS) Sequenzregel: {P }S1{R}, {R}S2{Q} {P }S1; S2{Q} (SEQ) Skip-Anweisung: Bedingte Anweisung: {P ∧ B}S1{Q}, {P ∧ ¬B}S2{Q} {P } if B then S1 else S2{Q} Schleife I: (Partielle Korrektheit) {I ∧ B}S{I} {I} while B do S {I ∧ ¬B} Schleife II: (Totale Korrektheit) {I ∧ B}S{I}, {I ∧ B ∧ t = z}S{t < z}, I ⇒ t ≥ 0 {I} while B do S {I ∧ ¬B} Konsequenzregel: P ⇒ P 1, {P 1}S{Q1}, Q1 ⇒ Q {P }S{Q} (IF) (WHILE) (WHILEtot ) (CONS) Wir nehmen im folgenden an, dass alle Programme syntaktisch korrekt sind. 4.2.2 Zuweisung Das Zuweisungsaxiom {P [u := t]} u := t {P } (ASS) gilt, sofern die Auswertung von t erfolgreich und ohne Nebenwirkungen ist. Man geht von der Nachbedingung P und bestimmt daraus durch Rückwärtssubstitution die Vorbedingung P [u := t]. Andere übliche Schreibweisen: {P [t/u]} u := t {P } und sogar {P [u/t]} u := t {P }. Vorgehen: Es wird die Nachbedingung P , gefolgt von der Zuweisungsoperation in eckigen Klammern, hingeschrieben und jedes Vorkommen von u durch den Ausdruck t ersetzt. 2 Behauptung: {true} x := 7 {x = 7} Beweis: Zu zeigen ist {true} x := 7 {x = 7}. Rückwärtssubstitution ergibt (x = 7)[x := 7] ≡ (7 = 7). Mit Wertzuweisungsaxiom (ASS) folgt daraus {7 = 7} x := 7 {x = 7}. Da zweifellos (true ⇒ (7 = 7)) gilt, folgt mit der Konsequenzregel (CONS) 6 (true ⇒ (7 = 7)), {7 = 7} x := 7 {x = 7}, ((x = 7) ⇒ (x = 7)) {true} x := 7 {x = 7} Gilt das Obere, kann man das Untere daraus schließen. x 2 Behauptung: {x > y} x := x − y {x ≥ 0} Beweis: Rückwärtssubstitution ergibt (x ≥ 0)[x := x − y] ≡ ((x − y) ≥ 0). Mit (ASS) folgt: {(x − y) ≥ 0} x := x − y {x ≥ 0}. Zweifellos gilt (x > y) ⇒ x − y ≥ 0. Mit (CONS) gilt deshalb auch die Behauptung: ((x > y) ⇒ ((x − y) ≥ 0)), {(x − y) ≥ 0} x := x − y {x ≥ 0} {x > y} x := x − y {x ≥ 0} Beweis (2. Möglichkeit, von hinten nach vorne“): ” Mit (CONS): ((x > y) ⇒ ((x − y) ≥ 0)), {(x − y) ≥ 0} x := x − y {x > 0}, ((x > 0) ⇒ (x ≥ 0)) {x > y} x := x − y {x ≥ 0} Mit (ASS): {(x − y) > 0} x := x − y {x > 0} Dies gilt, da (x > 0)[x := x − y] ≡ ((x − y) > 0). x 2 Behauptung: {x = X ∧ y 6= 0} x := x/y {x · y = X} für x, y ∈ R Beweis: Rückwärtsersetzung (x · y = X)[x := x/y] ≡ ((x/y) · y = X). Mit (ASS) und (CONS): ? {x = X ∧ y 6= 0} ⇒ {(x/y) · y = X} x := x/y {x · y = X} Frage: Gilt die Implikation x = X ∧ y 6= 0 ⇒ x = X? 1. Entweder: Herausfinden durch Überlegen und Argumentieren, 2. oder modifizierte Beweisführung: Geeignetes Verstärken der Nachbedingung von x := x/y, z.B. ((x · y = X) ∧ (y 6= 0)) ⇒ (x · y = X). 7 Wir nehmen 2.: {x = X ∧ y 6= 0} ⇒ {(x/y) · y = X ∧ y 6= 0} x := x/y {(x · y = X) ∧ (y 6= 0)} ⇒ {x · y = X} x 4.2.3 Sequenz Die Vorbedingung für S2 ist die Nachbedingung von S1: {P }S1{R}, {R}S2{Q} {P }S1; S2{Q} (SEQ) Kurzschreibweise: Man bildet für eine Korrektheitsformel {P }S{Q} eine Kette der Form {P }S1{P 1}S2 . . . Sk−1 {Pk−1 }Sk {Q}, wobei für jede Teilfolge der Form {Pv−1 }Sv {Pv } gilt: Entweder ist sie eine gültiges Axiom, eine Ableitungsregel oder eine prädikatenlogische Schlussfolgerung (⇒). 2 Behauptung: {x = y} x := x + 1; y := y + 1 {x = y} Beweis: {(x = y + 1)[x := x + 1]} {x + 1 = y + 1} {x = y} ⇒ {x = y} {(x = y)[y := y + 1]} {x = y + 1} x := x + 1; {x = y + 1} x := x + 1; {x = y + 1} {x + 1 = y + 1} {x + 1 = y + 1} x := x + 1; x := x + 1; {x + 1 = y + 1} x := x + 1; x := x + 1; {x = y + 1} x 2 Behauptung: {true} h := x; x := y; y := h; {x = Y ∧ y = X} Beweis: (von unten nach oben nachvollziehen!) {T } {y = Y ∧ x = X} h := x; {y = Y ∧ h = X} 8 y := y + 1; {x = y} y := y + 1; {x = y} (RWSubst) (ASS) (RWSubst) (ASS) y := y + 1; {x = y} y := y + 1; {x = y} (SEQ) y := y + 1; {x = y} y := y + 1; {x = y} (CONS) x := y; {x = Y ∧ h = X} y := h; {x = Y ∧ y = X} x 2 Behauptung: Für a, b ∈ N gilt: {b 6= 0} q := a / b; r := a % b; {a = qb + r ∧ r < b} Beweis: {b 6= 0} ⇒ true z }| { {b > 0 ∧ a = (a/b) · b + a%b} q := a / b; {b > 0 ∧ a = qb + a%b} true z }| { {b > 0 ∧ a = qb + a%b ∧ a%b < b} r := a % b; {a = qb + r ∧ r < b} x 4.2.4 Bedingte Anweisung Unabhängig von B muss nach der Ausführung Q gelten: {P ∧ B}S1{Q}, {P ∧ ¬B}S2{Q} {P } if B then S1 else S2{Q} (IF) Beweisplan: Zum Beweis von {P } if B then S1 else S2{Q} ist unabhängig zu zeigen: 1. {P ∧ B}S1{Q} und 2. {P ∧ ¬B}S2{Q} und schließlich 3. die Schlussregel (IF) anzuwenden. Beachte: Die Auswertung von B darf keinen Effekt (Seiten-/Nebeneffekt) bewirken! 2 Behauptung: {true} if (x > y) then max := x else max := y {max ≥ x ∧ max ≥ y} Beweis: Mit P ≡ true und B ≡ x > y 9 1. Zu zeigen: {P ∧ B} max := x {max ≥ x ∧ max ≥ y}. Rückwärtsersetzung und (ASS) ergibt: {x ≥ x ∧ x ≥ y} max := x {max ≥ x ∧ max ≥ y} Mit P ∧B ⇒ x≥x∧x≥y true ∧ x > y ⇒ x ≥ x ∧ x ≥ y und (CONS) folgt: {P ∧ B} ⇒ {x ≥ x ∧ x ≥ y} max := x {max ≥ x ∧ max ≥ y} 2. Gültigkeit von {P ∧ ¬B} max := y {max ≥ x ∧ max ≥ y} wird analog gezeigt. 3. Schließlich: Anwenden der Schlussregel (IF). Anmerkung: Wieso ist die Nachbedingung Q ≡ (max ≥ x) ∧ (max ≥ y) erfüllbar? Q ≡ (max ≥ x) ∧ (max ≥ y) ≡ ((max > x) ∨ (max = x)) ∧ ((max > y) ∨ (max = y))) x if (x > y) then (max = x) ⇒ else (max = y) ⇒ Q ≡ ((max > x)∨(max = x))∧((max > y)∨(max = y)) Q≡ (false ∨ true) ∧ (true ∨ false) Q≡ (true ∨ false) ∧ (false ∨ true) 2 Behauptung: {true} if (x < 0) then x := −x else skip {x ≥ 0} Beweis: (von innen nach außen nachvollziehen!) {true} if (x < 0) {true ∧ (x < 0)} ⇒ {−x ≥ 0} x := −x {x ≥ 0} else {true ∧ ¬(x < 0)} ≡ {x ≥ 0} skip {x ≥ 0} {x ≥ 0} x 4.2.5 Schleifen 4.2.5.1 Partielle Korrektheit Schlussregel zum Beweis der partiellen Korrektheit: {I ∧ B}S{I} {I} while B do S {I ∧ ¬B} 10 (WHILE) • I wird als Invariante bezeichnet, da sie sowohl vor als auch nach Schleifendurchlauf gültig ist, solange B gilt. • Mit Verlassen der Schleife gilt ¬B Beachte: Die Auswertung von B darf keinen Effekt (Seiten-/Nebeneffekt) bewirken! Allgemeiner Beweisplan für Algorithmen mit while-Schleifen: {P } S1 {I} while B do S {I ∧ ¬B} S2 {Q} Im Einzelnen: 1. Finden einer geeigneten Invariante I und Zeigen der Invarianz, d.h. oberer Teil der Schlussregel (WHILE): {I ∧ B}S{I} 2. Zeigen von {P }S1{I} (Initialisierung), 3. Zeigen von {I ∧ ¬B}S2{Q} (Finalisierung). 2 Gegeben: Algorithmus DIV zur Berechnung von Quotient und Rest zweier Zahlen x und y. Behauptung: {P } ≡ {x ≥ 0 ∧ y ≥ 0} q := 0; r := x; while (r ≥ y) do q := q + 1; r := r − y; {Q} ≡ {x = q · y + r ∧ 0 ≤ r < y} Beweis: Wählen einer geeigneten Schleifeninvariante I: Wir wählen ausgehend von Q unter Weglassen von r < y: I ≡ x = q · y + r ∧ r ≥ 0. Zu zeigen sind folgende Fakten: 1. Zu Beginn der Schleife gilt I, d.h. {P } q := 0; r := x {I}. 2. I ist tatsächlich die Schleifeninvariante, d.h. {I ∧ (r ≥ y)} q := q + 1; r := r − y; {I}. 11 3. Terminierung der Schleife impliziert Q, d.h. {I ∧ ¬(r ≥ y)} ⇒ {Q}. Zu 1.: Mit (ASS) folgt {x = q · y + x ∧ x ≥ 0} r := x; {I}. Mit (ASS) folgt {x = 0 · y + x ∧ x ≥ 0} q := 0; {x = q · y + x ∧ x ≥ 0}. Mit (SEQ) folgt {x = 0 · y + x ∧ x ≥ 0} q := 0; r := x; {I}. Es gilt offensichtlich {x ≥ 0 ∧ y ≥ 0} ⇒ {x = 0 · y + x ∧ x ≥ 0}. Mit (CONS) folgt somit tatsächlich {P } q := 0; r := x; {I}. Zu 2.: Mit (ASS): {x = q · y + (r − y) ∧ (r − y) ≥ 0} r := r − y; {I}. Mit (ASS): {x = (q+1)·y+(r−y)∧(r−y) ≥ 0} q := q+1; {x = q·y+(r−y)∧(r−y) ≥ 0}. Mit (SEQ): {x = (q + 1) · y + (r − y) ∧ (r − y) ≥ 0} q := q + 1; r := r − y; {I}. Es gilt offensichtlich {I ∧ (r ≥ y)} ⇒ {x = (q + 1) · y + (r − y) ∧ (r − y) ≥ 0}. Mit (CONS) folgt tatsächlich {I ∧ (r ≥ y)} q := q + 1; r := r − y; {I}. Zu 3.: Offensichtlich. x 4.2.5.2 Totale Korrektheit Schlussregel zum Beweis der totalen Korrektheit: {I ∧ B}S{I}, {I ∧ B ∧ t = z}S{t < z}, I ⇒ t ≥ 0 {I} while B do S {I ∧ ¬B} (WHILEtot ) • Hinzugekommene Bedingungen garantieren Terminierung der Schleife. • Ausdruck t heißt Terminierungsfunktion oder Schrankenfunktion. • Variable z wird benutzt, um Anfangswert von t festzuhalten. • z darf nicht in I, B, t oder S vorkommen! • Zweite Bedingung besagt, dass Wert von t mit jedem Schleifendurchlauf kleiner wird. • Dritte Bedingung besagt, dass Wert von t nach jedem Schleifendurchlauf nicht-negativ ist. 2 Gegeben ist der Algorithmus DIV aus dem vorigen Beispiel. Zu zeigen ist die totale Korrektheit von {x ≥ 0 ∧ y > 0} DIV {x = q · y + r ∧ 0 ≤ r < y}. Beweis: • Wählen der Schleifeninvariante I 0 ≡ I ∧ y > 0 ≡ x = q · y + r ∧ r ≥ 0 ∧ y > 0. 12 • Wählen der Terminierungsfunktion: t ≡ r Zu zeigen sind folgende Fakten: 1. {x ≥ 0 ∧ y > 0} q := 0; r := x {I 0 }. 2. {I 0 ∧ (r ≥ y)} q := q + 1; r := r − y; {I 0 }. 3. {I 0 ∧ (r ≥ y) ∧ (r = z)} q := q + 1; r := r − y; {r < z}. 4. I 0 ⇒ r ≥ 0. 5. {I 0 ∧ ¬(r ≥ y)} ⇒ {x = q · y + r ∧ 0 ≤ r < y}. (Fertigstellung als Übung!) x 4.2.6 Verifikation rekursiver Prozeduren Beweis erfolgt durch vollständige Induktion: • Entweder explizit: beweisen, daß 1. Prozedur total korrekt, falls kein Aufruf (Induktionsvoraussetzung) 2. Prozedur total korrekt, falls die rekursiven Aufrufe terminieren, korrektes Ergebnis liefern und Parameter sich wie die Funktion t (bei Schleifen) verhalten. • oder implizit: 1. partielle Korrektheit 2. Termination der Rekursion 2 Gegeben ist eine rekursive Prozedur zur Berechnung von f = n!. Das Ergebnis soll in int f; stehen. 1 2 3 4 5 6 7 8 9 10 11 void fact(int n) /* pre: n >= * post: f = */ if (n == 0) f = 1; else { fact(n f *= n; } } { 0 [else non-termination] n! 1); 1. Beweis durch vollständige Induktion 13 1. n = 0: fact(0) terminiert und bewirkt korrekt f = 1 = 0!. 2. fact(n) sei total korrekt für ein bestimmtes n ≥ 0. Bei Ausführung von fact(n+1) wird der else-Zweig durchlaufen. Dort terminiert nach Voraussetzung fact(n) mit f = n!. Nach f *= n + 1 gilt somit f = (n + 1)!. Also terminiert auch fact(n+1) mit dem richtigen Ergebnis, und damit ist fact(n) total korrekt (für alle n ≥ 0). 2. Beweis der partiellen Korrektheit mittels Hoare-Kalkül {n ≥ 0} if (n == 0) {n = 0} {1 = n!} f = 1; {f = n!} else { {n > 0} {n − 1 ≥ 0} fact(n - 1); {f = (n − 1)!} {n · f = n!} f = n * f; {f = n!} } {f = n!} x 14