Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1] 2  Все   Вниз
  Печать  
Автор Тема: Простецкая загвоздка с рекурсией.  (Прочитано 45568 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Taurus727
Постоялец

ru
Offline Offline
Пол: Мужской

« : 15-05-2007 15:14 » 

В общем в справочнике такой пример рекурсии - вычисление факториала числа :
Код:
int factr (int n) {
int answer;
if (n==1) return (1);
answer = factr(n-1)*n;
return (answer);
}
Все понятно вроде логически. Но я не вижу явного изменения числа n. Разве вызов factr(n-1) изменяет переменную? Как я вижу эта функция будет бесконечно вызывать сама себя если ее не вызвать с параметром 1? Или переменная каким-то образом меняется? Или опечатка в книге? Только не бейте чайником по голове за дурацкий вопрос. Спасибо Улыбаюсь
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #1 : 15-05-2007 15:37 » 

 factr(n-1)
передаёт в функцию уменьшенное на 1 текущее значение n )
Записан

Taurus727
Постоялец

ru
Offline Offline
Пол: Мужской

« Ответ #2 : 15-05-2007 15:50 » 

Ну а при следующем вызове? Вызываем с параметром 3. В функцию идет 2 (n-1). Дальше условие иф не проходит. Новый вызов. А переменная n все также 3. И мы опять имеем вызов с 2. Или вызов factr(n-1) уменьшает значение переменной? Непонятно тогда каким образом хм... Это ведь не присваивание.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #3 : 15-05-2007 16:05 » 

Taurus727, не, это же не статическая переменная.

в каждом вызове функции выделяется новая память под n. Тем и опасны рекурсии, если не задать условия тормоза ) Да и вернюю границу переменных иногда надо ограничить , а то не хватит стека )

сначала n==5.
после нового вызова n=5-1==4
после нового вызова n=4-1==3
...
Записан

Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #4 : 15-05-2007 16:10 » 

а зачем тебе изменённое n?
возвращает же функция answer

n функция получает через параметр
значит что ?
что если в первом вызове n=3, то во второй вызов n придёт равным n-1 т.е. 2
и внутри второго вызова функции она будет n=2

что бы понять рекурсию надо понать рекурсию Улыбаюсь
понятно? Улыбаюсь вот и хорошо
а теперь реализуй рекурсию на шаблонах Улыбаюсь
Записан

Странно всё это....
Taurus727
Постоялец

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 15-05-2007 16:15 » 

То есть когда передаем параметр factr(n-1) то новый вызов функции захватывает момент, когда эта переменная как бы уменьшается, копия происходит уменьшенного числа n и получается что копия переменной неявно, но уменьшилась, а на выходе из рекурсии полной по идее тогда n возвращется на первое значение? Ну и естественно разрушается при return так как она локальная...
Записан
Taurus727
Постоялец

ru
Offline Offline
Пол: Мужской

« Ответ #6 : 15-05-2007 16:19 » 

Кстати... Спасибо, ребят, вы тут терпеливые и вежливые, я уже сам себя, кажется, горазд с этими вопросами послать куда подальше Улыбаюсь надеюсь это правило тут на вашем форуме, а не исключение Улыбаюсь
Записан
Sla
Модератор

ua
Offline Offline
Пол: Мужской

WWW
« Ответ #7 : 15-05-2007 16:23 » 

по буквам Улыбаюсь

объявляешь функцию
int factr (int n) {
int answer;

проверяешь конец рекурсии
if (n==1) return (1);

а вот тут-то и самое интересное:
вот этот ансвер вернется самым последним
answer =
а это тоже ансвер, только не от n, а от n-1, но это таже самая функция
factr(n-1)
*n;

вот здесь возращем
return (answer);
}

и где здесь
новый вызов функции захватывает момент?
Записан

Мы все учились понемногу... Чему-нибудь и как-нибудь.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #8 : 15-05-2007 16:24 » 

когда эта переменная как бы уменьшается, копия происходит уменьшенного числа n и получается что копия переменной неявно, но уменьшилась,

да всё гораздо проще )

представь, что 3 итерации рекурсии выглядят так -
Код:

factr_3(3);

//--------

int factr_3(int n)
{
  //n==3
  return factr_2(n-1);
}

int factr_2(int n)
{
  //n==2
  return factr_1(n-1);
}

int factr_1(int n)
{
  //n==1
  return factr_0(n-1);
}
так же и в рекурсивных вызовах, только код один и тот же, а переменные лежат в разных ячейках памяти
Записан

Taurus727
Постоялец

ru
Offline Offline
Пол: Мужской

« Ответ #9 : 15-05-2007 16:30 » 

Жесть... Вкуриваю... Отлично
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #10 : 15-05-2007 17:43 » 

