Wstęp do programowania / Ćwiczenia 6: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Daria (dyskusja | edycje)
Cala flaga trojkolorowa
 
(Nie pokazano 22 wersji utworzonych przez 5 użytkowników)
Linia 1: Linia 1:
{{powrot|WDP_Dowodzenie_poprawności_programów|do modułu Dowodzenie poprawności programów}}
To są zadania na logikę Hoare'a.
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
Oglądaj wskazówki i rozwiązania __SHOWALL__<br>
Ukryj wskazówki i rozwiązania __HIDEALL__
</div>
== Zadanie 1 (Flaga trójkolorowa)==
== Zadanie 1 (Flaga trójkolorowa)==
Udowodnij częściową poprawność podanego poniżej rozwiązania zadania o fladze trójkolorowej.
Udowodnij częściową poprawność podanego poniżej rozwiązania zadania o fladze trójkolorowej.
   
   
'''Zadanie'''
{{cwiczenie||zadanie_1|
Dana jest tablica A typu array[1..N] of integer (N > 0). Należy tak poprzestawiać w niej elementy, żeby najpierw były elementy ujemne, potem równe zero, a na końcu dodatnie.
Dana jest tablica A typu array[1..N] of integer (N > 0). Należy tak poprzestawiać w niej elementy, żeby najpierw były elementy ujemne, potem równe zero, a na końcu dodatnie.}}


'''Program'''
'''Program'''
Linia 25: Linia 35:


<div class="mw-collapsible mw-made=collapsible mw-collapsed">
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
'''Rozwiązanie''' 
<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">  
<div class="mw-collapsible-content" style="display:none">  
Główną trudnością jest wymyślenie odpowiedniego niezmiennika pętli while. Tu, będzie to zdanie:
Główną trudnością jest wymyślenie odpowiedniego niezmiennika pętli while. Tu będzie to zdanie:
  &forall; i  (1 &le; i < m) &rArr;  A[i] < 0 &and;   
  &forall; i  (1 &le; i < m) &rArr;  A[i] < 0 &and;   
  &forall; j  (m &le; j < r) &rArr;  A[j] = 0 &and;   
  &forall; j  (m &le; j < r) &rArr;  A[j] = 0 &and;   
  &forall; k  (w < j &le; N) &rArr;  A[k] > 0 &and;   
  &forall; k  (w < k &le; N) &rArr;  A[k] > 0 &and;   
  1 &le; m &le; r &and;  r-1 &le; w &le; N &and; N > 0
  1 &le; m &le; r &and;  r-1 &le; w &le; N &and; N > 0
oznaczane przez N(m,r,w). Poniżej znajduje się program wraz z anotacjami i dowody, że anotacje wraz z kodem który otaczają są poprawnymi trójkami Hoare'a.
oznaczane przez N(m,r,w). Mówi ono, że na indeksach mniejszych od m są wartości ujemne, pomiędzy m a r - równe zero, a powyżej w - dodatnie. Nic nie jest powiedziane o wartościach tablicy pomiędzy r a w, za to wiemy jakie są relacje pomiędzy 1, m, r, w i N.
 
Poniżej znajduje się program wraz z anotacjami i dowody, że anotacje wraz z kodem który otaczają są poprawnymi trójkami Hoare'a.
   // N > 0  
   // N > 0  
  1:m:=1; r:=1; w:=N;
  1:m:=1; r:=1; w:=N;
Linia 71: Linia 83:
'''3''' Korzystamy z reguły na '''if''';  trzeba pokazać, że z N(m,r,w) &and; r &le; w po wykonaniu każdej gałęzi zachodzi N(m,r,w).
'''3''' Korzystamy z reguły na '''if''';  trzeba pokazać, że z N(m,r,w) &and; r &le; w po wykonaniu każdej gałęzi zachodzi N(m,r,w).


