• Nie Znaleziono Wyników

Zły warunek końcowy = wywołania nieskończone

N/A
N/A
Protected

Academic year: 2021

Share "Zły warunek końcowy = wywołania nieskończone "

Copied!
3
0
0

Pełen tekst

(1)

Rekurencja, zwana także rekursją (ang. recursion, z łac. recurrere, przybiec z powrotem) to w logice, programowaniu i w matematyce odwoływanie się np. funkcji lub definicji do samej siebie.

W logice wnioskowanie rekurencyjne opiera się na założeniu istnienia pewnego stanu początkowego oraz zdania (lub zdao) stanowiącego podstawę wnioskowania (przy czym aby cały dowód był poprawny zarówno reguła jak i stan początkowy muszą byd prawdziwe). Istotą rekurencji jest tożsamośd dziedziny i przeciwdziedziny reguły wnioskowania, wskutek czego wynik wnioskowania może podlegad tej samej regule zastosowanej ponownie.

Przykład programu wykorzystującego rekurencje:

PROGRAM REK;

VAR ZMIENNA:BYTE;

FUNCTION SILNIA(N: BYTE):BYTE;

BEGIN

IF(N < 1) THEN SILNIA := 1 ELSE

SILNIA := N * SILNIA(N– 1);

END;

BEGIN

WRITELN(‘PODAJ WARTOSC’);

READLN(ZMIENNA);

WRITELN(‘SILNIA WYNOSI:’, SILNIA(ZMIENNA));

READLN;

END.

Uwaga na niebezpieczeostwo rekursji:

FUNCTION F(A:BYTE):BYTE;

BEGIN

F := 0;

IF(A > -1) THEN F := A * F(A+1);

END;

(2)

Niebezpieczeństwa rekurencji

Programując programy lub funkcje rekurencyjne należy uważać na pewne dodatkowe efekty uboczne, które można niechcący wywołać. Kilka z nich opisano poniżej; praktyka programistyczna dostarczyć może więcej takich przykładów.

Ciąg Fibonacciego

Obliczanie parametrów ciągu tego i innych, podobnych typów, wykonane bezpośrednio na podstawie wzoru matematycznego, może powodować problemy w postaci zbyt wielu dublujących się obliczeń. Ciąg Fibonacciego jest zdefiniowany rekurencyjnie, w sposób analogiczny do definicji funkcji silnia:

F(0) = 1 F(1) = 1 F(n) = F(n-1) + F(n-2)

Zadanie obliczania elementów takiego ciągu można w języku C zapisać niemal dokładnie tak samo, jak wyraża to wzór definicyjny:

unsigned long int FIB( int n ){ if (x<2) return 1; else return FIB(n-1) + FIB(n-2); }

Schemat wywołań rekurencyjnych, jak wykaże prosta analiza kodu, doprowadzi do następującego drzewa wywołań:

Drzewo wywołań funkcji FIB dla parametru 4.

Widać od razu, że gałęzie zaznaczone kolorem wykonują się dwa razy. Zupełnie niepotrzebnie. W sumie, wywołanie funkcji FIB dla większych parametrów n, spowoduje że wykonane zostanie w przybliżeniu 2n obliczeń, co jest z oczywistych powodów nieefektywne. Dlatego należy pamiętać, że nie jest dobrą metodą programowanie rekurencyjne tam, gdzie wystarczą proste funkcje iteracyjne.

(3)

Nieoczywistość kodu

Istnieją przypadki w których program napisany jest poprawnie, jednak przewidywanie sposobu jego działania staje się nieco trudne. Pierwszym takim przykładem była funkcja obliczająca elementy ciągu Fibonacciego, teraz coś jeszcze bardziej dziwnego - funkcja MacCarthy'ego:

unsigned long int MC( int n ){ if (n>100) return n-10; else return MC( MC(n+11) ); }

Analiza kodu doprowadza do zawrotu głowy - funkcja jest jakaś dziwna: oto, dla wartości n większych od 100 zwraca ładnie wartość n-10 - jest to prawidłowe i mające szansę nastąpić zakończenie procesu wywołań rekurencyjnych; dla większych parametrów n zwracane jest wywołanie rekurencyjne z parametrem, którym jest wartość wywołania rekurencyjnego dla wartości n+11. Przebieg wywołań takiej funkcji jest na pierwszy rzut oka trudny do określenia, ale po wnikliwej analizie można dość do wniosku, że:

dla wszystkich n większych od 100 funkcja wykona się tylko raz

dla n równego 100 funkcja wywoła się dwa razy: raz z wartością 111 (n+11), drugi raz z wartością 101 (n-10).

dla n równego 99 funkcja wywoła się: z wartością 110 i 100, z wartością 111 i 101 - cztery razy

dla n równego 98 - sześć razy, itp..., zgodnie z nieścisłą zależnością empiryczną 1+(100-n)*2.

Ilość wywołań przedstawiona na wykresie rzeczywiście potwierdza analizę, chociaż wynik nie jest oczywisty. Stąd właśnie niebezpieczeństwo - można napisać kod, który działa doskonale, ale trudno powiedzieć, co będzie się działo dla niektórych wartości parametrów wejściowych.

Zły warunek końcowy = wywołania nieskończone

Oto kolejny przykład, w którym niedostatecznie przemyślano warunek zakończenia procesu rekurencyjnego.

int SDW( int n ){ if (n==1) return 1; else if( (n%2)==0 ) return n*SDW(n-2); else return n*SDW(n-1); }

Cytaty

Powiązane dokumenty

Dla dodatniej liczby naturalnej n znaleźć wzór na największą potęgę liczby pierwszej p dzielącą n!4. Rozłożyć na czynniki pierwsze

………. c) Ile czasu będzie trwało napełnianie pustej cysterny, jeśli będzie otwarty pierwszy kran, który napełnia cysternę i kran w dnie

Jeżeli n-elementowy układ jest bazą przestrzeni V , to każda baza tej przestrzeni składa się z dokładnie n

Gdy odległość pomiędzy pociągami wynosi 1 km, pszczoła zaczyna latać tam i z powrotem pomiędzy pociągami z prędkością 60 km na godzinę.. Wyrazić od- ległość jaką

Wskaż liczbę rzeczywistą k, dla której podana granica istnieje i jest dodatnią liczbą rzeczywistą.. Podaj wartość granicy dla tej wartości

1 Takie sformułowanie jest zgrabne, chociaż dla jego pełnej poprawności wymagałoby dodania nic nie wnoszącego do rozwiązania zastrzeżenia, że punkt styczności leży na stycznej,

Na potrzeby tego zadania, liczbę naturalną k nazwiemy ładną, jeżeli istnieje liczb naturalna, której kwadrat ma sumę cyfr równą k.. Wiadomo, że wśród 11 kolejnych

Podaj przyk lad grafu, kt´ory nie jest p