Semantyka i weryfikacja programów/Ćwiczenia 13: Różnice pomiędzy wersjami
Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Nie podano opisu zmian |
m Zastępowanie tekstu – „<math> ” na „<math>” |
||
(Nie pokazano 6 wersji utworzonych przez 3 użytkowników) | |||
Linia 1: | Linia 1: | ||
== Zawartość == | == Zawartość == | ||
Poprawność częściowa prostych programów w języku | Poprawność częściowa prostych programów w języku TINY. | ||
Linia 22: | Linia 21: | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><div class="mw-collapsible-content" style="display:none"> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> | |||
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span> | |||
<div class="mw-collapsible-content" style="display:none"> | |||
Najpierw musimy odgadnąć asercję, którą można wpisać pomiędzy początkowe przypisania a pętlę: | Najpierw musimy odgadnąć asercję, którą można wpisać pomiędzy początkowe przypisania a pętlę: | ||
Linia 41: | Linia 42: | ||
Możemy na przykład | Możemy na przykład "przeciągnąć" asercję początkową wprzód, otrzymując <math>{\color{Red}\{n \geq 0 \land x = 1 \land y = 0\}}</math>. | ||
Teraz wystarczy dowieść następujące dwie trójki Hoare'a: | Teraz wystarczy dowieść następujące dwie trójki Hoare'a: | ||
Linia 63: | Linia 64: | ||
Naszym zadaniem jest teraz odkrycie niezmiennika <math>N</math> takiego, że zachodzą poniższe dwie implikacje: | Naszym zadaniem jest teraz odkrycie niezmiennika <math>N</math> takiego, że zachodzą poniższe dwie implikacje: | ||
* <math> n \geq 0 \land x = 1 \land y = 0 \, \implies \, N</math> | * <math>n \geq 0 \land x = 1 \land y = 0 \, \implies \, N</math> | ||
* <math>N \land x > n \, \implies \, y=\frac{n{\cdot}(n+1)}{2}</math> | * <math>N \land x > n \, \implies \, y=\frac{n{\cdot}(n+1)}{2}</math> | ||
Linia 98: | Linia 99: | ||
a następnie niezależnie dowieść poprawność każdej z dwóch nstrukcji. | a następnie niezależnie dowieść poprawność każdej z dwóch nstrukcji. | ||
Ale w | Ale w "odgadnięciu" pomoże nam reguła dla przypisania, która, intuicyjnie mówiąc, daje sposób na "przeciągnięcie" asercji w tył. | ||
Jeśli zastosujemy ją do drugiego przypisania | Jeśli zastosujemy ją do drugiego przypisania "x := x + 1", otrzymamy: | ||
Linia 127: | Linia 128: | ||
Skoro dwie asercje | Skoro dwie asercje spotkały się, wystarczy teraz udowodnić implikację. | ||
I tak dalej. To znaczy, podobnie postępujemy z pozostałymi przypisaniami. A wszędzie, gdzie | I tak dalej. To znaczy, podobnie postępujemy z pozostałymi przypisaniami. A wszędzie, gdzie spotykają się dwie asercje, dowodzimy implikacji. | ||
Podsumowując, poniższe anotacje całego programu powinny stanowić dowód poprawności częściowej: | Podsumowując, poniższe anotacje całego programu powinny stanowić dowód poprawności częściowej: | ||
Linia 155: | Linia 156: | ||
A jedyne co pozostaje do zrobienia to tylko | A jedyne co pozostaje do zrobienia to tylko udowodnienie następujących implikacji: | ||
# <math>n \geq 0 \land x = 1 \land y = 0 \, \implies \, N</math> | # <math>n \geq 0 \land x = 1 \land y = 0 \, \implies \, N</math> | ||
Linia 163: | Linia 164: | ||
Co ciekawe, to wszystko uczyniliśmy nie wiedząc wogóle, jaki jest niezmiennik pętli! | Co ciekawe, to wszystko uczyniliśmy nie wiedząc wogóle, jaki jest niezmiennik pętli! | ||
Ale nadszedł wreszcie moment, w którym powinniśmy taki niezmiennik podać. | Ale nadszedł wreszcie moment, w którym powinniśmy taki niezmiennik podać. | ||
Musimy go | Musimy go odgadnąć poprzez staranną obserwację tego, co dzieje się wewnątrz pętli. | ||
Należy oczywiście uwzględnić również implikacje, które wypisaliśmy powyżej. | Należy oczywiście uwzględnić również implikacje, które wypisaliśmy powyżej. | ||
Linia 169: | Linia 170: | ||
Nierówność <math>x \leq n{+}1</math> jest niezbędna. Potrzebujemy jej po to, aby z <math>N \land x > n</math> wywnioskować <math>x = n{+}1</math>. | Nierówność <math>x \leq n{+}1</math> jest niezbędna. Potrzebujemy jej po to, aby z <math>N \land x > n</math> wywnioskować <math>x = n{+}1</math>. | ||
Implikacje są prawdziwe więc niezmiennik został wybrany poprawnie. | Implikacje są prawdziwe, więc niezmiennik został wybrany poprawnie. | ||
Zauważmy, że niezmiennik nie zawiera wymagania <math>x \geq 1</math>. Moglibyśmy je jednak dodać otrzymując <math>N' \, \equiv \, y = \frac{x{\cdot}(x{-}1)}{2} \land 1 \leq x \leq n{+}1</math>. | Zauważmy, że niezmiennik nie zawiera wymagania <math>x \geq 1</math>. Moglibyśmy je jednak dodać, otrzymując <math>N' \, \equiv \, y = \frac{x{\cdot}(x{-}1)}{2} \land 1 \leq x \leq n{+}1</math>. | ||
Wszystkie implikacje pozostają prawdziwe, więc jest to też poprawny wybór. Ponadto wiedząc, że <math>x \geq 1</math> możemy, dla czytelności, zapisywać niezmiennik w meta-notacji: | Wszystkie implikacje pozostają prawdziwe, więc jest to też poprawny wybór. Ponadto wiedząc, że <math>x \geq 1</math> możemy, dla czytelności, zapisywać niezmiennik w meta-notacji: | ||
<math> | <math> | ||
N \, \equiv \, y = 1{+}2{+}\ldots{+}(x{-}1) \land x \leq n{+}1 | N \, \equiv \, y = 1{+}2{+}\ldots{+}(x{-}1) \land x \leq n{+}1</math> | ||
</math> | |||
'''Pytanie:''' dlaczego nie potrzebowaliśmy warunku <math>x \geq 0</math>? Jak będzie działał nasz program, jeśli zmienną x zainicjujemy wartością ujemną (zamiast x := 1), np. x := -2, y := 3? | '''Pytanie:''' dlaczego nie potrzebowaliśmy warunku <math>x \geq 0</math>? Jak będzie działał nasz program, jeśli zmienną x zainicjujemy wartością ujemną (zamiast x := 1), np. x := -2, y := 3? | ||
Linia 218: | Linia 218: | ||
co można, za pomocą znanego nam już | co można, za pomocą znanego nam już "przepychania w tył", zredukować do udowodnienia implikacji: | ||
Linia 258: | Linia 258: | ||
</div></div> | </div></div> | ||
Linia 277: | Linia 276: | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><div class="mw-collapsible-content" style="display:none"> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> | |||
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span> | |||
<div class="mw-collapsible-content" style="display:none"> | |||
<math> | <math> | ||
Linia 296: | Linia 297: | ||
</div></div> | </div></div> | ||
Linia 317: | Linia 318: | ||
Wyrażenie odd(q) ma wartość <math>\mathbf{true}</math> wtedy i tylko wtedy, gdy q jest nieparzyste. | Wyrażenie odd(q) ma wartość <math>\mathbf{true}</math> wtedy i tylko wtedy, gdy q jest nieparzyste. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><div class="mw-collapsible-content" style="display:none"> | |||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> | |||
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span> | |||
<div class="mw-collapsible-content" style="display:none"> | |||
Jako niezmiennik pętli przyjmijmy <math>N \, \equiv \, z {\cdot} p^q = x^y</math>. Oto odpowiednie anotacje naszego programu: | Jako niezmiennik pętli przyjmijmy <math>N \, \equiv \, z {\cdot} p^q = x^y</math>. Oto odpowiednie anotacje naszego programu: | ||
Linia 398: | Linia 401: | ||
* <math>z {\cdot} p^q = x^y \land q=0 \, \implies \, z = x^y</math> | * <math>z {\cdot} p^q = x^y \land q=0 \, \implies \, z = x^y</math> | ||
Zauważmy, że w niezmienniku nie wymagamy aby <math>q \geq 0</math>, ponieważ nie jest to potrzebne do udowodnienia powyższych implikacji. | Zauważmy, że w niezmienniku nie wymagamy, aby <math>q \geq 0</math>, ponieważ nie jest to potrzebne do udowodnienia powyższych implikacji. | ||
Musimy tylko umówić się, na wszelki wypadek, jak działa operacja div, gdy jej argument jest ujemny. | Musimy tylko umówić się, na wszelki wypadek, jak działa operacja div, gdy jej argument jest ujemny. | ||
Przyjmijmy, że <math>q \,\mathrm{div}\, 2 = m</math> wtedy i tylko wtedy gdy największą liczbą parzystą nie większą od <math>q</math> jest | Przyjmijmy, że <math>q \,\mathrm{div}\, 2 = m</math> wtedy i tylko wtedy, gdy największą liczbą parzystą, nie większą od <math>q</math>, jest | ||
<math>2 {\cdot} m</math>. | <math>2 {\cdot} m</math>. | ||
{{uwaga||| | {{uwaga||| | ||
Warunek <math>q \geq 0</math> gwarantuje, że program się kończy. | Warunek <math>q \geq 0</math> gwarantuje, że program się kończy. | ||
Fakt, że go nie potrzebujemy wiąże się z tym, że dowodzimy tylko poprawność częściową: | Fakt, że go nie potrzebujemy, wiąże się z tym, że dowodzimy tylko poprawność częściową: | ||
wynik będzie poprawny, o ile program się zakończy, ale wcale zakończyć się nie musi! | wynik będzie poprawny, o ile program się zakończy, ale wcale zakończyć się nie musi! | ||
}} | }} | ||
'''Pytanie:''' czy niezmiennik pętli jest napewno prawdziwy gdy zmienna q ma wartość ujemną? | '''Pytanie:''' czy niezmiennik pętli jest napewno prawdziwy, gdy zmienna q ma wartość ujemną? | ||
</div></div> | </div></div> | ||
Linia 421: | Linia 423: | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><div class="mw-collapsible-content" style="display:none"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"> | ||
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie</span> | |||
<div class="mw-collapsible-content" style="display:none"> | |||
<math>\mathbf{repeat}\, I \,\mathbf{until}\, b</math> zachowuje się tak samo jak <math>I; \mathbf{while}\, \neg b \,\mathbf{do}\, I</math>. | <math>\mathbf{repeat}\, I \,\mathbf{until}\, b</math> zachowuje się tak samo jak <math>I; \mathbf{while}\, \neg b \,\mathbf{do}\, I</math>. | ||
Linia 435: | Linia 438: | ||
</math> | </math> | ||
Dwukrotnie odwołujemy się do poprawności częściowej instrukcji <math>I</math> | Dwukrotnie odwołujemy się do poprawności częściowej instrukcji <math>I</math> względem różnych asercji początkowych. | ||
Gdybyśmy chcieli tego uniknąć, moglibyśmy rozważyć <math>\alpha = \beta</math>, otrzymując regułę: | Gdybyśmy chcieli tego uniknąć, moglibyśmy rozważyć <math>\alpha = \beta</math>, otrzymując regułę: | ||
Linia 452: | Linia 455: | ||
</math> | </math> | ||
otrzymując regułę prostszą ale słabszą: we wnętrzu pętli nie możemy korzystać z tego, że począwszy od drugiej iteracji nie zachodzi <math>b</math>! | otrzymując regułę prostszą, ale słabszą: we wnętrzu pętli nie możemy korzystać z tego, że począwszy od drugiej iteracji nie zachodzi <math>b</math>! | ||
Jeszcze inna możliwość to: | Jeszcze inna możliwość to: | ||
Linia 466: | Linia 469: | ||
</div></div> | </div></div> | ||
== Zadania domowe == | == Zadania domowe == | ||
Linia 474: | Linia 475: | ||
{{cwiczenie|1|cw1.dom| | {{cwiczenie|1|cw1.dom| | ||
Wyprowadź ostatnią regułę dla pętli <math>\mathbf{repeat}\ | Wyprowadź ostatnią regułę dla pętli <math>\mathbf{repeat}\ </math>, z pierwszej reguły. | ||
}} | }} | ||
Linia 480: | Linia 481: | ||
{{cwiczenie|2|cw2.dom| | {{cwiczenie|2|cw2.dom| | ||
Napisz program obliczający | Napisz program obliczający największy wspólny dzielnik dwóch liczb i przeprowadź dowód poprawności częściowej. | ||
}} | }} | ||
Aktualna wersja na dzień 22:18, 11 wrz 2023
Zawartość
Poprawność częściowa prostych programów w języku TINY.
Poprawność częściowa
Ćwiczenie 1
Przeprowadź dowód poprawności częściowej poniższego programu:
x := 1; y := 0; while x <= n do y := y + x; x := x + 1
Rozwiązanie
Ćwiczenie 2
Uzupełnij poniższe reguły:
Rozwiązanie
Ćwiczenie 3 (potęgowanie binarne)
Dowiedź poprawności częściowej poniższego programu:
z := 1; p := x; q := y; while q <> 0 do if odd(q) then z := z * p; q := q div 2; p := p * p
Wyrażenie odd(q) ma wartość wtedy i tylko wtedy, gdy q jest nieparzyste.
Rozwiązanie
Ćwiczenie 4
Zaproponuj regułę dla instrukcji .
Rozwiązanie
Zadania domowe
Ćwiczenie 1
Wyprowadź ostatnią regułę dla pętli , z pierwszej reguły.
Ćwiczenie 2
Napisz program obliczający największy wspólny dzielnik dwóch liczb i przeprowadź dowód poprawności częściowej.
Ćwiczenie 3
Przeprowadź dowód poprawności częściowej:
a = 1; b := 1; y := 1; while y <= x do y := y + 3*a + 3*b + 1; a = a + 2*b + 1; b := b + 1;
Wskazówka