Помощ - Търси - Регистрирани - Календар
Пълна версия: Рекурсия
UniBG Forums > Дискусии > Dexter's Lab > Програмиране
Guest
Здравейте! В томи пример за рекурсия за факториел(стека се препълва ако е голямо числото, а ако е отрицателно също, но това е само пример):
[code]
long fact(int i){
if(i == 0) return 1;//Нали като 'i' == 0 връщаме 1, а не резулата??
else return i*fact(i - 1);
}
Вторият ми въпрос е :
Какво ще означава това:
QUOTE
По тази причина е важно да не се презаписват локални масиви от функции; обикновено това води до полреждане на стека, като се унищожава адреса за връщане и програмата зависва от объркване.)
Това какво ще рече:

int m_arr(const int arr[])
{
arr[0] = 1;arr[1] = 2;
return arr;
}
int arr[] = arr;
Въпреки че това е доста абсурдна ситуация...
int21h
За първия въпрос ще кажа, че е нормално да се препълни стека при дълга рекурсия. Ако не искаш да го препълниш ползвай цикъл за големи числа. Колкото до отрицателното число, можеш да си решиш проблема с една проверка.

//Да, при i = 0 връщаме единица.

За втория въпрос.. Не мисля, че мога да го кажа с по-прости думи от тези, които си ползвал в цитата.
Госг
Добре, но как се връща резултата от рекурсията??!?
GUEST
А как се връща резултат при функция, която два пъти вика себе си?!?
int21h
QUOTE(GUEST @ Aug 20 2007, 09:21 PM) *
А как се връща резултат при функция, която два пъти вика себе си?!?


В случая функцията връща резултат числото, което е приела като параметър умножено по резултата, който би върнала ако числото беше с единица по-малко. И така до 0.

Т.е. при 5! резултата би бил..

5 * fact( 4 ), но fact(4) връща 4*fact(3)
т.е. получаваме 5 * 4 * fact(3), но fact(3) връща 3 * fact(2)
т.е. получаваме 5 * 4 * 3 * fact(2), но fact(2) връща 2 * fact(1)
т.е. получаваме 5 * 4 * 3 * 2 * fact(1), но fact(1) връща 1 * fact(0)
т.е. получаваме 5 * 4 * 3 * 2 * 1 * fact(0), но fact(0) връща 1
т.е. получаваме 5 * 4 * 3 * 2 * 1 * 1, което не е проблем, защото колкото и пъти да умножаваме по 1 резултата не се променя.

Винаги в една рекурсия трябва да има условие, което да спре рекурсията, иначе ще се препълни стека. В случая условието е if(i == 0) return 1;
Guest
Мерси МНОГО! bravo.gif
offgeboren
Po-hitri ezici kato Scheme poddurjat "tail recursion" po eleganten nachin, koeto estestveno moje i na C da se implementira i e nachin da ne se pretrupva stack-a.
Poveche info: http://en.wikipedia.org/wiki/Tail_recursion
int21h
QUOTE(offgeboren @ Aug 21 2007, 05:15 AM) *
Po-hitri ezici kato Scheme poddurjat "tail recursion" po eleganten nachin, koeto estestveno moje i na C da se implementira i e nachin da ne se pretrupva stack-a.
Poveche info: http://en.wikipedia.org/wiki/Tail_recursion


Мерси за линка.
Polizei
Грррр... Факториел с рекурсия се изчислява лесно, но все пак имаш само 32 бита което май беше 11! или 12!, не помня точно... Не вярвам че би могъл да препълниш стека при 12 рекурсивни извиквания, дори и при 12000, все пак, да го обясня малко грубо, при извикване на функция от стека се "яде" толкова място, колкото са параметрите на функцията + лоаклните променливи + адреса за обратно връщане. Демек при този код отдолу за всяко извикване на функцията ще се заделят 8 байта. Също така би трябвало да ти прави впечатление че не ползвам 'unsigned int' а само 'int' и по този начин жертвам още 1 бит, но обстоятелствата го налагат за да имаме проверка за отрицателни числа. Ако 'n' беше 'unsigned int' то -1 би значело 4294967295 или 0xFFFFFFFF...
CODE
int fact(int n) {
    if (n < 1) return 1;
    return n*fact(n-1);
}


И да добавя, говоря в 32 бита навсякъде. Със 64 битовите машини още не съм пил кафе и не мога да ги коментирам...
int21h
QUOTE(Polizei @ Sep 28 2007, 05:46 AM) *
Грррр... Факториел с рекурсия се изчислява лесно, но все пак имаш само 32 бита което май беше 11! или 12!, не помня точно... Не вярвам че би могъл да препълниш стека при 12 рекурсивни извиквания, дори и при 12000, все пак, да го обясня малко грубо, при извикване на функция от стека се "яде" толкова място, колкото са параметрите на функцията + лоаклните променливи + адреса за обратно връщане. Демек при този код отдолу за всяко извикване на функцията ще се заделят 8 байта. Също така би трябвало да ти прави впечатление че не ползвам 'unsigned int' а само 'int' и по този начин жертвам още 1 бит, но обстоятелствата го налагат за да имаме проверка за отрицателни числа. Ако 'n' беше 'unsigned int' то -1 би значело 4294967295 или 0xFFFFFFFF...
CODE
int fact(int n) {
    if (n < 1) return 1;
    return n*fact(n-1);
}


И да добавя, говоря в 32 бита навсякъде. Със 64 битовите машини още не съм пил кафе и не мога да ги коментирам...


Разбира се, че с 12 извиквания на горепосочената функция няма да се препълни стека, но не можеш да не признаеш, че когато ти си учил що е то стек или рекурсия и ти си започнал с този пример. Това е класическия пример за рекурсия. Нормално е учениците да започват с него. Току виж му хареса програмирането и започне да пише рекурсивни функции, които ще препълват стека с 12 извиквания. :)
Polizei
QUOTE(int21h @ Oct 1 2007, 08:08 PM) *
Разбира се, че с 12 извиквания на горепосочената функция няма да се препълни стека, но не можеш да не признаеш, че когато ти си учил що е то стек или рекурсия и ти си започнал с този пример. Това е класическия пример за рекурсия. Нормално е учениците да започват с него. Току виж му хареса програмирането и започне да пише рекурсивни функции, които ще препълват стека с 12 извиквания. smile.gif

Било е преди 5-6 години... След това се хванах с асемблер и нещата придобиха коренно различен смисъл wink.gif
А лично моя съвет е да не се ползва рекурсия, тъй като един добре написан цикличен алгоритъм би бил по-бърз и по-добър от един рекурсивния.
Това е семпла версия на форума. За да видиш пълната версия, която има повече информация, по-добра подредба и снимки, натисни тук.
Invision Power Board © 2001-2008 Invision Power Services, Inc.