Wstęp do programowania / Ćwiczenia 6: Różnice pomiędzy wersjami
Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Nie podano opisu zmian |
|||
(Nie pokazano 10 wersji utworzonych przez 4 użytkowników) | |||
Linia 1: | Linia 1: | ||
{{powrot|WDP_Dowodzenie_poprawności_programów|do modułu Dowodzenie poprawności programów}} | {{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)== | ||
Linia 26: | Linia 34: | ||
'''end'''. | '''end'''. | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> | |||
Główną trudnością jest wymyślenie odpowiedniego niezmiennika pętli while. Tu | <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"> | |||
Główną trudnością jest wymyślenie odpowiedniego niezmiennika pętli while. Tu będzie to zdanie: | |||
∀ i (1 ≤ i < m) ⇒ A[i] < 0 ∧ | ∀ i (1 ≤ i < m) ⇒ A[i] < 0 ∧ | ||
∀ j (m ≤ j < r) ⇒ A[j] = 0 ∧ | ∀ j (m ≤ j < r) ⇒ A[j] = 0 ∧ | ||
∀ k (w < | ∀ k (w < k ≤ N) ⇒ A[k] > 0 ∧ | ||
1 ≤ m ≤ r ∧ r-1 ≤ w ≤ N ∧ N > 0 | 1 ≤ m ≤ r ∧ r-1 ≤ w ≤ N ∧ N > 0 | ||
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. | 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. | ||
Linia 115: | Linia 125: | ||
∀ i (1 ≤ i < m) ⇒ A[i] < 0 ∧ | ∀ i (1 ≤ i < m) ⇒ A[i] < 0 ∧ | ||
∀ j (m ≤ j < r) ⇒ A[j] = 0 ∧ | ∀ j (m ≤ j < r) ⇒ A[j] = 0 ∧ | ||
∀ k (w < | ∀ k (w < k ≤ N) ⇒ A[k] > 0 ∧ A[r] > 0 ∧ | ||
1 ≤ m ≤ r ∧ | 1 ≤ m ≤ r ∧ | ||
r-1 ≤ w-1 ≤ N ∧ N > 0 | r-1 ≤ w-1 ≤ N ∧ N > 0 | ||
Linia 124: | Linia 134: | ||
'''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 ≤ w, musi więc 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 ≤ 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"> | ||
Linia 137: | Linia 147: | ||
{{cwiczenie||| | {{cwiczenie||| | ||
Tablica A typu array[1..N] of integer, N >0 | 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''' | ||
Linia 154: | Linia 164: | ||
'''else''' '''begin''' //jeśli za duża, to przesuwamy lewy koniec segmentu | '''else''' '''begin''' //jeśli za duża, to przesuwamy lewy koniec segmentu | ||
suma:= suma-A[l]; | suma:= suma-A[l]; | ||
l:=l | l:=l+1; | ||
'''end'''; | '''end'''; | ||
'''while''' (suma > W) '''do''' '''begin''' | '''while''' (suma > W) '''do''' '''begin''' | ||
suma:= suma-A[l]; | suma:= suma-A[l]; | ||
l:=l | l:=l+1; | ||
'''end'''; | '''end'''; | ||
znalezione:=(suma=W); | znalezione:=(suma=W); | ||
'''end'''. | '''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 | Zacznijmy od niezmiennika pierwszej pętli | ||
∀ k ∀ m (k < l ∧ l ≤ m ≤ N+1) ⇒ A[k]+...+A[m-1] <> W ∧ | ∀ k ∀ m (k < l ∧ l ≤ m ≤ N+1) ⇒ A[k]+...+A[m-1] <> W ∧ | ||
Linia 215: | Linia 226: | ||
1 ≤ l ≤ p+1 ≤ N+1 ∧ N,W > 0 ∧ suma+A[p]=A[l]+....A[p] ∧ | 1 ≤ l ≤ p+1 ≤ N+1 ∧ N,W > 0 ∧ suma+A[p]=A[l]+....A[p] ∧ | ||
A jest tablicą o wartościach dodatnich | A jest tablicą o wartościach dodatnich | ||
Jedyna część, która nie jest oczywista to | 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 | '''5''' W drugiej gałęzi wiemy dodatkowo, że suma > W | ||
Linia 229: | Linia 240: | ||
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ą. | 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 ≤ p. Ponieważ suma > W to l < p, a więc | Pozostaje uzasadnienie l+1 ≤ p. Ponieważ suma > W to l < p, a więc też l+1 ≤ p. | ||
'''7''' Po pętli '''while''' wiemy, że zachodzi N(l,p,suma) i zaprzeczenie dozoru, czyli (suma=W ∨ p > N). Z niezmiennika wiemy, że p ≤ N+1, a więc musi być p=N+1. | '''7''' Po pętli '''while''' wiemy, że zachodzi N(l,p,suma) i zaprzeczenie dozoru, czyli (suma=W ∨ p > N). Z niezmiennika wiemy, że p ≤ N+1, a więc musi być p=N+1. | ||
Linia 250: | Linia 261: | ||
Czyli nie isnieje segment o sumie równej W. | Czyli nie isnieje segment o sumie równej W. | ||
</div> | </div> | ||
</div> | </div> | ||
<div class="mw-collapsible mw-made=collapsible mw-collapsed"> | <div class="mw-collapsible mw-made=collapsible mw-collapsed"> | ||
Linia 266: | Linia 277: | ||
{{cwiczenie||| | {{cwiczenie||| | ||
Dana jest tablica A typu array[1..N] of integer, N > 0. Sprawdź, czy jest w niej element | 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''' | ||
Linia 292: | Linia 303: | ||
'''end'''; | '''end'''; | ||
ile:=0; //sprawdzamy, czy kandydat jest liderem | ile:=0; //sprawdzamy, czy kandydat jest liderem | ||
'''for''' i:=1 '''to''' '''do''' | '''for''' i:=1 '''to''' n '''do''' | ||
'''if''' A[i]=kand '''then''' ile:=ile+1; | '''if''' A[i]=kand '''then''' ile:=ile+1; | ||
'''if''' (ile >= (N div 2 + 1) '''then''' '''begin''' | '''if''' (ile >= (N div 2 + 1) '''then''' '''begin''' | ||
Linia 302: | Linia 313: | ||
'''end'''. | '''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. | 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. | ||
Linia 346: | Linia 359: | ||
'''2''' Będziemy pokazywać, że N(i,ile,kand) jest niezmiennikiem pętli. | '''2''' Będziemy pokazywać, że N(i,ile,kand) jest niezmiennikiem pętli. | ||
'''3''' Trzeba osobno | '''3''' Trzeba osobno rozważyć gałęzie '''if'''. | ||
'''4''' Korzystając z reguły na przypisanie, mamy do pokazania, że N(i,ile,kand) ∧ i ≤ N ∧ A[i]=kand implikuje N(i+1,ile+1,kand). Jeśli P to permutacja z N(i,ile,kand), to | '''4''' Korzystając z reguły na przypisanie, mamy do pokazania, że N(i,ile,kand) ∧ i ≤ N ∧ A[i]=kand implikuje N(i+1,ile+1,kand). Jeśli P to permutacja z N(i,ile,kand), to | ||
Linia 353: | Linia 366: | ||
'''5''' Kolejny '''if'''; dowód znów się rozgałęzia. | '''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) ∧ i ≤ N ∧ A[i]<> kand i ∧ ile > 0 implikuje N(i+1,ile-1,kand). | '''6''' Korzystając z reguły na przypisanie, mamy do pokazania, że N(i,ile,kand) ∧ i ≤ N ∧ A[i]<> kand i ∧ 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 ≤ N+1; oraz ile > 0. | 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 ≤ N+1; oraz ile > 0. | ||
'''7''' Wpierw zauważmy, że ile ≤ 0 oraz ile ≥ 0 implikują ile=0. Korzystając z reguły na przypisanie, mamy do pokazania, że | '''7''' Wpierw zauważmy, że ile ≤ 0 oraz ile ≥ 0 implikują ile=0. Korzystając z reguły na przypisanie, mamy do pokazania, że | ||
N(i,ile,kand) ∧ i ≤ N ∧ A[i] <> kand ∧ ile =0 | N(i,ile,kand) ∧ i ≤ N ∧ A[i] <> kand ∧ 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]. | 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 ≤ N+1; oraz ile > 0. | Oczywiście i+1 ≤ 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 | '''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> | ||
</div> | </div> | ||
== Zadanie 4 (BinPower)== | == Zadanie 4 (BinPower)== | ||
Linia 388: | Linia 401: | ||
BinPower:=z; | BinPower:=z; | ||
'''end'''; | '''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): | Zacznijmy od niezmiennika pętli oznaczanego przez N(z,y,i): | ||
x<sup>n</sup>=z*y<sup>i</sup> ∧ i≥ 0 | x<sup>n</sup>=z*y<sup>i</sup> ∧ i≥ 0 | ||
Linia 423: | Linia 437: | ||
'''5''' Po wyjściu z pętli mamy N(z,y,i) ∧ i ≤ 0. Ponieważ i ≥ 0 z niezmiennika, to i=0. A więc x<sup>n</sup>=z. | '''5''' Po wyjściu z pętli mamy N(z,y,i) ∧ i ≤ 0. Ponieważ i ≥ 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).
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