'''4''' Korzystając z reguły na przypisanie otrzymujemy, że N(m,r+1,w) powinno być prawdziwe przed instrukcją 4. A więc musimy dowieść implikacji   
'''4''' Korzystając z reguły na przypisanie, otrzymujemy, że N(m,r+1,w) powinno być prawdziwe przed instrukcją 4. Musimy więc dowieść implikacji   
  (N(m,r,w) &and; r &le; w &and; A[r] = 0)  &rArr; N(m,r+1,w)
  (N(m,r,w) &and; r &le; w &and; A[r] = 0)  &rArr; N(m,r+1,w)
Rozpatrując części N(m,r+1,w) widzimy, że  
Rozpatrując części N(m,r+1,w) widzimy, że  
Linia 103: Linia 115:
  &forall; k  (w < j &le; N) &rArr;  A[k] > 0 &and;   
  &forall; k  (w < j &le; N) &rArr;  A[k] > 0 &and;   
  1 &le; m+1 &le; r+1 &and; r &le; w &le; N &and; N > 0
  1 &le; m+1 &le; r+1 &and; r &le; w &le; N &and; N > 0
czyli coś co łatwo wynika z  N(m,r,w) &and; r &le; w &and; A[r] < 0.
czyli coś, co łatwo wynika z  N(m,r,w) &and; r &le; w &and; A[r] < 0.


'''8''' Jest to trywialne użycie reguły na przypisanie.
'''8''' Jest to trywialne użycie reguły na przypisanie.
Linia 113: Linia 125:
  &forall; i  (1 &le; i < m) &rArr;  A[i] < 0 &and;   
  &forall; i  (1 &le; i < m) &rArr;  A[i] < 0 &and;   
  &forall; j  (m &le; j < r) &rArr;  A[j] = 0 &and;   
  &forall; j  (m &le; j < r) &rArr;  A[j] = 0 &and;   
  &forall; k  (w < j &le; N) &rArr;  A[k] > 0 &and; A[r] > 0 &and;   
  &forall; k  (w < k &le; N) &rArr;  A[k] > 0 &and; A[r] > 0 &and;   
  1 &le; m &le; r &and;
  1 &le; m &le; r &and;
  r-1 &le; w-1 &le; N &and; N > 0
  r-1 &le; w-1 &le; N &and; N > 0
Linia 120: Linia 132:
'''11''' Jest to trywialne użycie reguły na przypisanie.
'''11''' Jest to trywialne użycie reguły na przypisanie.


'''12''' Z reguły na '''while''' wiemy, że zachodzi N(m,r,w) i zaprzeczenie dozoru w '''while''' czyli r > w. Z niezmiennika wiemy, że r-1 &le; w, a więc musi być r-1=w.
'''12''' Z reguły na '''while''' wiemy, że zachodzi N(m,r,w) i zaprzeczenie dozoru w '''while''' czyli r > w. Z niezmiennika wiemy, że r-1 &le; w, musi więc być r-1=w.
</div>
</div>
</div>
</div>
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
'''Całkowita poprawność'''   
'''Całkowita poprawność'''   
<div class="mw-collapsible-content" style="display:none">  
<div class="mw-collapsible-content" style="display:none">  
Do udowodnienia całkowitej poprawności brakuje tylko argumentu, że program zawsze się zakończy, czyli że pętla zawsze się zakończy. Można w tym celu użyć miary (w-r)+1, ponieważ maleje ona w każdym obrocie pętli i jest zawsze nieujemna.  
Do udowodnienia całkowitej poprawności brakuje tylko argumentu, że program zawsze się zakończy, czyli że pętla zawsze się zakończy. Można w tym celu użyć miary (w-r)+1, ponieważ maleje ona w każdym obrocie pętli i jest zawsze nieujemna.  
</div>
</div>
== Zadanie 2 (Segment o danej sumie)==
Udowodnij częściową poprawność dla podanego poniżej zadania i jego rozwiązania.
{{cwiczenie|||
Tablica A typu array[1..N] of integer, N >0  zawiera tylko liczby dodatnie. Napisz program, który dla danego W typu integer takiego, że W > 0 sprawdza, czy w A istnieje segment o sumie W (czyli czy istnieją l, p takie, że W<math>=A[l]+...+A[p-1]</math>).}}
'''Program'''
'''program''' SegmentODanejSumie2(N,W:integer; A:array[1..N] of integer);
//Tablica A zawiera tylko liczby dodatnie
'''var''' l,p,suma: integer;
    znalezione: boolean;
