Transcript Wyklad4_studentx
Slide 1
Wprowadzimy teraz pojęcie niezmiennika pętli, które jest często
wykorzystywane do projektowania algorytmów i dowodzenia ich
poprawności. Rozważmy pętlę „while”, która ma postać:
Slide 2
Slide 3
Slide 4
Ostatnie stwierdzenie dotyczące prawdziwości zdania g po zakończeniu
pętli jest tak oczywistym, że często się o nim zapomina.
Jednak dostarcza ono ważnych informacji pozwalających uzasadnić
semantyczną poprawność algorytmów. Dlatego zostało umieszczone w
treści twierdzenia.
Slide 5
k=4;
while(k>=4)
k=k+1;
Slide 6
Przykład 1.
Algorytm NWD Euklidesa.
Zapis w pseudokodzie
Jak znaleźć niezmiennik
pętli?
Slide 7
Najpierw należy pokazać, że
Slide 8
Slide 9
Slide 10
Slide 11
Slide 12
Slide 13
Ćwiczenie
Slide 14
Slide 15
Slide 16
Przykład 2.
Rozważmy algorytm dzielenia całkowitego liczb naturalnych.
void dzielenie (int x,y)
{
//: 0<=x i 0<=y
int q,r;
q=0;
r=x;
while(y<=r)
//p: x=q*y+r i 0<=r i 0<=y
{
q=q+1;
r=r-y;
};
}
//: x=q*y+r i 0<=r
Pokażemy, że algorytm ten jest częściowo poprawny względem
warunku początkowego i końcowego .
Slide 17
Należy udowodnić pewną własność obliczeń algorytmu, która łączy
zachodzenie warunku początkowego z warunkiem końcowym.
Jaki warunek spełniają x, y, q, r w pętli „while” w chwili
sprawdzenia warunku „y<=r” sterującego iteracją?
Określamy niezmiennik p.
Wykażemy, że za każdym razem, gdy obliczenie algorytmu
rozpoczyna się stanem spełniającym warunek początkowy oraz
dochodzi do warunku iteracji, to spełniony jest warunek p.
Slide 18
bezpośrednio z
void dzielenie (int x,y)
początku
{
//: 0<=x i 0<=y
algorytmu
int q,r;
q=0;
r=x;
while(y<=r)
//p: x=q*y+r i 0<=r i 0<=y
{
z pętli
q=q+1;
r=r-y;
};
}
//: x=q*y+r i 0<=r Możemy dojść do p
dwiema drogami:
Slide 19
void dzielenie (int x,y)
{
//: 0<=x i 0<=y
int q,r;
q=0;
r=x;
while(y<=r)
//p: x=q*y+r i 0<=r i 0<=y
{
q=q+1;
r=r-y;
};
}
//: x=q*y+r i 0<=r
Wtedy zostaje wykonana instrukcja
złożona: q’=q+1;r’=r-y.
Trzeba sprawdzić, czy dla q’ i r’
zachodzi warunek p:
p: x=q’*y+r’ i 0<=r’ i 0<=y .
Jeśli dojdziemy do p z
początku algorytmu, to q=0,
r=x i p jest spełniony, bo
zachodzi .
Jeśli już przejdziemy przez
pętlę „while” i dojdziemy do
p, to wiemy, że y<=r i zaszedł
już warunek p: x=q*y+r i
0<=r i 0<=y .
Ale
x=(q+1)*y+(r-y)=q*y+r
0<=r’=r-y, bo y<=r i 0<=y.
Slide 20
void dzielenie (int x,y)
{
//: 0<=x i 0<=y
int q,r;
q=0;
r=x;
while(y<=r)
//p: x=q*y+r i 0<=r i 0<=y
{
q=q+1;
r=r-y;
};
}
//: x=q*y+r i 0<=r
Stosując teraz indukcję
względem liczby wykonanych
sprawdzeń warunku iteracji
„y<=r”, wnioskujemy, że
przy każdym sprawdzeniu
warunku iteracji zachodzi p.
Zatem, albo cały czas zachodzi „y<=r”i wtedy nie dochodzimy
do , albo w pewnej chwili „y>r” i wtedy dochodzimy do , ale
ponieważ p był spełniony, więc musi być spełniony .
Zwróćmy uwagę, że jeśli x=0 i y=0, to obliczenie algorytmu jest
nieskończone, a więc według podanych warunków algorytm jest
tylko częściowo poprawny i ma własność określoności obliczeń,
ale nie ma własności stopu!
Slide 21
Dowodzenie własności stopu
Kryterium liczników iteracji
Kryterium malejących wielkości
Slide 22
Załóżmy, że dany jest algorytm:
M:{
l=c;
while(p)do
{
K;
l=l+1;
}
zmienna l jest
licznikiem iteracji,
służy do
obliczania liczby
wykonań instrukcji
iterowanej K
Dobieramy teraz dwie wielkości:
takie, że l<= oraz takie,
które wyjaśnia zależność między
wartościami zmiennych w chwili
sprawdzania warunku
(niezmiennika) p.
}
Kryterium liczników iteracji
Jeżeli:
1) i >=l jest w algorytmie M niezmiennikiem instrukcji
iteracyjnej „while” przy warunku początkowym ,
2) K ma własność stopu względem i p,
to M oraz „while(p)do K” mają własność stopu względem .
Slide 23
Przykład 3.
!!
void dzielenie1 (int x,y)
{
//1: 0<=x i 0 int q,r;
q=0;
r=x;
while(y<=r)
//p: x=q*y+r i 0<=r i 0 {
q=q+1;
r=r-y;
};
}
//: x=q*y+r i 0<=r
Zmienna q pełni rolę licznika iteracji.
M:{
l=c;
while(p)do
{
K;
l=l+1;
}
}
Slide 24
void dzielenie1 (int x,y)
{
//1: 0<=x i 0 int q,r;
q=0;
r=x;
while(y<=r)
//p: x=q*y+r i 0<=r i 0 {
q=q+1;
r=r-y;
};
}
//: x=q*y+r i 0<=r
M:{
l=c;
while(p)do
{
K;
l=l+1;
}
}
Określmy : x/y oraz : x=q*y+r i r>=0 i 0 Pokażemy, że i q<=x/y jest niezmiennikiem instrukcji iteracyjnej.
Przy wejściu do instrukcji mamy: q=0, r=x, x>=0, y>0, czyli zachodzi
i q<=x/y oraz r>=y.
Wtedy dostajemy nowe wartości: q’=q+1 i r’=r-y.
Slide 25
void dzielenie1 (int x,y)
{
//1: 0<=x i 0 int q,r;
q=0;
r=x;
while(y<=r)
//p: x=q*y+r i 0<=r i 0 {
q=q+1;
r=r-y;
};
}
//: x=q*y+r i 0<=r
M:{
l=c;
while(p)do
{
K;
l=l+1;
}
}
: x/y oraz : x=q*y+r i r>=0 i 0
Ćwiczenie!
Łatwo pokazać, że te nowe zmienne spełniają warunek , a
nierówność q<=x/y wynika z , bo:
x=q*y+r,
y>0
q=x/y-r/y, y>0
(r>=0,y>0)
q<=x/y
Stosując teraz kryterium liczników iteracji wnioskujemy, że algorytm ma
własność stopu względem 1. Ponadto nierówność q<=x/y podaje
ograniczenie na liczbę wykonywanych iteracji.
Slide 26
Załóżmy, że dany jest algorytm:
M:{
i=w+1;
while(p)do
{
i=w;
K;
}
Dobieramy teraz trzy wielkości:
i oraz w będące liczbami
całkowitymi i takie, które
wyjaśnia zależność między
wartościami zmiennych w chwili
sprawdzania warunku
(niezmiennika) p.
}
Kryterium malejących wielkości
Jeżeli:
1) i i>w, i w>=0 jest w algorytmie M niezmiennikiem
instrukcji iteracyjnej „while” przy warunku początkowym ,
2) K ma własność stopu względem i p,
to M oraz „while(p)do K” mają własność stopu względem .
Slide 27
Metodę malejących wielkości stosuje się, gdy w algorytmie zwiększanie
wartości następuje w sposób nieregularny, czyli niekoniecznie o 1.
Zamiast szacować wzrost rozpatruje się jednak te wielkości, które
zmniejszają swoje wartości w trakcie wykonywania algorytmu i dla których
istnieją wartości ograniczające je z dołu.
Slide 28
Przykład 4.
void dzielenie2 (int x,y)
{
//2: 0<=x i 0 int q,r; int i;
q=0;
r=x; i=r+1;
while(y<=r)
//p: x=q*y+r i 0<=r i 0 {
q=q+1; i=r;
r=r-y;
};
}
//: x=q*y+r i 0<=r
M:{
i=w+1;
while(p)do
{
i=w;
K;
}
}
Zmienna r pełni rolę w. Wprowadzamy też pomocniczą zmienną i.
Slide 29
void dzielenie2 (int x,y)
{
//2: 0<=x i 0 int q,r; int i;
q=0;
r=x; i=r+1;
while(y<=r)
//p: x=q*y+r i 0<=r i 0 {
q=q+1; i=r;
r=r-y;
};
}
//: x=q*y+r i 0<=r
M:{
i=w+1;
while(p)do
{
i=w;
K;
}
}
Ustalamy : y>0 i (i=r+1 ∨ i=r+y).
Przy wejściu do instrukcji „while” warunek jest spełniony, bo i=r+1.
Slide 30
void dzielenie2 (int x,y)
{
//2: 0<=x i 0 int q,r; int i;
q=0;
r=x; i=r+1;
while(y<=r)
//p: x=q*y+r i 0<=r i 0 {
q=q+1; i=r;
r=r-y;
};
}
//: x=q*y+r i 0<=r
M:{
i=w+1;
while(p)do
{
i=w;
K;
}
}
: y>0 i (i=r+1 ∨ i=r+y).
Warunek zachowuje się przy każdym wykonaniu instrukcji
iterowanej, bo jeśli i’ i r’ są nowymi wartościami, to i’=r oraz
r’=r-y, czyli i’=r’+y.
Zatem jest niezmiennikiem iteracji.
Ponieważ r>=0, to cały warunek i r=0 jest niezmiennikiem
iteracji.
Na podstawie kryterium malejących wielkości mamy własność stopu
względem 2.
Wprowadzimy teraz pojęcie niezmiennika pętli, które jest często
wykorzystywane do projektowania algorytmów i dowodzenia ich
poprawności. Rozważmy pętlę „while”, która ma postać:
Slide 2
Slide 3
Slide 4
Ostatnie stwierdzenie dotyczące prawdziwości zdania g po zakończeniu
pętli jest tak oczywistym, że często się o nim zapomina.
Jednak dostarcza ono ważnych informacji pozwalających uzasadnić
semantyczną poprawność algorytmów. Dlatego zostało umieszczone w
treści twierdzenia.
Slide 5
k=4;
while(k>=4)
k=k+1;
Slide 6
Przykład 1.
Algorytm NWD Euklidesa.
Zapis w pseudokodzie
Jak znaleźć niezmiennik
pętli?
Slide 7
Najpierw należy pokazać, że
Slide 8
Slide 9
Slide 10
Slide 11
Slide 12
Slide 13
Ćwiczenie
Slide 14
Slide 15
Slide 16
Przykład 2.
Rozważmy algorytm dzielenia całkowitego liczb naturalnych.
void dzielenie (int x,y)
{
//: 0<=x i 0<=y
int q,r;
q=0;
r=x;
while(y<=r)
//p: x=q*y+r i 0<=r i 0<=y
{
q=q+1;
r=r-y;
};
}
//: x=q*y+r i 0<=r
Pokażemy, że algorytm ten jest częściowo poprawny względem
warunku początkowego i końcowego .
Slide 17
Należy udowodnić pewną własność obliczeń algorytmu, która łączy
zachodzenie warunku początkowego z warunkiem końcowym.
Jaki warunek spełniają x, y, q, r w pętli „while” w chwili
sprawdzenia warunku „y<=r” sterującego iteracją?
Określamy niezmiennik p.
Wykażemy, że za każdym razem, gdy obliczenie algorytmu
rozpoczyna się stanem spełniającym warunek początkowy oraz
dochodzi do warunku iteracji, to spełniony jest warunek p.
Slide 18
bezpośrednio z
void dzielenie (int x,y)
początku
{
//: 0<=x i 0<=y
algorytmu
int q,r;
q=0;
r=x;
while(y<=r)
//p: x=q*y+r i 0<=r i 0<=y
{
z pętli
q=q+1;
r=r-y;
};
}
//: x=q*y+r i 0<=r
dwiema drogami:
Slide 19
void dzielenie (int x,y)
{
//: 0<=x i 0<=y
int q,r;
q=0;
r=x;
while(y<=r)
//p: x=q*y+r i 0<=r i 0<=y
{
q=q+1;
r=r-y;
};
}
//: x=q*y+r i 0<=r
Wtedy zostaje wykonana instrukcja
złożona: q’=q+1;r’=r-y.
Trzeba sprawdzić, czy dla q’ i r’
zachodzi warunek p:
p: x=q’*y+r’ i 0<=r’ i 0<=y .
Jeśli dojdziemy do p z
początku algorytmu, to q=0,
r=x i p jest spełniony, bo
zachodzi .
Jeśli już przejdziemy przez
pętlę „while” i dojdziemy do
p, to wiemy, że y<=r i zaszedł
już warunek p: x=q*y+r i
0<=r i 0<=y .
Ale
x=(q+1)*y+(r-y)=q*y+r
0<=r’=r-y, bo y<=r i 0<=y.
Slide 20
void dzielenie (int x,y)
{
//: 0<=x i 0<=y
int q,r;
q=0;
r=x;
while(y<=r)
//p: x=q*y+r i 0<=r i 0<=y
{
q=q+1;
r=r-y;
};
}
//: x=q*y+r i 0<=r
Stosując teraz indukcję
względem liczby wykonanych
sprawdzeń warunku iteracji
„y<=r”, wnioskujemy, że
przy każdym sprawdzeniu
warunku iteracji zachodzi p.
Zatem, albo cały czas zachodzi „y<=r”i wtedy nie dochodzimy
do , albo w pewnej chwili „y>r” i wtedy dochodzimy do , ale
ponieważ p był spełniony, więc musi być spełniony .
Zwróćmy uwagę, że jeśli x=0 i y=0, to obliczenie algorytmu jest
nieskończone, a więc według podanych warunków algorytm jest
tylko częściowo poprawny i ma własność określoności obliczeń,
ale nie ma własności stopu!
Slide 21
Dowodzenie własności stopu
Kryterium liczników iteracji
Kryterium malejących wielkości
Slide 22
Załóżmy, że dany jest algorytm:
M:{
l=c;
while(p)do
{
K;
l=l+1;
}
zmienna l jest
licznikiem iteracji,
służy do
obliczania liczby
wykonań instrukcji
iterowanej K
Dobieramy teraz dwie wielkości:
takie, że l<= oraz takie,
które wyjaśnia zależność między
wartościami zmiennych w chwili
sprawdzania warunku
(niezmiennika) p.
}
Kryterium liczników iteracji
Jeżeli:
1) i >=l jest w algorytmie M niezmiennikiem instrukcji
iteracyjnej „while” przy warunku początkowym ,
2) K ma własność stopu względem i p,
to M oraz „while(p)do K” mają własność stopu względem .
Slide 23
Przykład 3.
!!
void dzielenie1 (int x,y)
{
//1: 0<=x i 0
q=0;
r=x;
while(y<=r)
//p: x=q*y+r i 0<=r i 0
q=q+1;
r=r-y;
};
}
//: x=q*y+r i 0<=r
Zmienna q pełni rolę licznika iteracji.
M:{
l=c;
while(p)do
{
K;
l=l+1;
}
}
Slide 24
void dzielenie1 (int x,y)
{
//1: 0<=x i 0
q=0;
r=x;
while(y<=r)
//p: x=q*y+r i 0<=r i 0
q=q+1;
r=r-y;
};
}
//: x=q*y+r i 0<=r
M:{
l=c;
while(p)do
{
K;
l=l+1;
}
}
Określmy : x/y oraz : x=q*y+r i r>=0 i 0
Przy wejściu do instrukcji mamy: q=0, r=x, x>=0, y>0, czyli zachodzi
i q<=x/y oraz r>=y.
Wtedy dostajemy nowe wartości: q’=q+1 i r’=r-y.
Slide 25
void dzielenie1 (int x,y)
{
//1: 0<=x i 0
q=0;
r=x;
while(y<=r)
//p: x=q*y+r i 0<=r i 0
q=q+1;
r=r-y;
};
}
//: x=q*y+r i 0<=r
M:{
l=c;
while(p)do
{
K;
l=l+1;
}
}
: x/y oraz : x=q*y+r i r>=0 i 0
Ćwiczenie!
Łatwo pokazać, że te nowe zmienne spełniają warunek , a
nierówność q<=x/y wynika z , bo:
x=q*y+r,
y>0
q=x/y-r/y, y>0
(r>=0,y>0)
q<=x/y
Stosując teraz kryterium liczników iteracji wnioskujemy, że algorytm ma
własność stopu względem 1. Ponadto nierówność q<=x/y podaje
ograniczenie na liczbę wykonywanych iteracji.
Slide 26
Załóżmy, że dany jest algorytm:
M:{
i=w+1;
while(p)do
{
i=w;
K;
}
Dobieramy teraz trzy wielkości:
i oraz w będące liczbami
całkowitymi i takie, które
wyjaśnia zależność między
wartościami zmiennych w chwili
sprawdzania warunku
(niezmiennika) p.
}
Kryterium malejących wielkości
Jeżeli:
1) i i>w, i w>=0 jest w algorytmie M niezmiennikiem
instrukcji iteracyjnej „while” przy warunku początkowym ,
2) K ma własność stopu względem i p,
to M oraz „while(p)do K” mają własność stopu względem .
Slide 27
Metodę malejących wielkości stosuje się, gdy w algorytmie zwiększanie
wartości następuje w sposób nieregularny, czyli niekoniecznie o 1.
Zamiast szacować wzrost rozpatruje się jednak te wielkości, które
zmniejszają swoje wartości w trakcie wykonywania algorytmu i dla których
istnieją wartości ograniczające je z dołu.
Slide 28
Przykład 4.
void dzielenie2 (int x,y)
{
//2: 0<=x i 0
q=0;
r=x; i=r+1;
while(y<=r)
//p: x=q*y+r i 0<=r i 0
q=q+1; i=r;
r=r-y;
};
}
//: x=q*y+r i 0<=r
M:{
i=w+1;
while(p)do
{
i=w;
K;
}
}
Zmienna r pełni rolę w. Wprowadzamy też pomocniczą zmienną i.
Slide 29
void dzielenie2 (int x,y)
{
//2: 0<=x i 0
q=0;
r=x; i=r+1;
while(y<=r)
//p: x=q*y+r i 0<=r i 0
q=q+1; i=r;
r=r-y;
};
}
//: x=q*y+r i 0<=r
M:{
i=w+1;
while(p)do
{
i=w;
K;
}
}
Ustalamy : y>0 i (i=r+1 ∨ i=r+y).
Przy wejściu do instrukcji „while” warunek jest spełniony, bo i=r+1.
Slide 30
void dzielenie2 (int x,y)
{
//2: 0<=x i 0
q=0;
r=x; i=r+1;
while(y<=r)
//p: x=q*y+r i 0<=r i 0
q=q+1; i=r;
r=r-y;
};
}
//: x=q*y+r i 0<=r
M:{
i=w+1;
while(p)do
{
i=w;
K;
}
}
: y>0 i (i=r+1 ∨ i=r+y).
Warunek zachowuje się przy każdym wykonaniu instrukcji
iterowanej, bo jeśli i’ i r’ są nowymi wartościami, to i’=r oraz
r’=r-y, czyli i’=r’+y.
Zatem jest niezmiennikiem iteracji.
Ponieważ r>=0, to cały warunek i r=0 jest niezmiennikiem
iteracji.
Na podstawie kryterium malejących wielkości mamy własność stopu
względem 2.