Semantyka i weryfikacja programów/Ćwiczenia 14: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Sl (dyskusja | edycje)
Nie podano opisu zmian
 
m Zastępowanie tekstu – „.↵</math>” na „</math>”
 
(Nie pokazano 7 wersji utworzonych przez 3 użytkowników)
Linia 1: Linia 1:


== Zawartość ==
== Zawartość ==
Linia 26: Linia 25:




{{rozwiazanie||roz1|


<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 spróbujmy formułę <math>N \, \equiv \, i \leq j \land \exists x. i \leq x \leq j \land A[x] = v</math>.
Jako niezmiennik pętli spróbujmy formułę <math>N \, \equiv \, i \leq j \land \exists x. i \leq x \leq j \land A[x] = v</math>.
Linia 60: Linia 60:




Oprócz samego niezmiennika, musimy też ,,odgadnąć" asercję, którą wpiszemy w miejsce znaku zapytania.
Oprócz samego niezmiennika, musimy też odgadnąć asercję, którą wpiszemy w miejsce znaku zapytania.
Warunek, którego potrzebujemy to <math>N \land i \leq k < j</math>.
Warunek, którego potrzebujemy to <math>N \land i \leq k < j</math>.


Linia 71: Linia 71:
  (
  (
   <math>{\color{Red}\{N \land i < j\}}</math>
   <math>{\color{Red}\{N \land i < j\}}</math>
     <math>{\color{Green}\Downarrow}</math>        <span style="color:brown">// tutaj się nie uda! </span>
     <math>{\color{Green}\Downarrow}</math>
   <math>{\color{Red}\{N \land i \leq (i{+}j) \div 2 < j\}}</math>      <span style="color:brown">// k nie występuje w <math>N</math> </span>
   <math>{\color{Red}\{N \land i \leq (i{+}j) \,\mathrm{div}\, 2 < j\}}</math>      <span style="color:brown">// k nie występuje w <math>N</math> </span>
   <math>{\color{Red}\{N[k \mapsto (i{+}j) \div 2] \land i \leq (i{+}j) \div 2 < j\}}</math>
   <math>{\color{Red}\{N[k \mapsto (i{+}j) \,\mathrm{div}\, 2] \land i \leq (i{+}j) \,\mathrm{div}\, 2 < j\}}</math>
   k := (i + j) div 2;
   k := (i + j) div 2;
   <math>{\color{Red}\{ N \land i \leq k < j\}}</math>
   <math>{\color{Red}\{ N \land i \leq k < j\}}</math>
Linia 79: Linia 79:
   (
   (
     <math>{\color{Red}\{ N \land i \leq k < j \land A[k] < v\}}</math>
     <math>{\color{Red}\{ N \land i \leq k < j \land A[k] < v\}}</math>
     <math>{\color{Green}\Downarrow}</math>        <span style="color:brown">// tutaj też się nie uda! </span>
     <math>{\color{Green}\Downarrow}</math>        <span style="color:brown">// tutaj się nie uda! </span>
     <math>{\color{Red}\{N[i \mapsto k{+}1]\}}</math>
     <math>{\color{Red}\{N[i \mapsto k{+}1]\}}</math>
     i := k + 1
     i := k + 1
Linia 87: Linia 87:
   (
   (
     <math>{\color{Red}\{ N \land i \leq k < j \land A[k] \geq v\}}</math>
     <math>{\color{Red}\{ N \land i \leq k < j \land A[k] \geq v\}}</math>
     <math>{\color{Green}\Downarrow}</math>
     <math>{\color{Green}\Downarrow}</math>        <span style="color:brown">// tutaj się tez ie uda! </span>
     <math>{\color{Red}\{N[j \mapsto k]\}}</math>
     <math>{\color{Red}\{N[j \mapsto k]\}}</math>
     j := k
     j := k
Linia 104: Linia 104:


<math>
<math>
N \, \equiv \, i \leq j \land (\forall x. 1 \leq x < n \implies A[x] \leq A[x+1]) \land (\exists x. i \leq x \leq j \land A[x] = v).
N \, \equiv \, i \leq j \land (\forall x. 1 \leq x < n \implies A[x] \leq A[x+1]) \land (\exists x. i \leq x \leq j \land A[x] = v)</math>
</math>


Wciąz jest on jednak za słaby!  
Wciąż jest on jednak za słaby! Nie potrafimy skorzystać z wiedzy o tym, że A[1 .. n] jest posortowana, bo nie wiemy, czy <math>i,j</math> należą do zakresu tablicy! Oto prawidłowy niezmiennik:
Nie potrafimy skorzystać z wiedzy o tym, że A[1 .. n] jest posortowana, bo nie wiemy, czy i,j nalezą do zakresu tablicy!
Oto prawidłowy niezmiennik:


<math>
<math>
N \, \equiv \, 1 \leq i \leq j \leq n \land (\forall x. 1 \leq x < n \implies A[x] \leq A[x+1]) \land (\exists x. i \leq x \leq j \land A[x] = v).
N \, \equiv \, 1 \leq i \leq j \leq n \land (\forall x. 1 \leq x < n \implies A[x] \leq A[x+1]) \land (\exists x. i \leq x \leq j \land A[x] = v)</math>
</math>


</div></div>
</div></div>
}}




Linia 123: Linia 118:
Dana jest tablica dwuwymiarowa A[1 .. n, 1 .. n] zawierająca tylko liczby 0 i 1, reprezentująca relację znajomości pomiędzy n osobami.
Dana jest tablica dwuwymiarowa A[1 .. n, 1 .. n] zawierająca tylko liczby 0 i 1, reprezentująca relację znajomości pomiędzy n osobami.
Umówmy się, że
Umówmy się, że
}}


* i zna j <math>\equiv</math> A[i, j] = 1
* i zna j <math>\equiv</math> A[i, j] = 1
* i nie zna j <math>\equiv</math> A[i, j] = 0
* i nie zna j <math>\equiv</math> A[i, j] = 0.


Powiemy, że i jest ''znakomitością'' jeśli zachodzi warunek:
Powiemy, że i jest ''znakomitością'' jeśli zachodzi warunek:
Linia 135: Linia 131:
Napisz program znajdujący znakomitość, o ile istnieje. (Czy mogą być dwie różne znakomitości?)
Napisz program znajdujący znakomitość, o ile istnieje. (Czy mogą być dwie różne znakomitości?)
Przeprowadź dowód poprawności częściowej tego programu.
Przeprowadź dowód poprawności częściowej tego programu.
}}




 
<div class="mw-collapsible mw-made=collapsible mw-collapsed">
{{rozwiazanie|(część pierwsza: program)|roz2.1|
<span class="mw-collapsible-toogle mw-collapsible-toogle-default style="font-variant:small-caps">Rozwiązanie (część pierwsza: program)</span>
 
<div class="mw-collapsible-content" style="display:none">
<div class="mw-collapsible mw-made=collapsible mw-collapsed"><div class="mw-collapsible-content" style="display:none">


Oto program znajdujący znakomitość w czasie liniowym względem n:
Oto program znajdujący znakomitość w czasie liniowym względem n:
Linia 164: Linia 158:
   else
   else
     j := j - 1
     j := j - 1
  <math>{\color{Red}\{(exists k. 1 \leq k \leq n \land k \mbox{ jest znakomitością}) \implies i \mbox{ jest znakomitością}\}}</math>
  <math>{\color{Red}\{(\exists k. 1 \leq k \leq n \land k \mbox{ jest znakomitością}) \implies i \mbox{ jest znakomitością}\}}</math>




Zmieniliśmy warunek instrukcji warunkowej na ,,i zna j".  
Zmieniliśmy warunek instrukcji warunkowej na "i zna j".  
Dzięki temu nie musimy wpisywać do asercji założenia, że <math>forall i, j. A[i, j] \in \{ 0, 1 \}</math>.
Dzięki temu nie musimy wpisywać do asercji założenia <math>\forall i, j. A[i, j] \in \{ 0, 1 \}</math>.


</div></div>
</div></div>
}}


{{rozwiazanie|(część druga: dowód poprawności)|roz2.2|


<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 (część druga: dowód poprawności)</span>
<div class="mw-collapsible-content" style="display:none">


Niezmiennik  
Niezmiennik:
<math>
<math>N \, \equiv \, i \leq j \land (\exists k. 1 \leq k \leq n \land k \mbox{ jest znakomitością}) \implies (\exists k. i \leq k \leq j \land k \mbox{ jest znakomitością})</math>.
N \, \equiv \, i \leq j \land (exists k. 1 \leq k \leq n \land k \mbox{ jest znakomitością}) \implies (exists k. i \leq k \leq j \land k \mbox{ jest znakomitością}).
</math>




  <math>{\color{Red}\{n \geq 1\}}</math>
  <math>{\color{Red}\{n \geq 1\}}</math>
  i := 1; j := n;
  i := 1; j := n;
  <math>{\color{Red}\{N \\,\mathbf{end}AS
  <math>{\color{Red}\{N\}}</math>
  while  <math>{\color{Red}\{N \\,\mathbf{end}AS i <> j do
  while  <math>{\color{Red}\{N\}}</math> i <> j do
  (
  (
   <math>{\color{Red}\{N \land i \neq j \\,\mathbf{end}AS
   <math>{\color{Red}\{N \land i \neq j\}}</math>
   if i zna j then
   if i zna j then
   (
   (
     <math>{\color{Red}\{N \land i \neq j \land i \mbox{ zna } j \\,\mathbf{end}AS
     <math>{\color{Red}\{N \land i \neq j \land i \mbox{ zna } j\}}</math>
       <math>{\color{Green}\Downarrow}</math>
       <math>{\color{Green}\Downarrow}</math>
     <math>{\color{Red}\{N[i \mapsto i{+}1] \\,\mathbf{end}AS
     <math>{\color{Red}\{N[i \mapsto i{+}1]\}}</math>
     i := i + 1
     i := i + 1
     <math>{\color{Red}\{N \\,\mathbf{end}AS
     <math>{\color{Red}\{N\}}</math>
   )
   )
   else
   else
   (
   (
     <math>{\color{Red}\{N \land i \neq j \land i \mbox{ nie zna } j \\,\mathbf{end}AS
     <math>{\color{Red}\{N \land i \neq j \land i \mbox{ nie zna } j\}}</math>
     <math>{\color{Red}\{N[j \mapsto j{-}1] \\,\mathbf{end}AS
     <math>{\color{Red}\{N[j \mapsto j{-}1]\}}</math>
     j := j - 1
     j := j - 1
     <math>{\color{Red}\{N \\,\mathbf{end}AS
     <math>{\color{Red}\{N\}}</math>
   )
   )
   <math>{\color{Red}\{N \\,\mathbf{end}AS
   <math>{\color{Red}\{N\}}</math>
  )
  )
  <math>{\color{Red}\{N \land i = j \\,\mathbf{end}AS
  <math>{\color{Red}\{N \land i = j\}}</math>
  <math>{\color{Red}\{(exists k. 1 \leq k \leq n \land k \mbox{ jest znakomitością}) \implies i \mbox{ jest znakomitością}\}}</math>
  <math>{\color{Red}\{(\exists k. 1 \leq k \leq n \land k \mbox{ jest znakomitością}) \implies i \mbox{ jest znakomitością}\}}</math>




Linia 214: Linia 205:


<math>
<math>
N \land i \neq j \land i \mbox{ zna } j \, \implies \,  N[i \mapsto i{+}1].
N \land i \neq j \land i \mbox{ zna } j \, \implies \,  N[i \mapsto i{+}1]</math>
</math>


Niemiennik <math>N</math> zawiera w sobie zdanie warunkowe: ,,jeśli wśród 1 .. n istnieje znakomitość to ..".
Niemiennik <math>N</math> zawiera w sobie zdanie warunkowe: "jeśli wśród 1 .. n istnieje znakomitość to ..".
Zatem w dowodzie tej implikacji najpierw zakładamy przesłankę całej implikacji, a następnie
Zatem w dowodzie tej implikacji najpierw zakładamy przesłankę całej implikacji, a następnie
zakładamy, że wśród 1 .. n istnieje znakomitość.
zakładamy, że wśród 1 .. n istnieje znakomitość.
Linia 223: Linia 213:


</div></div>
</div></div>
}}


== Inne struktury danych ==
== Inne struktury danych ==
Linia 231: Linia 219:
{{cwiczenie|3 (algorytm zachłanny)|cw3|
{{cwiczenie|3 (algorytm zachłanny)|cw3|
Niech <math>G = (V, E)</math> będzie spójnym grafem nieskierowanym.
Niech <math>G = (V, E)</math> będzie spójnym grafem nieskierowanym.
Krawędziom grafu przypisane są różnowartościowo wagi liczbowe (czyli nie ma dwóch różnych krawędzi o tej samej wadze).
Załóżmy, że mamy do dyspozycji następujące struktury danych:
Załóżmy, że mamy do dyspozycji następujące struktury danych:


* zbiór krawędzi grafu T, z operacjami:
* zbiór T krawędzi grafu, z operacjami:
** inicjuj T jako zbiór pusty
** inicjuj T jako zbiór pusty
** dodaj nowy element do T
** dodaj nowy element do T
* kolejka priorytetowa krawędzi grafu Q, z operacjami:
* kolejka priorytetowa Q przechowująca krawędzie grafu, z operacjami:
** inicjuj kolejkę za pomocą zbioru elementów (krawędzi)
** inicjuj kolejkę za pomocą zbioru wszystkich krawędzi grafu
** deletemin(Q) usuwa z Q namniejszy element (krawędź) i zwraca go w wyniku
** add(Q, e): dodaj krawędż e do Q
* podział zbioru wierzchołków P, z operacjami:
** deletemin(Q): usuń z Q namniejszy element (krawędź)
** inicjuj P za pomocą zbioru wierzchołków jako podział jednoelementowych klas abstrakcji
** min(Q): zwróć w wyniku najmniejszy element (krawędź) w Q
** find(x) zwraca klasę abstrakcji wierzchołka x
* podział P zbioru wierzchołków grafu, z operacjami:
** union(X, Y) łączy klasy <math>X</math> i <math>Y</math> w jedną klasę <math>X \cup Y</math>.
** inicjuj P jako zbiór jednoelementowych klas abstrakcji, po jednej dla każdego wierzchołka grafu
** |P| -- liczba klas abstrakcji podziału
** find(P, x): zwróć klasę abstrakcji wierzchołka x
** union(P, X, Y): połącz klasy abstrakcji <math>X</math> i <math>Y</math> w jedną klasę <math>X \cup Y</math>
** <math>|P|</math> -- zwróć liczbę klas abstrakcji podziału P.


Graf reprezentowany jest przez dwie operacje pocz(e), kon(e) zwracające wierzchołek
Graf reprezentowany jest przez dwie operacje pocz(e), kon(e) zwracające wierzchołki będące odpowiednio początkiem i końcem krawędzi e.
będący oppowiednio początkiem i końcem krawędzi e.


Przeprowadź dowód poprawności częściowej programu obliczającego minimalne drzewo rozpinające:
Przeprowadź dowód poprawności częściowej programu obliczającego minimalne drzewo rozpinające:
Linia 254: Linia 244:
  <math>{\color{Red}\{G=(V,E) \mbox{ jest spójny}\}}</math>
  <math>{\color{Red}\{G=(V,E) \mbox{ jest spójny}\}}</math>
   
   
  P.inicjuj(V);
  // inicjuj P i Q j.w.
Q.inicjuj(E);
  T := <math>\emptyset</math>;
  T := <math>emptyset</math>;
   
   
  while |P| > 1 do
  while |P| > 1 do
   e := deletemin(Q);
   e := min(Q);
  Q := deletemin(Q);
   x = pocz(e); y := kon(e);
   x = pocz(e); y := kon(e);
   X := P.find(x);
   X := find(P, x);
   Y := P.find(y);
   Y := find(P, y);
   
   
   if X <> Y then
   if X <> Y then
     T := T <math>\cup</math> {e};
     T := T <math>\cup</math> {e};
     P.union(X, Y)
     P := union(P, X, Y)
   
   
  <math>{\color{Red}\{T \mbox{ jest minimalnym drzewem rozpinającym graf } G\}}</math>
  <math>{\color{Red}\{T \mbox{ jest minimalnym drzewem rozpinającym graf } G\}}</math>
Linia 276: Linia 266:
<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"><div class="mw-collapsible-content" style="display:none">


Tym razem niezmiennik pętli <math>N</math> jest niebanalną formułą, używającą operacji udostępnianych przez struktury danych:
Tym razem niezmiennik pętli <math>N</math> jest niebanalną formułą, używającą operacji udostępnianych przez struktury danych.
Zawiera on następujące zdania:


* T jest zawarte w pewnym minimalnym drzewie rozpinającym i
* T jest zawarte w pewnym minimalnym drzewie rozpinającym
* wierzchołki x, y należą do tej samej klasy abstrakcji podziału P wtw. gdy x i y są połączone ścieżką w T, i
* wierzchołki x, y należą do tej samej klasy abstrakcji podziału P wtw. gdy x i y są połączone ścieżką w <math>T</math>
* elementy kolejki Q zawarte należą do <math>E \setminus T</math>, i
* elementy kolejki <math>Q</math> należą do <math>E \setminus T</math>
* <math>\forall e \in E \setminus T. e \notin Q \implies</math> obydwa końce krawędzi e należą do tej samej klasy podziału P (czyli <math>T \cup \{e\}</math> ma cykl).
* jeśli krawędź <math>e</math> nie należy do <math>Q</math>, to obydwa końce krawędzi <math>e>/math> należą do tej samej klasy podziału <math>P</math>.
 
Ostatni warunek można też sformułować tak: jeśli krawędź <math>e</math> nie należy do <math>Q</math> i nie należy do drzewa <math>T</math>, to <math>T \cup \{e\}</math> ma cykl.
Oznacza to w szczególności, że krawędzi <math>e</math> na pewno nie możemy dodać do <math>T</math>.




  <math>{\color{Red}\{G=(V,E) \mbox{ jest spójny}\}}</math>
  <math>{\color{Red}\{G=(V,E) \mbox{ jest spójny}\}}</math>
   
   
  P.inicjuj(V);
  // inicjuj P i Q j.w.
Q.inicjuj(E);
  T := <math>\emptyset</math>;
  T := <math>emptyset</math>;
  <math>{\color{Red}\{N\}}</math>
  <math>{\color{Red}\{N\}}</math>
  while |P| > 1 do
  while |P| > 1 do
  (
  (
   <math>{\color{Red}\{N \land |P| > 1\}}</math>
   <math>{\color{Red}\{N \land |P| > 1\}}</math>
   e := deletemin(Q);
   e := min(Q);
  Q := deletemin(Q);
   x = pocz(e); y := kon(e);
   x = pocz(e); y := kon(e);
   X := P.find(x);
   X := find(P, x);
   Y := P.find(y);
   Y := find(P, y);
   <math>{\color{Red}\{\alpha\}}</math>  
   <math>{\color{Red}\{\alpha\}}</math>  
   if X <> Y then
   if X <> Y then
   (
   (
     <math>{\color{Red}\{\alpha \land X <> Y\}}</math>
     <math>{\color{Red}\{\alpha \land X \neq Y\}}</math>
     <math>{\color{Red}\{N[P \mapsto P \setminus \{ X, Y \} \cup \{ X \cup Y \}][T \mapsto T \cup {e}]\}}</math>   
     <math>{\color{Red}\{N[P \mapsto P \setminus \{ X, Y \} \cup \{ X \cup Y \}][T \mapsto T \cup {e}]\}}</math>   
     T := T <math>\cup</math> {e};
     T := T <math>\cup</math> {e};
     P := P <math>\setminus</math> { X, Y <math>\cup</math> { X <math>\cup</math> Y }    <span style="color:brown">//  P := union(P, X, Y)  </span>
     P := P <math>\setminus</math> { X, Y <math>\cup</math> { X <math>\cup</math> Y }    <span style="color:brown">//  czyli P := union(P, X, Y)  </span>
     <math>{\color{Red}\{N\}}</math>   
     <math>{\color{Red}\{N\}}</math>   
   )
   )
Linia 318: Linia 312:
* X, Y są klasami abstrakcji końców krawędzi e.
* X, Y są klasami abstrakcji końców krawędzi e.


Skoncentrujmy się na instrukcji warunkowej.
Pierwszy z warunków łatwo jest zaakceptować wiedząc, że <math>\mathrm{add}(\mathrm{deletemin}(Q), \mathrm{min}(Q)) = Q</math>.
Należy pokazać dwie implikacje:


* <math>\alpha \land X <> Y \, \implies \, N[P \mapsto P \setminus \{ X, Y \} \cup \{ X \cup Y \}][T \mapsto T \cup {e}]</math>   
Skoncentrujmy się wyłącznie na instrukcji warunkowej. Należy pokazać dwie implikacje:
 
* <math>\alpha \land X \neq Y \, \implies \, N[P \mapsto P \setminus \{ X, Y \} \cup \{ X \cup Y \}][T \mapsto T \cup {e}]</math>   
* <math>\alpha \land X = Y \, \implies \, N</math>.
* <math>\alpha \land X = Y \, \implies \, N</math>.


Przykładowo, pierwsza implikacja wymaga wykazania, że <math>T \cup \{ e \}</math> jest podzbiorem minimalnego drzewa rozpinającego,
Przykładowo, pierwsza implikacja wymaga wykazania, że <math>T \cup \{ e \}</math> jest podzbiorem minimalnego drzewa rozpinającego,
wiedząc, że T jest takim podzbiorem, że e nie tworzy cyklu z krawędziami z T, oraz że e jest najmniejszą krawędzią
wiedząc, że T jest takim podzbiorem, że <math>e</math> nie tworzy cyklu z krawędziami z <math>T</math>, oraz że <math>e</math> jest najmniejszą krawędzią
o tej własności.
o tej własności.
Prawdziwość tego zdania wynika stąd, że poddzbiory drzew rozpinających stranowią strukturę zwaną ''matroidem''.
Prawdziwość tego zdania wynika stąd, że poddzbiory drzew rozpinających stanowią strukturę zwaną ''matroidem''.
Pomijamy dalsze szczegóły.
Pomijamy dalsze szczegóły.


Linia 374: Linia 369:


{{cwiczenie|3|cw3.dom|
{{cwiczenie|3|cw3.dom|
}}
Dana jest tablica A przechowująca wyniki wyborów: A[i] = j oznacza, że i głosował na j.
Dana jest tablica A przechowująca wyniki wyborów: A[i] = j oznacza, że i głosował na j.
Ordynacja wyborcza jest większościowa: zwycięzcą jest osoba, na którą głosowała ''przynajmniej'' połowa uprawnionych.
Ordynacja wyborcza jest większościowa: zwycięzcą jest osoba, na którą głosowała ''przynajmniej'' połowa uprawnionych.
Sformalizuj asercję końcową i przeprowadź dowód poprawności częściowej programu znajdującego zwycięzcę:
Sformalizuj asercję końcową i przeprowadź dowód poprawności częściowej programu znajdującego zwycięzcę:
}}




  <math>{\color{Red}\{n \geq 0 \land \odd(n)\}}</math>
  <math>{\color{Red}\{n \geq 0 \land \,\mathrm{odd}(n)\}}</math>
  k := A[1]; p := 1; i := 2;
  k := A[1]; p := 1; i := 2;
  while i <= n do
  while i <= n do
Linia 397: Linia 392:


{{cwiczenie|4|cw4.dom|
{{cwiczenie|4|cw4.dom|
Przeprowadź jeszcze jeden dowód poprawności częściowej programu znajującego znakomitość, względem
Przeprowadź jeszcze jeden dowód poprawności częściowej programu znajdującego znakomitość względem
innych asercji:
innych asercji:
}}
}}




  <math>{\color{Red}\{exists k. 1 \leq k \leq n \land k \mbox{ jest znakomitością}\}}</math>
  <math>{\color{Red}\{\exists k. 1 \leq k \leq n \land k \mbox{ jest znakomitością}\}}</math>
  i := 1; j := n;
  i := 1; j := n;
  while i <> j do
  while i <> j do
Linia 409: Linia 404:
   else
   else
     j := j - 1
     j := j - 1
  <math>{\color{Red}\{\mbox{ jest znakomitością}\}}</math>
  <math>{\color{Red}\{i \mbox{ jest znakomitością}\}}</math>

Aktualna wersja na dzień 21:35, 11 wrz 2023

Zawartość

Kontynuujemy ćwiczenie dowodzenia poprawności częściowej prostych programów. Rozważymy programy operujące na tablicach i na innych strukturach danych.


Tablice

Ćwiczenie 1 (wyszukiwanie binarne)

Przeprowadź dowód poprawności częściowej poniższego programu:


{n1(x.1x<nA[x]A[x+1])(x.1xnA[x]=v)}
i := 1; j := n;
while i < j do
  k := (i + j) div 2;
  if A[k] < v then
    i := k + 1
  else
    j := k
{A[i]=v}


Rozwiązanie


Ćwiczenie 2

Dana jest tablica dwuwymiarowa A[1 .. n, 1 .. n] zawierająca tylko liczby 0 i 1, reprezentująca relację znajomości pomiędzy n osobami. Umówmy się, że

  • i zna j A[i, j] = 1
  • i nie zna j A[i, j] = 0.

Powiemy, że i jest znakomitością jeśli zachodzi warunek:

j.(1jnji)(j zna ii nie zna j)

Napisz program znajdujący znakomitość, o ile istnieje. (Czy mogą być dwie różne znakomitości?) Przeprowadź dowód poprawności częściowej tego programu.


Rozwiązanie (część pierwsza: program)


Rozwiązanie (część druga: dowód poprawności)

Inne struktury danych

Ćwiczenie 3 (algorytm zachłanny)

Niech G=(V,E) będzie spójnym grafem nieskierowanym. Krawędziom grafu przypisane są różnowartościowo wagi liczbowe (czyli nie ma dwóch różnych krawędzi o tej samej wadze). Załóżmy, że mamy do dyspozycji następujące struktury danych:

  • zbiór T krawędzi grafu, z operacjami:
    • inicjuj T jako zbiór pusty
    • dodaj nowy element do T
  • kolejka priorytetowa Q przechowująca krawędzie grafu, z operacjami:
    • inicjuj kolejkę za pomocą zbioru wszystkich krawędzi grafu
    • add(Q, e): dodaj krawędż e do Q
    • deletemin(Q): usuń z Q namniejszy element (krawędź)
    • min(Q): zwróć w wyniku najmniejszy element (krawędź) w Q
  • podział P zbioru wierzchołków grafu, z operacjami:
    • inicjuj P jako zbiór jednoelementowych klas abstrakcji, po jednej dla każdego wierzchołka grafu
    • find(P, x): zwróć klasę abstrakcji wierzchołka x
    • union(P, X, Y): połącz klasy abstrakcji X i Y w jedną klasę XY
    • |P| -- zwróć liczbę klas abstrakcji podziału P.

Graf reprezentowany jest przez dwie operacje pocz(e), kon(e) zwracające wierzchołki będące odpowiednio początkiem i końcem krawędzi e.

Przeprowadź dowód poprawności częściowej programu obliczającego minimalne drzewo rozpinające:


{G=(V,E) jest spójny}

// inicjuj P i Q j.w.
T := ;

while |P| > 1 do
  e := min(Q);
  Q := deletemin(Q);
  x = pocz(e); y := kon(e);
  X := find(P, x);
  Y := find(P, y);

  if X <> Y then
    T := T  {e};
    P := union(P, X, Y)

{T jest minimalnym drzewem rozpinającym graf G}


Rozwiązanie (szkic)

P


Zadania domowe

Ćwiczenie 1

Przeprowadź jeszcze jeden dowód poprawności częściowej wyszukiwania binarnego.


{n1x.1x<nA[x]A[x+1]}
i := 1; j := n;
while i < j do
  k := (i + j) div 2;
  if A[k] < v then
    i := k + 1
  else
    j := k
{(x.1xnA[x]=v)A[i]=v}


Program pozostał ten sam, ale zmienione zostały asercje.


Ćwiczenie 2

Sformalizuj asercję końcową w poniższym programie i przeprowadź dowód poprawności częściowej:


{n1x.1x<nA[x]A[x+1]}
i := 1; p := 0;
while i <= n do
  if A[i] = A[i - p] then
    p := p + 1;
  i := i + 1
{ maksymalna długość stałego odcinka w tablicy A wynosi p}


Ćwiczenie 3

Dana jest tablica A przechowująca wyniki wyborów: A[i] = j oznacza, że i głosował na j. Ordynacja wyborcza jest większościowa: zwycięzcą jest osoba, na którą głosowała przynajmniej połowa uprawnionych. Sformalizuj asercję końcową i przeprowadź dowód poprawności częściowej programu znajdującego zwycięzcę:


{n0odd(n)}
k := A[1]; p := 1; i := 2;
while i <= n do
  if A[i] = k then
    p := p + 1;
  else
    if p > 1 then
      p := p - 1
    else
      k := A[i];
      p := 1;
  i := i + 1
{ jeśli jest zwycięzca to jest nim k}


Ćwiczenie 4

Przeprowadź jeszcze jeden dowód poprawności częściowej programu znajdującego znakomitość względem innych asercji:


{k.1knk jest znakomitością}
i := 1; j := n;
while i <> j do
  if i zna j then
    i := i + 1
  else
    j := j - 1
{i jest znakomitością}