'''begin'''
  l:=1; p:=1;
  suma:=0;
  '''while''' (suma <> W) '''and''' (p <= N) '''do'''
    '''if''' suma < W '''then''' '''begin''' //jeśli suma jest za mała, to przesuwamy prawy koniec segmentu
      suma:=suma+A[p];
      p:=p+1;
    '''end'''
    '''else''' '''begin''' //jeśli za duża, to przesuwamy lewy koniec segmentu
      suma:= suma-A[l];
      l:=l+1;
    '''end''';
  '''while''' (suma > W) '''do''' '''begin''' 
    suma:= suma-A[l];
    l:=l+1;
  '''end''';
  znalezione:=(suma=W);
'''end'''.
<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">
Zacznijmy od niezmiennika pierwszej pętli
&forall; k  &forall; m (k < l &and; l &le; m &le; N+1) &rArr;  A[k]+...+A[m-1] <> W &and;
&forall; m (l &le; m < p) &rArr;  A[l]+...+A[m-1] < W &and; 
1 &le; l &le; p &le; N+1 &and; N,W > 0 &and; suma=A[l]+....A[p-1] &and;
A jest tablicą o wartościach dodatnich
oznaczanego przez N(l,p,suma). Mówi ono, że wszystkie segmenty o początku mniejszym od l i dowolnym końcu mają sumy różne od W, i że wszystkie segmenty o początku w l i końcu mniejszym od p mają sumy mniejsze niż W. Poza tym opisane są zależności między 1, l, p, N i suma.
Poniżej znajduje się program wraz z anotacjami i dowody, że anotacje wraz z kodem, który otaczają, są poprawnymi trójkami Hoare'a.
    // N,W > 0, A jest tablicą o wartościach dodatnich
1: l:=1; p:=1; suma:=0;
    // N(l,p,suma)
2: '''while''' (suma <> W) '''and''' (p <= N) '''do'''
      // N(l,p,suma) &and; (suma <> W) &and; (p &le; N)
3:  '''if''' suma < W '''then''' '''begin'''
        // N(l,p,suma) &and; (suma < W) &and; (p &le; N)
4:    suma:=suma+A[p];
        p:=p+1;
        // N(l,p,suma)
      '''end'''
5:  '''else''' '''begin'''
        // N(l,p,suma) &and; (suma > W) &and; (p &le; N)
6:    suma:= suma-A[l];
        l:=l+1;
        // N(l,p,suma)
7:  '''end''';
      // N(l,p,suma) &and; (suma=W &or; p = N+1)
8:  '''while''' (suma > W) '''do''' '''begin''' 
        // N(l,p,suma) &and; p = N+1 &and; suma > W
9:    suma:= suma-A[l];
        l:=l+1;
10:  '''end''';
    // N(l,p,suma) &and; (suma=W &or; (p=N+1 &and; suma < W))
11: znalezione:=(suma=W);
    //znalezione &hArr; (&exist; l &exist; p 1 &le; l &le; p &le; N+1 &and; A[l]+...A[p-1] = W) 
