2010-06-21 2 views
10

В настоящее время я работаю в PHP, поэтому этот пример будет в PHP, но вопрос относится к нескольким языкам.Что представляет собой практическое различие между циклом и рекурсией

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

Теперь я хотел рассказать ему о различии между циклом и рекурсией, но я не мог найти решение, в котором вам нужна рекурсия по нормальному циклу.

Я собираюсь сделать упрощенную версию обоих, я надеюсь, что кто-то может объяснить, как каждый отличается от другого.

Пожалуйста, прости меня за любые ошибки кодирования

Петля:

printnumbers(1,10); 

public function printnumbers($start,$stop) 
{ 
    for($i=$start;$i<=$stop;$i++) 
    { 
     echo $i; 
    } 
} 

Теперь код выше просто печатает цифры.

Теперь давайте делать это с помощью рекурсии:

printnumbers(1,10); 

public function printnumbers($start,$stop) 
{ 
    $i = $start; 
    if($i <= $stop) 
    { 
     echo $i; 
     printnumbers($start+1,$stop); 
    } 
} 

Этот метод выше будет делать одно и то же, как контур, но только с помощью рекурсии.

Может ли кто-нибудь объяснить мне, что существует, в отличие от использования одного из этих методов.

+0

Хмм, почему вы считаете назад во втором фрагменте кода? –

+0

Ой, прости, это просто опечатка, исправлена! –

+0

Я думаю, что это одна из причин быть осторожным в отношении рекурсивных функций: они могут быть труднодоступными и иногда ведут к этому самому сайту. –

ответ

12

Петли и рекурсии во многом эквивалентны. Нет необходимости в программах, или в принципе, в принципе вы всегда можете переводить из циклов в рекурсию или наоборот.

Рекурсии более мощные в том смысле, что для перевода рекурсии в цикл может потребоваться стек, с которым вы должны манипулировать собой. (Попробуйте пересечь двоичное дерево, используя цикл, и вы почувствуете боль.)

С другой стороны, многие языки (и реализации), например Java, не реализуют хвостовую рекурсию должным образом. Рекурсия хвоста - это когда последнее, что вы делаете в функции, - это называть себя (как в вашем примере). Такая рекурсия не должна потреблять какой-либо стек, но на многих языках, что означает, что вы не всегда можете использовать рекурсию.

+0

Проблема в тех языках, которые не поддерживают хвостовую рекурсию, заключается в том, что стек часто довольно мал, что вызывает переполнение стека на больших структурах. В языках Java или C# вы должны использовать явный стек вместо рекурсии (на больших или неизвестных структурах). – Moberg

+0

Я никогда не думал, что разница между этими двумя была настолько минимальной. Есть только некоторые проблемы с функциональностью языка и проблемы с производительностью. Я думал, что будет много практических различий, думаю, я был неправ. –

0

По сравнению с циклами вызов функции имеет свои собственные накладные расходы, такие как распределение стека и т. Д. И в большинстве случаев петли более понятны, чем их рекурсивные копии.

Кроме того, вы получите больше памяти и сможете даже выходить из пространства стека, если разница между стартом и остановкой высока, и слишком много экземпляров этого кода работают одновременно (что может случиться, когда вы получите больше трафика).

1

Ну, я не знаю о PHP, но большинство языков генерирует вызов функции (на уровне машины) для каждой рекурсии. Таким образом, у них есть потенциал для использования большого количества пространства стека, если только компилятор не выполняет оптимизацию хвостового вызова (если это позволяет ваш код). Циклы более «эффективны» в этом смысле, потому что они не растут. Преимущество рекурсии состоит в том, что она может выражать некоторые задачи более естественно.

В этом конкретном случае, с концептуальной (а не реалистичной) точки зрения, эти два решения полностью эквивалентны.

2

В общем, рекурсивная функция будет потреблять больше пространства стека (так как это действительно большой набор вызовов функций), в то время как итеративный решение не будет. Это также означает, что итеративное решение, в общем, будет быстрее, потому что.

Я не уверен, если это относится к интерпретируемый язык, как PHP, хотя, возможно, что интерпретатор может справиться с этим лучше.

2

петля будет быстрее, потому что всегда есть накладные расходы при выполнении вызова дополнительных функций.

