Matematyka Dyskretna
Andrzej Szepietowski
25 czerwca 2002 roku
Rozdział 1
Rekurencja
1.1
Wie˙ze Hanoi
Rekurencja jest to zdolno´s´c podprogramu (procedury lub funkcji) do wywoływania
sa-mego siebie. Zacznijmy od przykładu wie˙z Hanoi. Przypu´s´cmy, ˙ze mamy trzy wie˙ze lub trzy paliki: , i . Na pierwszym paliku, , znajduj¸a si¸e trzy kr¸a˙zki ró˙znej wielko´sci,
nanizane w porz¸adku od najwi¸ekszego na dole do najmniejszego na górze. Paliki i
s¸a na pocz¸atku puste. Nale˙zy przenie´s´c wszystkie kr¸a˙zki z palika na , posługuj¸ac si¸e
w razie potrzeby palikiem , według nast¸epuj¸acych reguł:
mo˙zna przenosi´c po jednym kr¸a˙zku z jednego palika na inny,
nie mo˙zna umieszcza´c wi¸ekszego kr¸a˙zka na mniejszym.
Rozwi¸azaniem tej łamigłówki dla trzech kr¸a˙zków jest nast¸epuj¸acy ci¸ag siedmiu przeło-˙ze´n: gdzie zapis
oznacza przeniesienie szczytowego kr¸a˙zka z palika na palik
. Chodzi nam teraz o algorytm, który dla dowolnej liczby kr¸a˙zków wypisze ci¸ag
operacji potrzebnych do przeło˙zenia kr¸a˙zków z palika na palik .
Algorytm przekładania kr¸a˙zków
je˙zeli , to przekładamy ten jeden kr¸a˙zek
,
je˙zeli , to:
przekładamy kr¸a˙zków z na pomocniczy palik (posługuj¸ac si¸e w
razie potrzeby palikiem ),
przekładamy -ty kr¸a˙zek z na ,
przekładamy kr¸a˙zków z na (za pomoc¸a palika ).
Nietrudno zauwa˙zy´c, ˙ze je˙zeli proces przekładania kr¸a˙zków jest prawidłowy, to
cały proces te˙z jest prawidłowy, poniewa˙z obecno´s´c najwi¸ekszego kr¸a˙zka na dole palika nie przeszkadza w przekładaniu mniejszych kr¸a˙zków.
Powy˙zszy algorytm opiszmy teraz za pomoc¸a rekurencyjnej procedury przenie´s, któ-ra odwołuje si¸e sama do siebie i wypisuje ci¸ag instrukcji przeniesienia kr¸a˙zków z palika na palik
.
procedura przenie´s( kr¸a˙zków z na
, za pomoc¸a ): je˙zeli , to wypisz , je˙zeli , to przenie´s( kr¸a˙zków z na , za pomoc¸a ), wypisz , przenie´s( kr¸a˙zków z na , za pomoc¸a ).
Rysunek 1.1: Schemat działania procedury przenie´s dla wie˙z Hanoi z trzema kr¸a˙zkami
Na rysunku 8.1 zilustrowano działanie procedury przenie´s dla
. Skrót
oznacza wywołanie procedury przenie´s( kr¸a˙zków z na
, za pomoc¸a
). Strzałki w dół oznaczaj¸a wywołanie procedury, strzałki w gór¸e powroty po wykonaniu procedu-ry, a strzałki poziome odpowiadaj¸a nast¸epstwu instrukcji w ramach jednego wykonania procedury.
1.2. Algorytm Euklidesa, wersja rekurencyjna 5
1.2
Algorytm Euklidesa, wersja rekurencyjna
Innym przykładem algorytmu rekurencyjnego mo˙ze by ´c rekurencyjna wersja algorytmu Euklidesa, który oblicza najwi¸ekszy wspólny dzielnik dwóch dodatnich liczb naturalnych
i : fun¯kcja NWD(a,b): je˙zeli , to , je˙zeli , to , je˙zeli , to .
W j¸ezyku Pascal powy˙zsz¸a procedur¸e mo˙zemy zapisa´c w nast¸epuj¸acy sposób:
function NWD(a,b:integer):integer; begin
if a=b then NWD:=a else if
then NWD:=NWD(a-b,b) else NWD:=NWD(a,b-a) end;
Rysunek 1.2: Schemat działania rekurencyjnej wersji algorytmu Euklidesa NWD(5,3):=1
NWD(2,3):=1
NWD(2,1):=1
NWD(1,1):=1
Na rysunku 8.2 przedstawiono proces obliczania funkcji NWD(7,3).
1.3
Funkcje rekurencyjne
Czasami wygodnie jest zdefiniowa´c funkcje za pomoc¸a wzoru rekurencyjnego. Na przy-kład funkcj¸e silnia definiuje si¸e zwykle za pomoc¸a nast¸epuj¸acych dwóch równa ´n:
Podobnie mo˙zna definiowa´c inne funkcje ze zbioru liczb naturalnych w zbiór liczb natu-ralnych. Definicja taka zawiera przepis, jak policzy´c warto´s´c funkcji dla warto´sci pocz¸atkowych, oraz drugi przepis, jak wyliczy´c warto´s´c dla argumentu
za pomoc¸a warto´sci funkcji dla mniejszych argumentów.
1.4
Funkcja (ci¸ag) Fibonacciego
Nast¸epnym przykładem rekurencyjnego definiowania funkcji jest funkcja Fibonacciego, okre´slona równaniami: !
Kolejne warto´sci funkcji Fibonacciego to:
!!!
Udowodnimy teraz przez indukcj¸e, ˙ze
(1.1) gdzie " oraz "
Równo´s´c (1.1) zachodzi dla i
. Załó˙zmy teraz, ˙ze zachodzi dla wszystkich
argumentów mniejszych od
. Zauwa˙zmy, ˙ze
oraz s¸a rozwi¸azaniami równania , mamy wi¸ec oraz a tak˙ze oraz
. Łatwo teraz mo˙zna pokaza´c, ˙ze
. Poniewa˙z , mamy , wi¸ec warto´s´c jest równa !
po zaokr¸agleniu do najbli˙zszej liczby naturalnej i funkcja Fibonacciego ro´snie
wykładniczo.
1.5
Funkcja Ackermanna
Funkcja Ackermanna okre´slona jest, dla liczb naturalnych
#" , nast¸epuj¸acymi rów-naniami: $" &% " #" $" $" "!
Funkcja Ackermanna jest przykładem funkcji maj¸acej do´s´c prost¸a definicj¸e, ale jest prak-tycznie nieobliczalna z tego powodu, ˙ze jej warto´sci szybko rosn¸a. Na przykład:
) +*
1.6. Algorytm sortowania przez scalanie 7 ) ) ' ) ) ) i ogólnie ) !
1.6
Algorytm sortowania przez scalanie
Zajmijmy si¸e teraz pewnym prostym rekurencyjnym algorytem sortowania ci¸agu. Dla prostoty załó˙zmy, ˙ze długo´s´c ci¸agu jest pot¸eg¸a dwójki.
Algorytm sortowania
je˙zeli ci¸ag ma długo´s´c jeden, to jest ju˙z posortowany,
je˙zeli ci¸ag jest dłu˙zszy, to:
dzielimy go na połowy,
sortujemy te połowy,
ł¸aczymy posortowane połowy w jeden posortowany ci¸ag.
Istota pomysłu polega na tym, ˙ze mo˙zna szybko poł¸aczy ´c dwa posortowane ci¸agi w jeden posortowany ci¸ag. Algorytm ł¸aczenia wyja´snijmy na przykładzie. Przypu´s´cmy, ˙ze mamy dwa ci¸agi: '
i ˙ze chcemy je poł¸aczy´c w posortowany ci¸ag . Na pocz¸atku ci¸ag jest pusty.
Usta-wiamy dwa wska´zniki po jednym na pocz¸atku ka˙zdego ci¸agu (wskazane elementy b¸ed¸a oznaczone daszkiem): ' !
Porównujemy wskazane elementy. Mniejszy z porównanych elementów przepisujemy na ci¸ag i przesuwamy wska´znik w tym ci¸agu, z którego był wzi¸ety element do ci¸agu .
W wyniku otrzymamy: ' !
Powtarzamy ten proces tak długo, a˙z w jednym z ci¸agów ostatni element zostanie zabrany do ci¸agu . ' ' ' ' ' '
W takiej sytuacje pozostałe elementy tego drugiego ci¸agu przenosimy do ci¸agu : ' ' !
Liczba porówna ´n potrzebna do scalenia ci¸agów nie jest wi¸eksza od sumy długo´sci tych ci¸agów.
Algorytm merge-sort (inaczej — sortowanie przez scalanie), który sortuje ci¸ag , mo˙zna rekurencyjnie opisa´c tak (rysunek 8.3):
merge-sort(S):
je˙zeli ma tylko jeden element, to koniec,
je˙zeli ma wi¸ecej elementów, to:
dzielimy ci¸ag na połowy i
, merge-sort( ), merge-sort( ), ł¸aczymy i .
Rysunek 1.3: Schemat działania algorytmu merge-sort dla ci¸agu 3, 7, 5, 2, 6, 1, 8, 4 3752|6186 37|52 61|84 3|7 5|2 6|1 8|4 3 7 5 2 6 1 8 4 37 25 16 48 2357 1468 12345678
Algorytm merge-sort jest przykładem algorytmu typu „dziel i zwyci¸e˙zaj”, którego ogólny schemat wygl¸ada tak:
1.7. Rozwi¸azywanie równa ´n i nierówno´sci rekurencyjnych 9
je˙zeli problem jest du˙zy, to:
dzielimy problem na podproblemy,
rozwi¸azujemy podproblemy,
ł¸aczymy rozwi¸azania podproblemów w rozwi¸azanie całego problemu.
1.7
Rozwi¸azywanie równa ´
n i nierówno´
sci rekurencyjnych
Oszacujmy teraz czas działania algorytmu sortowania przez scalanie. Niech
oznacza liczb¸e operacji porównania potrzebn¸a do posortowania ci¸agu długo´sci
. Mamy nast¸epuj¸ace oszacowania: (1.2) ! (1.3)
Druga zale˙zno´s´c wynika st¸ad, ˙ze aby posortowa´c ci¸ag długo´sci
, sortujemy dwa ci¸agi długo´sci
, a nast¸epnie potrzebujemy
porówna ´n, aby scali´c te dwie połówki. Dla pro-stoty rozwa˙za ´n zakładamy tutaj, ˙ze
jest pot¸eg¸a dwójki,
dka jakiego´s naturalnego
.
Istnieje wiele sposobów wyliczania lub szacowania funkcji okre´slonej rekurencyjnie. Przedstawimy teraz dwa najprostsze z nich: metod¸e podstawiania i metod¸e iteracji.
1.8
Metoda podstawiania
W metodzie podstawiania odgadujemy rozwi¸azanie albo jego oszacowanie, a nst¸epnie pokazujemy, ˙ze jest ono poprawne. Poka˙zemy działanie tej metody szacuj¸ac zło˙zono´s´c czasow¸a merge-sortu okre´slon¸a rekurencj¸a (1.2)-(1.3). Zgadujemy, ˙ze
dla jakiej´s stałej
i udowodnimy przez indukcj¸e, ˙ze powy˙zsza nierówno´s´c
zacho-dzi dla ka˙zdego pot¸egi dwójki
. Zachodzi ona dla
. Zakładamy teraz, ˙ze
i podstawiamy do nierówno´sci (1.3)
Ostatnia nierówno´s´c jest spełniona, je˙zeli
.
Metoda podstawiania została zastosowana w podrozdziale o funkcji Fibonacciego do pokazania, ˙ze (1.4)
ale tam odgadni¸eto dokładne rozwi¸azanie. Teraz poka˙zemy jak doj´s´c do rozwi¸aznia zaczynaj¸ac od ogólniejszej postaci. Zacznijmy od równania
! (1.5)
Sprawd´zmy, jakie funkcje postaci
,
spełniaj¸a to równanie. Po podstawieniu do równania (1.5) mamy
Po podzieleniu stromnami przez
, otrzymamy "!
Jest to równanie kwadratowe z dwoma rozwi¸azaniami oraz
, czyli dwie funkcje
oraz spełniaj¸a równanie (1.5). Zauwa˙zmy, ˙ze tak˙ze ka˙zda funkcja postaci
gdzie i
to stałe, spełnia równanie (1.5). Sprawd´zmy teraz, dla jakich stałych i
funkcja spełnia zale˙zno´sci
i
. Otrzymujemu układ równo´sci
Powy˙zszy układ jest spełniony dla
Tak wi¸ec otrzymujemy wzór na funkcj¸e Fibonacciego
(1.6)
1.9
Metoda iteracyjna
Moteda iteracyjna polega na rozwijaniu rekursji. Jako pierwszy przykład rozwa˙zmy za-le˙zno´s´c. '
Dla uproszczenia zakładamy, ˙ze
jest pot¸eg¸a '
, dla jakiego´s
naturalnego. Funkcj¸e rozwijamy w nast¸epuj¸acy sposób:
1.10. Metoda rekurencji uniwersalnej 11 ' , ' , ' ' , ' !!! ' , !!! '
Iteracj¸e powtarzamy, a˙z ostatni składnik b¸edzie zawierał
, czyli gdy * . Otrzymamy wtedy ' , !!! ' ' Skorzystali´smy tu z równo´sci
oraz z faktu, ˙ze *
i .
Jako drugi przykład rozwa˙zmy rekursj¸e
Jak b¸edziemy j¸a rozwija´c, to otrzymamy
' ' !!! !!! Dla . Otrzymamy wtedy ) ) (1.7)
1.10
Metoda rekurencji uniwersalnej
Przypu´s´cmy, ˙ze mamy równanie rekurencyjne
(1.8)
gdzie i
. Równanie takie otrzymamy szacuj¸ac czas działania algorytmu
rekurencyjnego, który metod¸a "dziel i rz¸ad´z" dzieli problem na podproblemów rozmiaru
. Funkcja
opisuje czas potrzebny na podzielenie problemu na podproblemy i na poł¸aczenie rozwi¸aza ´n podproblemów w rozwi¸azanie całego problemu.
Na koniec podamy bez dowodu twierdzenie mówi¸ace jak mo˙zna szacowa ´c funkcj¸e
okre´slon¸a równaniem (1.8).
Twierdzenie 1.1 (o rekurencji uniwersalnej) Niech
b¸edzie okre´slone dla nieujem-nych liczb całkowitych równaniem rekurencyjnym
" gdzie , i " oznacza " lub . Wtedy Je˙zeli
dla pewnej stałej , to
. Je˙zeli , to . Je˙zeli
dla pewnej stałej oraz
dla pewnej stałej , to .
1.11
Zadania
1. Napisz program, który rekurencyjnie oblicza: a) funkcj¸e silnia, b) funkcj¸e Fibonac-ciego, c) funkcj¸e wykładnicz¸a
.
2. Napisz program, który oblicza symbol Newtona: a) według wzoru rekurencyjnego
b) według wzoru ! Porównaj te programy.
3. Napisz program, który rekurencyjnie oblicza funkcj¸e:
! 4. Oblicz '
według rekurencyjnego algorytmu Euklidesa. 5. Według algorytmu ł¸aczenia poł¸acz ci¸agi
i ' .
1.11. Zadania 13 6. Stosuj¸ac algorytm merge-sort posortuj ci¸ag słów: słowik, wróbel, kos, jaskółka,
kogut, dzi¸ecioł, gil, kukułka, szczygieł, sowa, kruk, czubatka. [Fragment wiersza Ptasie radio Juliana Tuwima]
7. Dana jest funkcja
: "! Oblicz . Co oblicza funkcja ?
8. Dana jest funkcja
: ! Oblicz ' . Co oblicza funkcja ?
9. Wypisz ci¸ag przeło˙ze ´n potrzebnych do przeniesienia czterech kr¸a˙zków na wie˙zach Hanoi.
10. Udowodnij, ˙ze algorytm opisany w podrozdziale 8.1 wymaga
przeło˙ze´n do
przeniesienia