Taurus727, здесь n - параметр ф-ии. параметры передаются по значению. Т.е. передается просто число. Т.ч. с перекуром повремени Ага
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Taurus727
Постоялец

ru
Offline Offline
Пол: Мужской

« Ответ #11 : 15-05-2007 18:12 » 

Пардон?
Записан
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #12 : 16-05-2007 05:22 » 

Taurus727, значится так
запись
int factr(int n) говорить о том, что паремер n будет передан по значению,
что собственно это означает?
Это значит, что если ты вызовешь функцию вот таким макаром
factr(n+z+y*1024-f)
то
перед вызовом будет вычислено значение n+z+y*1024-f
затем это значение будет засуното в стек, после чего вызовится функция
функция в свою очередь(очередной) сохранённое значени в стеке(стек конкретного вызова, а не какой-то разделямый между вызовами стек) вытащит из стека и засунет в переменную n
т.е. функции пополам каким образом было вычислено переданное значение главное, что оно было засунуто в стек

теперь пример как это работает в твоей функции

первый вызов

int res = factr(3);
что тут происходит(в общем случае, без оптимизации, inline и прочей фигни)?
1. число 3 заносится в стек
2. проиходит вызов функции
3. функция вытаскивает 3 из стека и заносит в переменную n
4. функция, что-то делает
5. результат вычислений опять засовыет в стек
6. выходим из функции
7. значение из стека вытаскивается и засовывается в res

Код:
int factr (int n) {
// тут у нас n==3
int answer;
// проверяем, что n==1, если это, так, то 1 засовываем в стек и возвращаем управление
if (n==1) return (1);
/*вычисляется значение n-1==2, засовывается в стек, вызывается фунция,
она вытягивает из стека 2 засовывает в n, вычисляет результат, засовывает
в стек, возвращает управление, результат из стека вытаскивается,
умножается на n==3 и результат помещается в answer,
что происходит внутри factr(n-1) уже обяснять думаю не надо :)
*/
answer = factr(n-1)*n;
//answer копируется в стек, и возвращается управление(выход из функции)
return (answer);
}
кстати если написать
вместо
answer = factr(n-1)*n;
return (answer);
вот это
return factr(n-1)*n;
то из бавишься от одного копирования лишнего копирования Улыбаюсь
вообще можно функцию сократить до такой Улыбаюсь
Код:
int factr (int n) {return n==1 ? 1 : factr(n-1)*n;}
« Последнее редактирование: 16-05-2007 05:24 от LogRus » Записан

Странно всё это....
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #13 : 16-05-2007 05:49 » 

>>вообще можно функцию сократить до такой

но не нужно ) Читабельность падает
Записан

Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #14 : 16-05-2007 05:54 » 

Алексей1153++, для кого, как для меня она прекрастно читаема и намного проще в восприятии что то что было раньше
особенно вот в таком виде
int factr (int n)
{
    return n==1 ? 1 : factr(n-1)*n;
}
если 1, то 1, иначе посчитаем еще что-то, и всё
полностью отражает смысл без лишних переменных сравнений и прочего
Записан

Странно всё это....
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #15 : 16-05-2007 06:00 » 

LogRus, Улыбаюсь нечитабельный стиль кода - дело хозяйское, конечно, но протрассируй в дебаге вот это чудо

Код:
int factr (int n){return n==1 ? 1 : factr(n-1)*n;}

и это
Код:
int factr (int n)
{
   if(n==1)return 1;
  
   return factr(n-1)*n;
}


и ты прочувствуешь разницу )

а где там лишние сравнения, кстати ?
« Последнее редактирование: 16-05-2007 06:01 от Алексей1153++ » Записан

Taurus727
Постоялец

ru
Offline Offline
Пол: Мужской

« Ответ #16 : 16-05-2007 06:35 » 

Запустил в debug код (в самой длинной записи Улыбаюсь ) и на каждую строку поставил точку прерывания. И проследил изменение переменных. Действительно. Все так и есть. Но боюсь  буду рекурсию использовать редко Отлично  итерация ближе моему мозгу видимо Улыбаюсь хотя посмотрим Улыбаюсь всем спасибо Улыбаюсь
Записан
Sla
Модератор

ua
Offline Offline
Пол: Мужской

WWW
« Ответ #17 : 16-05-2007 06:36 » 

но здесь и с кодом надо поиграться Улыбаюсь
1. уже было замечено - что будет если n достаточно большое?
   надо что-то делать
2. что будет если
   n=0?
   надо что-то делать
3. что будет если n<0 (почти п.2)
   надо что-то делать

Т.е. для правильной работы данной функции нужно вводить еще и флаг ошибки

Что будет?

int n;
res=factr(n);

n -не определена, возможно что угодно, диагностика ошибки обязательна
Записан

Мы все учились понемногу... Чему-нибудь и как-нибудь.
Taurus727
Постоялец