Проблемы изучения рекурсии много примеров, приведенных (скажем, факториалы) плохие примеры использования рекурсии.

По возможности, придерживайтесь петли, если вам не нужно делать что-то другое. Хорошим примером использования рекурсии является цикл по каждому узлу в Дереве с несколькими уровнями дочерних узлов.

+1

+1 для: «По возможности, придерживайтесь петли, если вам не нужно делать что-то другое». Я никогда не понимал полной цели рекурсии, поэтому я никогда не знал, будет ли решение лучше использовать рекурсию. Теперь я знаю, что рекурсия - это решение проблемы, где цикла недостаточно. –

+0

@SaifBechan: Я не согласен с догмой о пребывании с петлями; рекурсия часто отражает проблему лучше, чем циклы, пытаясь заставить совпадающую рекурсивную проблему с итерацией часто приводить к сложному и трудно понятному коду (и наоборот). –

+0

Никто не говорил ничего о догме. В большинстве случаев вам, вероятно, не нужна рекурсия. –

2

Рекурсия немного медленнее (потому что вызовы функций медленнее, чем установка переменного) и использует больше места на стеке вызовов большинства Языки. Если вы попытались установить printnumbers(1, 1000000000), рекурсивная версия, скорее всего, произвела бы фатальную ошибку PHP или даже ошибку 500.

В некоторых случаях рекурсия имеет смысл, например, делать что-то с каждой частью дерева (получать все файлы в каталоге и его подкаталогах или, может быть, испортить XML-документ), но у него есть цена скорость, стековый след и время, затраченное на то, чтобы убедиться, что он не застревает, называя себя снова и снова, пока он не сработает. Если цикл имеет больше смысла, это определенно путь.

+3

Рекурсия не всегда медленнее. Скорость зависит от использования языка. – oherrala

+0

Единственный раз, когда это не медленнее, когда язык/компилятор может оптимизировать хвостовой вызов. Даже тогда я никогда не видел, чтобы это было быстрее. Вызов функции включает в себя сохранение параметров (или указателей на параметры), нажатие обратного адреса и переход к функции. Это * любой * язык, который поддерживает функции, потому что это в значительной степени то, что функция - список инструкций с определенной точкой входа (необязательно) способ принятия параметров (что на языках, поддерживающих рекурсию, в значительной степени требует стек) и способ вернуться. Для сравнения, в цикле есть магазин и прыжок. – cHao

+0

Оптимизация звонков в хвост - это просто переодевание. И единственный способ рекурсии даже * может быть быстрее, чем цикл, если ваш язык зацикливается, и в этом случае я не буду его использовать. – cHao

8

Часто задаваемые вопросы проще с помощью рекурсии. Это особенно актуально, когда вы говорите о древовидных структурах данных (например, каталоги, деревья решений ...).

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

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

Особенно функциональные языки хороши при обработке «бесконечной» рекурсии. Императивные языки сосредоточены на итерационных циклах.

+0

+1 Спасибо за статическую практическую разницу, как дерево. Я могу представить, как рекурсия была бы идеальной для этой ситуации, так как вы можете легко перейти к другой точке дерева. –

+0

@xtofi Отличное объяснение! Не могли бы вы - или кто-то другой - усовершенствовать или указать мне на какую-то литературу или ссылку на подробное описание вашего последнего заявления? «Особенно функциональные языки хорошо справляются с« бесконечной »рекурсией. Императивные языки ориентированы на итерационные петли». – Anovative

+1

@ Анимация: найдите «рекурсию хвоста» или «оптимизацию хвостовых вызовов». – xtofl

0

Вам не нужна рекурсия для такой плоской структуры. В первом коде, который я когда-либо использовал, рекурсия включала управление физическими контейнерами. Каждый контейнер может содержать материал (список из них, каждый с весами) и/или больше контейнеров, которые имеют вес. Мне нужен общий вес контейнера и все, что он держал. (Я использовал его, чтобы предсказать вес больших рюкзаков, полный снаряжения для кемпинга, без упаковки и взвешивания.) Это было легко сделать с рекурсией и было бы намного сложнее с петлями. Но многие проблемы, которые, естественно, подходят для одного подхода, также могут быть решены с другой.

1

Переполнение стека.

И нет, я не имею в виду сайт или что-то в этом роде. Я ЗНАЧУ «переполнение стека».

Смежные вопросы