В чем практическая разница между циклом и рекурсией

в настоящее время я работаю в 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);
    }
}

этот метод будет делать точно то же самое, что и цикл, но только с рекурсией.

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

9 ответов


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

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

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


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

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

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

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


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

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


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

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

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


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

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


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

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


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

и нет, я не имею в виду сайт или что-то. Я имею в виду"переполнение стека".


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

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


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