Podstawowe elementy programu patrz:następne2 slajdy
Transkrypt
Podstawowe elementy programu patrz:następne2 slajdy
Podstawowe elementy programu Zestaw komend stojący do dyspozycji programisty • zależy od języka programowania; • jest ograniczony; • jest na tyle bogaty, że daje się z nich złożyć (jak z klocków) sensowne programy. Umiejętność programowania jest sztuką odpowiedniego układania tych klocków. Do typowych komend należą: patrz: n astępne 2 slajdy Wykład 2. INSTRUKCJE I NIEZMIENNIKI, str. 2 Podstawowe elementy programu Komendy proste: SCHEMAT przypisanie wartości zmiennej C ? zmienna ← wartość ? zmienna = wartość ; Podstawowe elementy programu Komendy złożone (strukturalne): SCHEMAT pętla C ? warunek | ¬warunek ? ciało while ( warunek ) { ciało } ? rozgałęzienie ? warunek | ¬warunek ? ? instrukcja1 instrukcja2 if ( warunek ) { instrukcja1 } else { instrukcja2 } ? Wykład 2. INSTRUKCJE I NIEZMIENNIKI, str. 4 Rozgałęzienie warunkowe pełne ? warunek | ¬warunek ? ? instrukcja1 instrukcja2 ? uproszczone ? warunek | ¬warunek ? instrukcja1 ? ? if ( warunek ) { instrukcja1 } else { instrukcja2 } if ( warunek ) { instrukcja1 } Budowa prostych programów Zagnieżdżanie instrukcji: ? war1 | ¬war1 ? war | ¬war2 2 ? ? instr1 instr2 while (war1 ) { if (war2 ) { instr1 } else { instr2 } } ? ? schemat C Wykład 2. INSTRUKCJE I NIEZMIENNIKI, str. 6 Przykład programu: największy wspólny dzielnik a=A; b=B; while (a != b) { if (a > b) a = a-b; else b = b-a; } != — oznaczenie relacji „nierówne” w języku C A 180 180 180 180 180 180 180 180 B a b 48 48 180 48 48 132 48 48 84 48 48 36 48 48 36 12 48 24 12 48 12 12 Przykład programu: NWD wielu liczb nwd(a1 , a2 , a3 , a4 ) = nwd nwd nwd(a1 , a2 ), a3 , a4 nwd(a1 , a2 , . . . , an ) = nwd(nwd(nwd(a1 , a2 ), . . .), an ) wczytaj a; ile wczyt = 1; while (ile wczyt < n){ wczytaj b; ile wczyt = ile wczyt+1; while (a != b) { if (a > b) a = a-b; else b = b-a; } } Wykład 2. INSTRUKCJE I NIEZMIENNIKI, str. 8 Poprawność algorytmów Przykład: (potęgowanie przez wielokrotne mnożenie) M ? wynik ← 1; w ← wykładnik wynik · podstawaw = podstawawykladnik ? w>0 | w=0 wynik · podstawa · podstawaw−1 = = podstawawykladnik ? wynik ← wynik · podstawa wynik · podstawaw−1 = podstawawykladnik ? w ←w−1 ? wynik = podstawawykladnik Metoda niezmienników DEFINICJA: M Niezmiennik pętli while (war) instr — dowolna własność P zmiennych programu taka, że jeśli przed wykonaniem instr spełnione jest P & war, to po wykonaniu instr spełnione jest P TWIERDZENIE: M Jeśli P jest niezmiennikiem pętli while (war) instr i po inicjalizacji pętli jest spełniony, to na wyjściu z pętli jest spełnione P & ¬war . Wykład 2. INSTRUKCJE I NIEZMIENNIKI, str. 10 Niezmienniki spoza informatyki 2 m/sek - .................... ??? m/sek 6 ........... ...... ........... . . . . . . . . . . . . . ..... 6 12 m ...... . ..... . 10 m . . ......... ......... . . . . . . ? ? . . . . . . . . . ·································· mv 2 + mgh energia = energia kinetyczna + energia potencjalna = 2 — jest stała. v2 (2 m/sek)2 2 + 10 m/sek · 12 m = + 10 m/sek2 · 10 m 2 2 √ v = 44 m/sek ≈ 6.63 m/sek Metoda niezmienników W trakcie obliczeń pętli wartości zmiennych stale się zmieniają, ale pozostaje w mocy pewien związek między nimi — niezmiennik. Z tego faktu wynikają własności pętli. ............. A A — założenia wstępne B — niezmiennik C — niezmiennik + warunek D — stan wynikowy ? inicjalizacja - .............. B ? warunek | ¬warunek ............. C ? ciało ............. D Trzeba zbadać: 1. przejście przez inicjalizację z A do B, 2. przejście przez ciało z C do B, 3. implikację B & warunek ⇒ C, 4. implikację B & ¬warunek ⇒ D. ? Wykład 2. INSTRUKCJE I NIEZMIENNIKI, str. 12 Co liczy dany program? ? n ← 500 000; a ← 0; b ← 0; c ← 1; d ← 6 B ? b+c¬n | b+c>n ? a ← a + 1; b ← b + c ? c←c+d ? d←d+6 ? Ręczna symulacja: wartości zmiennych w B w kilku pierwszych obrotach pętli: a b c d 0 0 1 6 1 1 7 12 2 8 19 18 3 27 37 24 4 64 61 30 5 125 91 36 6 216 127 42 7 343 169 48 Zależności między wartościami zmiennych: b = a3 d = 6 · (a + 1) 3 c = (a + 1) − a3 = 3a2 + 3a + 1 Co liczy dany program? Czy B jest niezmiennikiem? ? n ← 500 000; a ← 0; b ← 0; c ← 1; d ← 6 3 b = a & d = 6(a + 1) & c = 3a2 + 3a + 1 B ? b+c¬n | b+c>n zmienne nieprimowane — przed przejściem przez ciało pętli zmienne primowane — po przejściu przez ciało pętli Wiemy: b = a3 & d = 6(a + 1) & c = 3a2 + 3a + 1 & b + c ¬ n ? a ← a + 1; b ← b + c oraz a′ = a + 1 b′ = b + c ? c←c+d c′ = c + d d′ = d + 6 Stąd wnioskujemy: ? d←d+6 ? ? b′ = a′ & d′ = 6(a′ + 1) & 2 ? 3a′ + 3a′ + 1 c′ = 3 ? Wykład 2. INSTRUKCJE I NIEZMIENNIKI, str. 14 Co liczy dany program? ? n ← 500 000; a ← 0; b ← 0; c ← 1; d ← 6 3 b = a & d = 6(a + 1) & c = 3a2 + 3a + 1 B ? b+c¬n | b+c>n ? a ← a + 1; b ← b + c ? c←c+d ? d←d+6 D b = a3 & d = 6(a + 1) & c = 3a2 + 3a + 1 & b + c > 500 000 D & a3 ¬ 500 000 < (a + 1)3 D √ & a ¬ 3 500 000 < a + 1 D &a= ? √ 3 Program oblicza a= j√ k 3 500 000 = = ⌊79.37 . . .⌋ = 79 500 000 ⌊x⌋ — część całkowita liczby rzeczywistej Przykłady programów /* A : m 1 */ k=0; p=1; q=2; /* B : p = 3k ¬ m & q = 2 · 3k */ while (p+q <= m) { k=k+1; p=p+q; q=p+p; } /* D : k = ⌊log3 m⌋ */ Dowieść (zmienne primowane oznaczają nowe wartości): 1. m 1 & k ′ = 0 & p′ = 1 & q ′ = 2 ′ ′ p′ = 3k ¬ m & q ′ = 2 · 3k ? ⇒ 2. p = 3k ¬ m & q = 2 · 3k & p + q ¬ m & k ′ = k + 1 & p′ = p + q & q ′ = p′ + p′ ′ ′ p′ = 3k ¬ m & q ′ = 2 · 3k 3. p = 3k ¬ m & q = 2 · 3k & p + q > m ? ⇒ ? ⇒ k = ⌊log3 m⌋ Wykład 2. INSTRUKCJE I NIEZMIENNIKI, str. 16 Przykład programu: największy wspólny dzielnik /* A > 0 & B > 0 */ a=A; b=B; /* a > 0 & b > 0 & nwd(a, b) = nwd(A, B) */ while (a != b) { if (a > b) a = a-b; else b = b-a; } /* a = nwd(A, B) */ Przykłady programów Dzielenie całkowite z resztą: Dzielenie dokładne: Dzielenie z resztą: q jest ilorazem n przez k ⇐⇒ k·q =n q jest ilorazem całkowitym a r jest resztą n przez k ⇐⇒ k·q+r =n&0¬r <k Dzielenie z resztą łatwo zorganizować przez wielokrotne odejmowanie: • znaleźć jakiekolwiek q i r spełniające k · q + r = n & 0 ¬ r , • tak długo odejmować dzielnik k od r, aż dodatkowo będzie spełnione r < k (zakładamy k > 0). Wykład 2. INSTRUKCJE I NIEZMIENNIKI, str. 18 Przykłady programów Dzielenie całkowite z resztą (zał.: k > 0): A ? q ← 0; r ← n n0&k>0 1. Przejście od A do B - k·q+r =n&0¬r B ? k¬r | r<k k·q+r =n&0<k ¬r k · (q + 1) + (r − k) = n & 0 ¬ r − k C C ? r ←r−k k · (q + 1) + r = n & 0 ¬ r k·q+r =n&0¬r E B ? q ←q+1 D ? k·q+r =n&0¬r <k 2. Wynikanie B&k¬r⇒C 3. Przejście od C do B (przez E) 4. Wynikanie B&r<k⇒D Przykłady programów Dzielenie całkowite z resztą: /* n 0 & k > 0 */ q = 0; r = n; /* n = k · q + r & 0 ¬ r */ while (r>=k) { q = q+1; r = r-k; } /* n = k · q + r & 0 ¬ r < k */ Dla poprawności należy sprawdzić: 1. jeśli n 0 & k > 0, to po wykonaniu q = 0; r = n; jest n = k · q ′ + r′ & 0 ¬ r′ 2. jeśli n = k · q + r & 0 ¬ r & r k, to po wykonaniu q = q+1; r = r-k; jest n = k · q ′ + r′ & 0 ¬ r′ 3. jeśli n = k · q + r & 0 ¬ r & r < k, to n = k · q ′ + r′ & 0 ¬ r′ < k Wykład 2. INSTRUKCJE I NIEZMIENNIKI, str. 20 Przykład programu: pierwiastek kwadratowy √ Napisać algorytm, który dla danej liczby naturalnej n oblicza ⌊ n⌋, czyli część całkowitą pierwiastka kwadratowego z n. /* n 0 */ a=0; h=1; while (n >= h*h) h = 2*h; /* h 1 & a2 ¬ n < (a + h)2 */ while (h>1) { h = h/2; if (n >= (a+h)*(a+h)) a = a+h; } 2 /* a2 ¬ n √< (a + 1) */ /* a = ⌊ n⌋ */ a2 ¬ n < (a√+ 1)2 ⇒ a = ⌊ n⌋ h1& a2 ¬ n < (a + h)2 & h¬1 ⇒ a2 ¬ n < (a + 1)2 Niezmiennik pętli: h 1 & a2 ¬ n < (a + h)2 Przykład: pierwiastek kwadratowy √ Napisać algorytm, który dla danej liczby naturalnej n oblicza ⌊ n⌋, czyli część całkowitą pierwiastka kwadratowego z n. /* n 0 */ a=0; h=1; while (n >= h*h) h = 2*h; /* h 1 & a2 ¬ n < (a + h)2 */ while (h>1) { h = h/2; if (n >= (a+h)*(a+h)) a = a+h; } 2 /* a2 ¬ n √< (a + 1) */ /* a = ⌊ n⌋ */ Żeby nie podnosić sumy do kwadratu w każdym obrocie pętli, wprowadzamy nowe zmienne: x = a2 , y = a · h, z = h2 . Wtedy: (a + h) · (a + h) =x+2·y+z Nowy niezmiennik pętli: h 1 & a2 ¬ n < (a + h)2 & x = a2 & y = a · h & x · z = y2 Wykład 2. INSTRUKCJE I NIEZMIENNIKI, str. 22 Przykład: pierwiastek kwadratowy √ Napisać algorytm, który dla danej liczby naturalnej n oblicza ⌊ n⌋, czyli część całkowitą pierwiastka kwadratowego z n. /* n 0 */ a=0; x=0; y=0; h=1; z=1; while (n >= h*h) { h = 2*h; z = 4*z; } /* h 1 & a2 ¬ n < (a + h)2 & x = a2 & y = a · h & z = h2 & x · z = y 2 */ while (h>1) { h = h/2; y=y/2; z=z/4; if (n >= x+2*y+z) { a = a+h; x = x+2*y+z; y = y+z; } } 2 /* a2 ¬ n √< (a + 1) */ /* a = ⌊ n⌋ */ Przykład: pierwiastek kwadratowy √ Napisać algorytm, który dla danej liczby naturalnej n oblicza ⌊ n⌋, czyli część całkowitą pierwiastka kwadratowego z n. /* n 0 */ a=0; x=0; y=0; h=1; z=1; while (n >= h*h) { h = 2*h; z = 4*z; } /* h 1 & a2 ¬ n < (a + h)2 & x = a2 & y = a · h & z = h2 & x · z = y 2 */ while (h>1) { h = h/2; y=y/2; z=z/4; if (n >= x+2*y+z) { a = a+h; x = x+2*y+z; y = y+z; } } 2 /* a2 ¬ n √< (a + 1) */ /* a = ⌊ n⌋ */ Przy takim niezmienniku: n h2 ⇐⇒ n z h 1 ⇐⇒ z 1 h=1 ⇒ y=a więc zmienne a i h już nie są potrzebne Wykład 2. INSTRUKCJE I NIEZMIENNIKI, str. 24 Przykład: pierwiastek kwadratowy √ Napisać algorytm, który dla danej liczby naturalnej n oblicza ⌊ n⌋, czyli część całkowitą pierwiastka kwadratowego z n. /* n 0 */ a=0; x=0; y=0; h=1; z=1; while (n >= z) { h = 2*h; z = 4*z; } /* z 1 & x ¬ n < x + 2 · y + z & x = a2 & y = a · h & z = h2 & x · z = y 2 */ while (z>1) { h = h/2; y=y/2; z=z/4; if (n >= x+2*y+z) { a = a+h; x = x+2*y+z; y = y+z; } } /* y ¬ n√< (y + 1)2 */ /* y = ⌊ n⌋ */ Przykład: pierwiastek kwadratowy √ Napisać algorytm, który dla danej liczby naturalnej n oblicza ⌊ n⌋, czyli część całkowitą pierwiastka kwadratowego z n. /* n 0 */ x=0; y=0; z=1; while (n >= z) z = 4*z; /* z 1 & x ¬ n < x + 2 · y + z & x · z = y 2 */ while (z>1) { y=y/2; z=z/4; if (n >= x+2*y+z) { x = x+2*y+z; y = y+z; } } /* y ¬ n√< (y + 1)2 */ /* y = ⌊ n⌋ */ Nowy program z dowodem poprawności, nadal liczący część całkowitą pierwiastka kwadratowego. n 35 x 0 y 0 16 16 8 4 5 25 z 1 4 16 64 16 4 1 Wykład 2. INSTRUKCJE I NIEZMIENNIKI, str. 26 Jak pisać programy z dowodami? • Z tego, że asercje rozmieszczone w programie są niezmiennikami, wnioskujemy o jego poprawności. Np. w powyższym programie z niezmienniczości asercji n 0 na wejściu wynika niezmienniczość asercji √ a = ⌊ n⌋ na wyjściu, czyli fakt, że program liczy część całkowitą pierwiastka z n. • Konstruując program najpierw piszemy odpowiednie asercje; potem tak dobieramy komendy, żeby te asercje były niezmiennicze. • Program powstaje więc razem z dowodem swojej poprawności; dowód zawsze „o pół kroku wcześniej”. • Asercje mogą również pomóc w kontrolowanym transformowaniu programu do innej postaci.