Poprawność algorytmu

advertisement
Poprawność algorytmu - ćwiczenia 2
Definicja 1.
Algorytm jest częściowo poprawny, gdy dla każdego zestawu danych X ze zbioru dopuszczalnych
danych wejściowych jeżeli algorytm sie zatrzyma, to relacja R pomiędzy zestawem danych X,
a otrzymanym zestawem wyników jest zachowana.
Warunek stopu
Algorytm zatrzymuje sie dla dozwolonego zbioru danych wejściowych jeśli zatrzymuje sie (kończy) dla
każdego zestawu danych z tego zbioru.
Definicja 2.
Algorytm jest całkowicie poprawny, gdy dla każdego zestawu danych X ze zbioru dopuszczalnych
danych wejściowych algorytm zatrzymuje się i jest częściowo poprawny.
algorytm całkowicie poprawny = algorytm częściowo poprawny + warunek stopu
Dowodzenie częściowej poprawności algorytmu - metoda niezmienników Floyda:
 Wybieramy w algorytmie punkty kontrolne.
 Dla każdego z punktów kontrolnych określamy asercje (funkcja logiczna reprezentująca
przypuszczenie co do stanu algorytmu; niezmiennik).
 Pokazujemy, że z prawdziwości jednej asercji wynika prawdziwość następnej asercji (a to
pociąga za sobą prawdziwość ostatniej asercji).
Asercja początkowa (w pierwszym punkcie kontrolnym) - warunki nakładane na dopuszczalne
dane wejściowe.
Asercja końcowa (w ostatnim punkcie kontrolnym) - opisuje relacje wyników i danych
wejściowych.
Niezmiennik pętli (iteracji) - własność (formuła), która jeśli jest prawdziwa na początku
wykonania pętli, to jest również prawdziwa po wykonaniu pętli.
Dowodzenie całkowitej poprawności algorytmu:
 metoda niezmienników,
 pokazanie zbieżności
Zbieżnik - wielkość zależna od zmiennych i danych; wykazujemy, ze zbieżnik zmniejsza się w
czasie wykonania algorytmu, lecz istnieje granica poniżej której nie zejdzie, np. zbieżnikiem dla
algorytmu sortowania jest liczba elementów, które pozostały jeszcze nieposortowane.
Zad 1. Rozważmy problem znalezienia elementu największego w danym ciągu liczb naturalnych
x0,..., xn-1. Algorytm rozwiązujący ten problem możemy zapisać w postaci:
max_el = x0;
i = 1;
while (i < n)
{
if (xi > max_el) max_el= xi;
i = i+1;
}
a) Wykaż częściową poprawność tego algorytmu korzystając z metody niezmienników.
b) Wykaż całkowitą poprawność algorytmu
Rozwiązanie:
Aby wykazać częściową poprawność algorytmu należy pokazać, że:
 w pierwszym kroku iteracji prawdziwość asercji 1 implikuje prawdziwość asercji 2
 w kolejnych przebiegach iteracji: prawdziwość asercji 2 implikuje prawdziwość tej asercji w
następnym kroku (niezmiennik iteracji)
 w ostatnim kroku iteracji prawdziwość asercji 2 implikuje prawdziwość asercji 3
Asercja 1: Naturalnym warunkiem początkowym będzie założenie niepustości ciągu: nie można
znaleźć elementu największego w zbiorze pustym. Możemy ją zapisać w postaci: n>0.
Asercja 2: W i-tym kroku (1<=i<n) element maksymalny max_eli = max{max_eli-1, xi}
Asercja 3: W max_el mamy element maksymalny całego ciągu tj. max_el = xk dla pewnego k
< n oraz dla wszystkich i<n, xi <= max_el.
//Asercja 1
max_el = x0;
i = 1;
while (i < n) //Asercja 2
{
if (xi > max_el)
max_el = xi;
i = i+1;
}
// Asercja 3
Algorytm max zatrzymuje się dla dowolnych danych w strukturze liczb naturalnych. Rzeczywiście,
zmienna i, kontrolująca pętlę, przyjmuje jako swoje wartości kolejne liczby naturalne, a parametr n
też jest liczbą naturalną. Zatem po skończonej liczbie kroków zmienna i przyjmie wartość n.
Co więcej, po wykonaniu algorytmu spełniony jest warunek (xi <= max_el dla wszystkich i):
W pętli "while" każdy element ciągu jest porównywany z elementem max_el. Jeśli po przejrzeniu
i-pierwszych elementów ciągu, max_el ma wartość największego z nich, to w następnym kroku,
max_el przyjmie wartość największego elementu wśród pierwszych (i+1) elementów:
max{max{x1,..., xi}, xi+1} = max{x1,..., xi+1}. Ponieważ, dla ciągu o długości 1,
wartością zmiennej max_el jest element największy (ten jedyny element ciągu), to, na mocy zasady
indukcji matematycznej, dla ciągu dowolnej długości n, po zakończeniu pętli "while", wartością
max_el będzie element największy ciągu x1,...,xn.
Badanie poprawności metodą niezmienników możemy również realizować bezpośrednio w
programie korzystając (w C++) z makra assert(...):
#include <cassert>
#include <iostream>
using namespace std;
//funkcja pomocnicza do asercji 3
bool testmaxtab(int n, int x[], int max_el)
{
for (int i=0; i<n; i++)
if (x[i] > max_el) return false;
return true;
}
int maxtab(int n, int x[])
{
//Asercja 1
assert(n > 0);
int max_el = x[0];
int i = 1;
while (i < n)
{
int max_el_i_1 = max_el;
if (x[i] > max_el) max_el = x[i];
//Asercja 2
assert(max_el == (max_el_i_1 > x[i]?max_el_i_1 : x[i]));
i = i+1;
}
// Asercja 3
assert(testmaxtab(n, x, max_el));
return max_el;
}
int main(int argc, char* argv[])
{
int tab[10] = {3,56,-3,4,67,31,11,55,9,0};
cout << maxtab(10, tab) << endl;
system("pause");
return 0;
}
Uwaga: Ze względu na to, że asercja jest nienormalnym przerwaniem programu, to aby zobaczyć
błędy zgłaszane przez assert w programie konsolowym należy ten program uruchomić z wiersza
poleceń/konsoli.
Aby wyłączyć wszystkie asercje użyte w danym kodzie należy przed linijką
#include <cassert>
umieścić definicję:
#define NDEBUG
Przetestuj działanie asercji generując błędy np.
test asercji 1:
cout << maxtab(0, tab) << endl;
test asercji 2:
zmieniamy wyrażenie na przeciwne : if (x[i] < max_el)
Zad 2. Rozważmy algorytm znajdowania NWD dwóch liczb naturalnych x i y
// jeżeli jedna z liczb x, y jest równa zeru, to największy wspólny
//dzielnik tych liczb jest równy drugiej z nich
if (x*y == 0)
nwd = x+y;
else
{
while (!(x == y))
{
if (x > y)
x = x - y;
else
y = y - x;
}// jeżeli liczby są równe, to ich największy wspólny dzielnik jest równy ich wspólnej wartości
}
nwd = y;
Wykaż całkowitą poprawność tego algorytmu.
Download