'''1''' Korzystamy z reguły na przypisanie. N(l,p,suma) z wartościami l=1, p=1, suma=0 jest równe
&forall; k  &forall; m (k < 1 &and; 1 &le; m &le; N+1) &rArr;  A[k]+...+A[m-1] <> W &and;
&forall; m (l &le; m < 1) &rArr;  A[l]+...+A[m-1] < W &and; 
1 &le; 1 &le; 1 &le; N+1 &and; N,W > 0 &and; 0=0 &and;
A jest tablicą o wartościach dodatnich
i jest trywialnie prawdziwe.
'''2''' Korzystamy z reguły na '''while'''; trzeba pokazać, że N(l,p,suma) jest niezmiennikiem ciała pętli, czyli że zaczynając z N(l,p,suma) &and; suma <> W &and; p &le; N  po wykonaniu pętli zachodzi N(l,p,suma).
'''3''' Korzystamy z reguły na '''if''';  trzeba zrobić osobne sprawdzenia dla każdej gałęzi.
'''4''' Zgodnie z regułą na przypisanie, musimy dowieść, że  N(l,p,suma) &and; (suma < W) &and; (p &le; N) wynika N(l,p+1,suma+A[p]),czyli
&forall; k  &forall; m (k < l &and; l &le; m &le; N+1) &rArr;  A[k]+...+A[m-1] <> W &and;
&forall; m (l &le; m < p+1) &rArr;  A[l]+...+A[m-1] < W &and; 
1 &le; l &le; p+1 &le; N+1 &and; N,W > 0 &and; suma+A[p]=A[l]+....A[p] &and;
A jest tablicą o wartościach dodatnich
Jedyna część, która nie jest oczywista, to że A[l]+...A[p-1] < W, ale to wynika z  (suma < W) i suma=A[l]+....A[p-1]
'''5''' W drugiej gałęzi wiemy dodatkowo, że suma > W
'''6''' Zgodnie z regułą na przypisanie, musimy dowieść, że  N(l,p,suma) &and; (suma > W) &and; (p &le; N) wynika N(l+1,p,suma-A[l]),czyli
&forall; k  &forall; m (k < l+1 &and; l+1 &le; m &le; N+1) &rArr;  A[k]+...+A[m-1] <> W &and;
&forall; m (l+1 &le; m < p) &rArr;  A[l+1]+...+A[m-1] < W &and; 
1 &le; l+1 &le; p &le; N+1 &and; N,W > 0 &and;
suma-A[l]=A[l+1]+....A[p-1] &and;
A jest tablicą o wartościach dodatnich
Spójrzmy najpierw  na pierwszą linię niezmiennika: dla k < l,  A[k]+...+A[m-1]  <> W z założenia; dla k=l mamy pokazać że dla dowolnych m A[l]+...+A[m-1]  <> W. Ale wiemy, że dla m < p+1 zachodzi A[l]+...A[m-1] < W i że  A[l]+...A[p-1] > W (bo suma > W). Tym bardziej więc dla m > p+1 będzie A[l]+...A[m-1] > W (bo A jest dodatnia).
Dla uzasadnienia drugiej linii wystarczy zauważyć, że gdy skracamy segment o wartości mniejszej niż W, to jego wartość spada, bo A jest tablica dodatnią.
Pozostaje uzasadnienie  l+1 &le; p. Ponieważ suma > W to l < p, a więc też l+1 &le; p.
'''7''' Po pętli '''while''' wiemy, że zachodzi N(l,p,suma) i zaprzeczenie dozoru, czyli (suma=W &or; p > N). Z niezmiennika wiemy, że p &le; N+1, a więc musi być p=N+1.
'''8''' Pokażemy, że N(l,p,suma) &and; (suma=W &or; p = N+1) jest niezmiennikiem tej pętli.
'''9''' Zauważmy najpierw, że  N(l,p,suma) &and; (suma=W &or; p = N+1) &and; suma > W jest równoważne N(l,p,suma) &and; p = N+1 &and; suma > W.
Zgodnie z regułą na przypisanie, musimy dowieść, że N(l,p,suma) &and; p = N+1 &and; suma > W implikuje N(l+1,p,suma-A[l])  &and; (suma-A[l]=W &or; p = N+1). Zdanie N(l+1,p,suma-A[l]) dowodzi się tak, jak w punkcie '''6'''. Ponieważ p =N+1, to zachodzi (suma-A[l]=W &or; p = N+1).
'''10''' Po wyjściu z pętli mamy N(l,p,suma) &and; (suma=W &or; p = N+1) i zaprzeczenie dozoru, czyli suma &le; W, a więc N(l,p,suma) &and; (suma=W &or; (p=N+1 &and; suma < W)).
'''11''' Pozostaje udowodnić, że N(l,p,suma) &and; (suma=W &or; (p=N+1 &and; suma < W)) implikuje  (suma=W) &hArr; (&exist; l &exist; p 1 &le; l &le; p &le; N+1 &and; A[l]+...A[p-1] = W) .
Jeśli suma=W to z N(l,p,suma), oczywiście wynika istnienie odpowiednich l i p. Jeśli suma <> W, to z założenia mamy p=N+1 i suma < W. I wtedy z niezmiennika wiemy, że
* segmenty o początkach mniejszych niż l mają sumy różne od W,
* segmenty o początkach w l i końcach mniejszych niż p mają sumy mniejsze niż W, 
* segment o początku w l i końcu w p ma sumę mniejszą W,
* a więc też segmenty o początkach wiekszych od l i dowolnych końcach mają sumy mniejsze niż W.
Czyli nie isnieje segment o sumie równej W.
</div>
</div>
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
'''Całkowita poprawność''' 
<div class="mw-collapsible-content" style="display:none">
Do udowodnienia całkowitej poprawności brakuje tylko argumentu, że program zawsze się zakończy, czyli że obie pętle zawsze się zakończą.
Dla pierwszej pętli można w tym celu użyć miary (2*N+1-p-l), ponieważ maleje ona w każdym obrocie pętli i jest zawsze nieujemna.
Dla drugiej pętli miarą jest suma+max(A[i] | i=1...N). Maleje ona w każdym obrocie pętli (bo tablica A jest dodatnia) i jest nieujemna.
</div>
</div>
== Zadanie 3 (Głosowanie większościowe)==
Udowodnij częściową poprawność dla podanego poniżej zadania i jego rozwiązania.
{{cwiczenie|||
Dana jest tablica A typu array[1..N] of integer, N > 0. Sprawdź, czy jest w niej element występujący więcej niż N/2 razy i jeśli tak, wskaż go.}}
'''Program'''
'''program''' Głosowanie2(N,W:integer; A:array[1..N] of integer);
'''var''' ile,i,kand,lider: integer;
'''begin'''
  kand:=A[1]; //szukamy kandydata na lidera
  ile := 1;
  i:=2;
  '''while''' i <= N '''do''' '''begin'''
    '''if''' A[i]=kand '''then''' '''begin'''
      ile:=ile+1;
      i:=i+1;
    '''end'''
    '''else'''
      '''if''' ile > 0 '''then''' '''begin'''
        ile:=ile-1;
        i:=i+1;
      '''end'''
      '''else''' '''begin'''
        kand:=A[i];
        ile:=1;
        i:=i+1;
      '''end''';
  '''end''';
  ile:=0; //sprawdzamy, czy kandydat jest liderem
  '''for''' i:=1 '''to''' n '''do'''
    '''if''' A[i]=kand '''then''' ile:=ile+1;
  '''if''' (ile >= (N div 2 + 1) '''then''' '''begin'''
    lider:=kand;
    write('Liderem jest: ', kand);
  '''end'''
  '''else'''
    lider:=0;
'''end'''.
<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">
Sprawdzenie, czy kandydat jest liderem, jest proste. Skoncentrujemy się więc na pierwszej części i wykażemy, że jeśli w tablicy jest lider to pierwsza część go znajduje.
Zacznijmy od niezmiennika pierwszej pętli oznaczanego przez N(i,ile,kand).
istnieje permutacja P:1..(i-1)->1..(i-1), taka że P(A[1..i-1])=kand<sup>ile</sup>@ogon i
ogon (rozpatrywany jako osobna tablica) nie ma lidera
N > 0 &and; 1 &le; i &le; N+1 &and; ile &ge; 0
Poniżej znajduje się program wraz z anotacjami i dowody, że anotacje wraz z kodem, który otaczają, są poprawnymi trójkami Hoare'a.
    // N > 0
1: kand:=A[1];
    ile := 1;
    i:=2;
    // N(i,ile,kand)
2: '''while''' i <= N '''do''' '''begin'''
      // N(i,ile,kand) &and; i &le; N
3:  '''if''' A[i]=kand '''then''' '''begin'''
        // N(i,ile,kand) &and; i &le; N &and; A[i]=kand
4:    ile:=ile+1;
        i:=i+1;
        // N(i,ile,kand)
      '''end'''
      '''else'''
5:    '''if''' ile > 0 '''then''' '''begin'''
        // N(i,ile,kand) &and; i &le; N &and; A[i] <> kand &and; ile > 0 
6:      ile:=ile-1;
        i:=i+1;
        // N(i,ile,kand)
      '''end'''
      '''else''' '''begin'''
        // N(i,ile,kand) &and; i &le; N &and; A[i] <> kand &and; ile &le; 0 
7:      kand:=A[i];
        ile:=1;
        i:=i+1;
        // N(i,ile,kand)
      '''end''';
8: '''end''';
    //Jeśli w tablicy istnieje lider to jest nim kand
'''1''' Korzystając z reguły na przypisanie mamy pokazać, że N(1,1,A[1]). Szukana permutacja jest identycznościowa, reszta jest pusta, i oraz ile są w wymaganych zakresach.
'''2''' Będziemy pokazywać, że N(i,ile,kand) jest niezmiennikiem pętli.
'''3''' Trzeba osobno rozważyć gałęzie '''if'''.
'''4''' Korzystając z reguły na przypisanie, mamy do pokazania, że N(i,ile,kand) &and; i &le; N &and; A[i]=kand implikuje N(i+1,ile+1,kand). Jeśli P to permutacja z N(i,ile,kand), to
szukana permutacja to P', gdzie P'(ile+1)=A[i], P'(i)=P(ile+1), P'(x)=P(x) dla x<>i lub x<>ile+1. Ogon pozostaje niezmieniony, czyli nadal nie ma tam lidera. Ponieważ i &le; N to i+1 &le; N+1; oczywiście ile > 0.
'''5''' Kolejny '''if'''; dowód znów się rozgałęzia.
'''6''' Korzystając z reguły na przypisanie, mamy do pokazania, że N(i,ile,kand) &and; i &le; N &and; A[i]<> kand i &and; ile > 0 implikuje N(i+1,ile-1,kand). Jeśli P to permutacja z N(i,ile,kand), to
szukana permutacja to P rozszerzona o identyczność na i. W nowym ogonie mamy dodatkowo A[i] i kand. Z tego wynika, że gdyby A[i] był liderem w nowym ogonie, to A[i] byłby liderem w starym ogonie, co jest niemożliwe. Oczywiście i+1 &le; N+1; oraz ile > 0.
'''7''' Wpierw zauważmy, że ile &le; 0 oraz ile &ge; 0 implikują ile=0. Korzystając z reguły na przypisanie, mamy do pokazania, że
N(i,ile,kand) &and; i &le; N &and; A[i] <> kand &and; ile =0
implikuje N(i+1,1,A[i]). Ponieważ ile=0, to w tablicy A[1..i-1] nie ma lidera. Szukaną permutacją jest więc na przykład A[i]@A[1..i-1].
Oczywiście i+1 &le; N+1; oraz ile > 0.
'''8''' Po wyjściu z pętli mamy N(i,ile,kand) i zaprzeczenie dozoru, czyli i > N. To oznacza, że i=N+1. Czy możliwe jest, by liderem była
wartość w <> kand? Jeśli tak przypuścimy, to niezależnie od ile, w musiałaby być liderem w ogonie, co jest sprzeczne z niezmiennikiem. Jedynym kandydatem na lidera jest więc rzeczywiście kand.
</div>
</div>
== Zadanie 4 (BinPower)==
Udowodnij częściową poprawność dla podanego poniżej zadania i jego rozwiązania.
{{cwiczenie|||
Dla zadanych x,n > 0 wyznacz x<sup>n</sup> (oczywiscie bez exp i ln).}}
'''Program'''
'''function''' BinPower(x,n:integer):integer;
// Dla x,n > 0 wyznaczamy x do potęgi n
'''var''' z,y,i: integer;
'''begin'''
  z:=1;
  y:=x;
  i:=n;
  '''while''' i > 0 '''do''' '''begin'''
    '''if''' (i mod 2 = 1) '''then''' z:=z*y;
    y:=y*y;
    i:=i div 2;
  '''end''';
  BinPower:=z;
'''end''';
<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">
Zacznijmy od niezmiennika pętli oznaczanego przez N(z,y,i):
x<sup>n</sup>=z*y<sup>i</sup> &and; i&ge; 0
Poniżej znajduje się program wraz z anotacjami i dowody, że anotacje wraz z kodem, który otaczają, są poprawnymi trójkami Hoare'a.
    // x,n > 0
1: z:=1; y:=x; i:=n;
    // N(z,y,i)
2: '''while''' i > 0 '''do''' '''begin'''
      // N(z,y,i) &and; i > 0
3:  '''if''' (i mod 2 = 1) '''then''' z:=z*y;
      // N(z,y*y,i div 2)
4:  y:=y*y;
      i:=i div 2;
      // N(z,y,i)
5: '''end''';
    // z= x<sup>n</sup>
'''1''' Korzystając z reguły na przypisanie, mamy pokazać, że N(1,x,n). Ponieważ n>0 to n&ge; 0
'''2''' Będziemy pokazywać, że N(z,y,i) jest niezmiennikiem pętli.
'''3''' Choć mamy instrukcję '''if''' bez '''else''', musimy pokazać dwie implikacje
N(z,y,i) &and; i>0 &and; i mod 2 = 1 &rArr; N(z*y, y*y, i div 2)
N(z,y,i) &and; i>0 &and; i mod 2 = 0 &rArr; N(z, y*y, i div 2)
Dla pierwszej z nich zauważmy, że jeśli i mod 2 = 1 to i=2*(i div 2) +1. Wtedy  z*y<sup>i</sup>= (z*y) * (y*y)<sup>(i div 2)</sup>. Oczywiście jeśli i > 0 to i div 2 &ge; 0.
Dla drugiej mamy  i=2*(i div 2). Wtedy  z*y<sup>i</sup>= z * (y*y)<sup>(i div 2)</sup>. Podobnie, jeśli i > 0 to i div 2 &ge; 0.
'''4''' Jest to dwa razy użyta reguła na przypisanie.
'''5''' Po wyjściu z pętli mamy  N(z,y,i) &and; i &le; 0. Ponieważ i &ge; 0  z niezmiennika, to i=0. A więc  x<sup>n</sup>=z.
</div>
</div>
</div>
</div>

Aktualna wersja na dzień 16:00, 28 maj 2020

<<< Powrót do modułu Dowodzenie poprawności programów

To są zadania na logikę Hoare'a.

Oglądaj wskazówki i rozwiązania __SHOWALL__
Ukryj wskazówki i rozwiązania __HIDEALL__


Zadanie 1 (Flaga trójkolorowa)

Udowodnij częściową poprawność podanego poniżej rozwiązania zadania o fladze trójkolorowej.

Ćwiczenie

Dana jest tablica A typu array[1..N] of integer (N > 0). Należy tak poprzestawiać w niej elementy, żeby najpierw były elementy ujemne, potem równe zero, a na końcu dodatnie.

Program

program FlagaTrójkolorowa(N:integer; A:array[1..N] of integer);
var m,r,w,pom : integer;
begin
  m:=1; r:=1; w:=N;
  while r <= w do 
    if A[r]=0 then r:=r+1			//przedłużamy segment liczb dodatnich	
    else 
      if A[r]<0 then begin			
        pom:=A[r]; A[r]:=A[m]; A[m]:=pom;	//zamieniamy wartości w A[r] i A[m], po zamianie A[r]=0 i A[m] < 0  
        m:=m+1;					//więc zwiększamy oba indeksy r i m
        r:=r+1;
      end
      else begin
        pom:=A[r]; A[r]:=A[w]; A[w]:=pom;	//zamieniamy wartości w A[r] i A[w]
        w:=w-1;					//po zamianie A[w]>0,  ale o A[r] nic nie wiemy
      end;			     
end.

Rozwiązanie

Całkowita poprawność

Zadanie 2 (Segment o danej sumie)

Udowodnij częściową poprawność dla podanego poniżej zadania i jego rozwiązania.

Ćwiczenie

Tablica A typu array[1..N] of integer, N >0 zawiera tylko liczby dodatnie. Napisz program, który dla danego W typu integer takiego, że W > 0 sprawdza, czy w A istnieje segment o sumie W (czyli czy istnieją l, p takie, że W=A[l]+...+A[p1]).

Program

program SegmentODanejSumie2(N,W:integer; A:array[1..N] of integer);
//Tablica A zawiera tylko liczby dodatnie
var l,p,suma: integer;
    znalezione: boolean;
begin
  l:=1; p:=1;
  suma:=0;
  while (suma <> W) and (p <= N) do
    if suma < W then begin		//jeśli suma jest za mała, to przesuwamy prawy koniec segmentu
      suma:=suma+A[p];
      p:=p+1;
    end
    else begin			//jeśli za duża, to przesuwamy lewy koniec segmentu
      suma:= suma-A[l];
      l:=l+1;
    end;
  while (suma > W) do begin  
    suma:= suma-A[l];
    l:=l+1;
  end;
  znalezione:=(suma=W);
end.

Rozwiązanie

Całkowita poprawność

Zadanie 3 (Głosowanie większościowe)

Udowodnij częściową poprawność dla podanego poniżej zadania i jego rozwiązania.

Ćwiczenie

Dana jest tablica A typu array[1..N] of integer, N > 0. Sprawdź, czy jest w niej element występujący więcej niż N/2 razy i jeśli tak, wskaż go.

Program

program Głosowanie2(N,W:integer; A:array[1..N] of integer);
var ile,i,kand,lider: integer;
begin
  kand:=A[1];		//szukamy kandydata na lidera
  ile := 1;
  i:=2;
  while i <= N do begin	
    if A[i]=kand then begin
      ile:=ile+1;
      i:=i+1;
    end
    else
      if ile > 0 then begin
        ile:=ile-1;
        i:=i+1;
      end
      else begin
        kand:=A[i];
        ile:=1;
        i:=i+1;
      end;
  end;
  ile:=0;		//sprawdzamy, czy kandydat jest liderem
  for i:=1 to n do 
    if A[i]=kand then ile:=ile+1; 
  if (ile >= (N div 2 + 1) then begin 
    lider:=kand;
    write('Liderem jest: ', kand);
  end 
  else 
    lider:=0;
end.

Rozwiązanie

Zadanie 4 (BinPower)

Udowodnij częściową poprawność dla podanego poniżej zadania i jego rozwiązania.

Ćwiczenie

Dla zadanych x,n > 0 wyznacz xn (oczywiscie bez exp i ln).

Program

function BinPower(x,n:integer):integer;
// Dla x,n > 0 wyznaczamy x do potęgi n 
var z,y,i: integer;
begin
  z:=1;
  y:=x;
  i:=n;
  while i > 0 do begin
    if (i mod 2 = 1) then z:=z*y;
    y:=y*y;
    i:=i div 2;
  end;
  BinPower:=z;
end;

Rozwiązanie