n - Wkuwanko.pl

Transkrypt

n - Wkuwanko.pl
Podstawy Informatyki
Algorytmy i ich poprawność
Błędy
Błędy:
• językowe
• logiczne
Błędy językowe
• Związane ze składnią języka
• Wykrywane automatycznie przez kompilator lub interpreter
• Prosty sposób usuwania
Przykład:
//fragment programu w języku C++
.........
if(a){
................
błąd!!! Komunikat kompilatora: „Else” – brak
}
deklaracji tego identyfikatora.
Else{
Słowo kluczowe „else” powinno być napisane z
................
małej litery
}
Błędy logiczne (1)
Błędy semantyczne
Związane z semantyką języka programowania
– Kompilator w niektórych przypadkach sugeruje (ostrzega), że dana
konstrukcja może nie działać poprawnie
– Stosunkowo prosto można je zdiagnozować i usunąć
Przykład:
//fragment programu w języku C++
.........
if(i=8){
................
}
...............
Błąd semantyczny
Zamiarem programisty było sprawdzenie
czy i jest równe 8 (i==8), a nie
dokonanie podstawienia i=8 (co zawsze
jest prawdziwe)
Błędy logiczne (2)
Przykład
Błędy algorytmiczne
– Błędy logiczne wynikające z niepoprawnego rozwiązania zadania
algorytmicznego (błędny algorytm rozwiązania)
– Są niezależne od języka, w którym zaimplementowano algorytm
– Najtrudniej wykrywalne
................
Struktura „Lista”
struktura Lista{
next
info
Dane info;
Wskaźnik_Lista next; //next jest wskaźnikiem do obiektu typu Lista
};
void main(){
Wskaźnik_Lista head=Λ; //lista pusta
................
head
Λ
utworz_liste(head,plik); //utworz liste z danych w pliku „plik”
//i niech head wskazuje na pierwszy element
//tej listy
while(head){
//przegladanie elementow listy
//operacje na liscie
...................
błąd algorytmiczny
head = head->next;
}
................
}
Testowanie programów
– Testowanie odbywa się poprzez uruchamianie programu dla danych
testowych
–
Debugging – proces wielokrotnego uruchamiania programu dla
wykrycia i usunięcia błędów. Większość środowisk programistycznych
jest wyposażona w narzędzia wspomagające proces debuggowania
Obydwie metody nie dają gwarancji wykrycia i usunięcia wszystkich
błędów.
Niepoprawność algorytmu
– Wykonanie algorytmu rozwiązania zadania dla pewnego zestawu danych
zakończy się prawidłowo, ale wyniki są niepoprawne
np. źle sformułowany warunek, powinna być silna nierówność, a jest słaba
–
Działanie algorytmu dla pewnego zestawu danych zostanie przerwane;
np. dzielenie przez zero, obliczenie pierwiastka kwadratowego z liczby ujemnej
–
Algorytm dla pewnego zestawu danych działa w nieskończoność
(pętle nieskończone);
np. przy przeglądaniu listy wskaźnikowej, wskaźnik nie jest przesuwany do
następnego elementu listy
Poprawność algorytmu
Poprawność algorytmu
Zadanie algorytmiczne:
1. I – zbiór dopuszczalnych danych wejściowych
2. R – zależności między danymi wejściowymi a żądanymi wynikami
Przykład:
• Zbiór I = <N,L>, gdzie N∈ℵ+, L – lista słów.
• Zależność R – wynikiem ma być lista N-elementowa L’, utworzona ze
słów z listy L, uporządkowana leksykograficznie
• Zbiór I i relacja R są specyfikacją zadania uporządkowania listy L,
opisanego algorytmem A.
Algorytm A jest częściowo poprawny względem I i R:
∀X∈I, jeżeli A uruchomiony dla I zatrzyma się
⇒
relacja R między X a otrzymanym wynikiem jest spełniona
Algorytm A zatrzymuje się dla zbioru I, tzn. algorytm A zatrzymuje się ∀X ∈I
Algorytm A jest całkowicie poprawny względem I i R:
∀X ∈I algorytm A zatrzymuje się i daje wynik spełniający relację R
Poprawność algorytmu
Indukcja matematyczna
Niech P(n) będzie pewnym stwierdzeniem o liczbie całkowitej n. Chcemy udowodnić,
że P jest prawdziwe dla n∈ℵ+. Korzystamy z indukcji matematycznej:
1. Przedstaw dowód, że P(1) jest prawdziwe
2. Przedstaw dowód, że „jeśli wszystkie P(1), P(2), .., P(n) są prawdziwe, to P(n+1) jest
też prawdziwe”
Algorytm Euklidesa (rozszerzony)
Dane są dwie dodatnie liczby całkowite m i n. Oblicz ich największy wspólny dzielnik
(nwd) d, oraz dwie liczby całkowite a i b takie, że am + bn = d.
K1. Inicjowanie. a’ ← b ← 1; a ← b’ ← 0; c ← m; d ← n;
K2. Dzielenie. Niech q i r będą odpowiednio ilorazem i resztą z dzielenia c przez d:
c = qd + r i 0 ≤ r < d;
K3. Czy reszta zero? Jeśli r = 0, zakończ algorytm; w tym przypadku am + bn = d;
K4. Ponowienie. Przyjmij c ← d; d ← r; t ← a’; a’ ← a; a ← t – qa; t ← b’; b’ ← b;
b ← t – qb; wróć do K2.
Przykład
Niech m = 2884, n = 364
a’
a
b’
1
0
0
0
1
1
1
-1
-7
b
1
-7
8
c
2884
364
336
(-1)*2884 + 8*364 = -2884 + 2912 = 28
d
364
336
28
q
7
1
12
r
336
28
0
Poprawność algorytmu
Dowód poprawności algorytmu Euklidesa dla dowolnych m i n.
Lemat
Równości
a’m + b’n = c, am + bn = d
są prawdziwe po każdym wykonaniu kroku K2 (na podstawie faktu, że równości są
prawdziwe po pierwszym dojściu do K2 i krok K4 nie zmienia ich prawdziwości).
Stwierdzenie
Pary {m, n} i {n, r} mają ten sam zbiór wspólnych dzielników
Indukcja względem n.
1. Jeśli m jest wielokrotnością n, to algorytm działa poprawnie (pierwsze
wykonanie kroku K3). Tak jest zawsze, gdy n = 1.
2. Jeśli n > 1 i m nie jest wielokrotnością n. Algorytm po pierwszym przebiegu
przechodzi do wykonania przypisania c ← d; d ← r, oraz r <n ⇒ z założenia
indukcyjnego stwierdzamy, że końcowa wartość d jest nwd liczb n i r. Ze
stwierdzenia że, pary {m, n} i {n, r} mają te same wspólne dzielniki ⇒ wspólny
nwd. Stąd d jest nwd liczb m i n. Z lematu ⇒ am + bn = d.
Poprawność algorytmu
Ogólna metoda dowodzenia poprawności dowolnego algorytmu: opis schematu
blokowego algorytmu asercjami.
Asercje
Opisuje stan rzeczy w chwili, gdy wykonywane jest obliczenie strzałki w schemacie
blokowym algorytmu związanej z tą asercją.
start
A1: m > 0 , n > 0
K1. a’ ← 1;
b’ ← 0;
b ← 1;
c ← m;
a ← 0;
d ← n;
A2: c = m > 0 , d = n > 0,
a = b’ = 0, a’ = b = 1
K2. q ← iloraz(c / d);
r ← reszta(c / d);
K3. r == 0?
tak
nie
K1. c ← d; d ← r;
t ← a’; a’ ← a; a ← t – qa;
t ← b’; b’ ← b; b ← t – qb;
A3: am + bn = d, a’m + b’n = c = qd + r,
0 <= r < d, nwd(c, d) = nwd(m, n)
A4: am + bn = d = nwd(m, n)
stop
A5: am + bn = d, a’m + b’n = c = qd + r,
0 < r < d, nwd(c, d) = nwd(m, n)
A6: am + bn = d, a’m + b’n = c = qd + r,
d > 0, nwd(c, d) = nwd(m, n)
Poprawność algorytmu
Ogólna metoda polega na udowodnieniu, że dla każdego bloku zachodzi warunek:
Jeśli asercja związana z jakąkolwiek strzałką prowadzącą do bloku jest prawdziwa
zanim zostanie wykonana operacja opisywana przez blok, to wszystkie asercje
związane ze strzałkami wychodzącymi z bloku są prawdziwe po wykonaniu tej
operacji.
Z prawdziwości powyższego twierdzenia dla każdego bloku wynika, że wszystkie
asercje są prawdziwe podczas każdego wykonania algorytmu (dowód przez indukcję
względem liczby kroków obliczenia – w sensie liczby strzałek w schemacie blokowym,
po którym przeszło obliczenie).
Metoda dowodzenia poprawności algorytmów przez asercje i indukcję pochodzi od
R.W. Floyda: definicja każdej operacji języka programowania może być sformułowana
jako reguła logiczna mówiąca, że pewne asercje można udowodnić po operacji przy
założeniu, że pewne asercje były prawdziwe przed operacją.
Metoda została rozwinięta przez C.A.R. Hoare’a jako metoda niezmienników.