ru
Offline Offline
Пол: Мужской

« Ответ #18 : 16-05-2007 06:53 » 

Ну все ошибки тут никто не ловил. Кстати даже когда вызываешь функцию с параметром 120 уже ответ выходит за границы int потом его кидает в минус. А потом возвращает 0 в итоге. Так что функция эта чисто для примера что есть рекурсия Улыбаюсь
Записан
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #19 : 16-05-2007 07:15 » 

Алексей1153++, не волнуйся я отлажусь в одной строчке Улыбаюсь
я отлажывался даже в макросе который разваричавался в фигительного размера функцию Улыбаюсь

Sla, это же функция очень похожа на C и следовательно её поведение должно быть похоже на поведение atoi
Код:
unsigned int factr (unsigned int n)
{
   return n>1 ? factr(n-1)*n : n;
}
маленькая и красивая Улыбаюсь
Записан

Странно всё это....
Taurus727
Постоялец

ru
Offline Offline
Пол: Мужской

« Ответ #20 : 16-05-2007 07:27 » 

За вами надо в тетрадку записывать Улыбаюсь чем я и займусь. Улыбаюсь
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #21 : 16-05-2007 07:57 » 

LogRus, красиво, когда

n ? 10 : 20

а у тебя - ребячество Ага
Записан

Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #22 : 16-05-2007 08:20 » 

За вами надо в тетрадку записывать Улыбаюсь чем я и займусь. Улыбаюсь
Валяй. Улыбаюсь Тебе полезно, нам приятно. Улыбаюсь
Записан

Странно всё это....
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #23 : 16-05-2007 08:28 » 

Но боюсь  буду рекурсию использовать редко Отлично  итерация ближе моему мозгу видимо

"Итерация свойственна человеку, рекурсия божественна."
Л. Питер Дойч

Подожди, вот до "древесной" структуры доберёшься, там без рекурсии никуда. Ага


Спасибо, ребят, вы тут терпеливые и вежливые, я уже сам себя, кажется, горазд с этими вопросами послать куда подальше Улыбаюсь надеюсь это правило тут на вашем форуме, а не исключение Улыбаюсь

Это дух клуба!
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
nikedeforest
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #24 : 16-05-2007 09:52 » 

Бывают задачи, решение которых интуитивно толкает на рекурсию и надо приложить немало умственных усилий, чтобы реализовать данную задачу через итерацию. Рекурсию всем хороша, но в ней один большой недостаток, озвученный выше - в случае большой глубины, а также большого кол-ва параметров функции (а также локальных переменных ) стэк переполняется. Получается, что для многих задач надо заранее просчитывать, хватит ли стэка на худший случай.
Классным примером рекурсии может служить алгоритм закраски замкнутой облатси области, предстьавленный ниже.
Код:
void Paint(int x, int y, RGB col_background, RGB col_border)
{
  if(getpixel(x,y)!=col_border)
  {
     putpixel(x,y,col_background);
     Paint(x+1,y,col_background,col_borde);
     Paint(x-1,y,col_background,col_borde);
     Paint(x,y+1,col_background,col_borde);
     Paint(x,y-1,col_background,col_borde);
     Paint(x+1,y+1,col_background,col_borde);
     Paint(x+1,y-1,col_background,col_borde);
     Paint(x-1,y+1,col_background,col_borde);
     Paint(x-1,y-1,col_background,col_borde);
  }
return;
}
А также с помощью данной функции видно каким критическим ресурсом явялется стэк. Такая функция закрашивает только маленькие области Улыбаюсь. Экран с разрашением 600х800 она не зальет при всем ее желании Улыбаюсь .
Записан

ещё один вопрос ...
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #25 : 16-05-2007 10:01 » 

>>Экран с разрашением 600х800 она не зальет при всем ее желании

а кроме того, даже если стека хватит, будет делать это медленно )
Записан

nikedeforest
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #26 : 16-05-2007 10:06 » 

Кстати неправда. Или точнее спросить - медленнее чем что? Алгоритм полинейной закраски не был быстрее.
Записан

ещё один вопрос ...
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #27 : 16-05-2007 10:14 » 

nikedeforest, со стеком много работы
Записан

Taurus727
Постоялец

ru
Offline Offline
Пол: Мужской

« Ответ #28 : 16-05-2007 10:32 » 

Простите, а getpixel() и putpixel() в какой библиотеке есть в Visual Studio 2005? Я бы ваш примерчик обкатал...
Записан
nikedeforest
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #29 : 16-05-2007 10:38 » 

Вы перешли на Visual Studio? Дело хозяйское, просто я начинал с Борланда 3.1. под ДОС который. Ну да ладно. А собираетесь консольное приложение делать или с GUI?
Записан

ещё один вопрос ...
Страниц: [1] 2  Все   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines