0 added
0 removed
Original
2026-01-01
Modified
2026-02-21
1
<p><a>#статьи</a></p>
1
<p><a>#статьи</a></p>
2
<ul><li>8 ноя 2022</li>
2
<ul><li>8 ноя 2022</li>
3
<li>0</li>
3
<li>0</li>
4
</ul><p>Чтобы понять рекурсивные алгоритмы, нужно понять рекурсивные алгоритмы. Или прочитать эту статью.</p>
4
</ul><p>Чтобы понять рекурсивные алгоритмы, нужно понять рекурсивные алгоритмы. Или прочитать эту статью.</p>
5
<p>Фото: Bernard Van Berg / Getty Images</p>
5
<p>Фото: Bernard Van Berg / Getty Images</p>
6
<p>Журналист, изучает Python. Любит разбираться в мелочах, общаться с людьми и понимать их.</p>
6
<p>Журналист, изучает Python. Любит разбираться в мелочах, общаться с людьми и понимать их.</p>
7
<p>Мы уже рассказывали о <a>рекурсии простыми словами</a>и о том, где её можно встретить в реальной жизни. Пришло время объяснить, как и зачем в программировании применяют рекурсивные функции.</p>
7
<p>Мы уже рассказывали о <a>рекурсии простыми словами</a>и о том, где её можно встретить в реальной жизни. Пришло время объяснить, как и зачем в программировании применяют рекурсивные функции.</p>
8
<p>В давние времена некоторые девушки, далёкие от точных наук, любили погадать, закрываясь ровно в полночь в чёрной-чёрной комнате и ставя свечку между двумя зеркалами. Согласно их поверьям, это открывало портал в потусторонний мир. А математики увидели бы в этом наглядное представление рекурсии.</p>
8
<p>В давние времена некоторые девушки, далёкие от точных наук, любили погадать, закрываясь ровно в полночь в чёрной-чёрной комнате и ставя свечку между двумя зеркалами. Согласно их поверьям, это открывало портал в потусторонний мир. А математики увидели бы в этом наглядное представление рекурсии.</p>
9
<p>В каждом зеркале отражалась свеча, и отражение этой свечи в другом зеркале, и отражение отражения, и отражение отражения отражения… и так до тех пор, пока девушке не померещится суженый.</p>
9
<p>В каждом зеркале отражалась свеча, и отражение этой свечи в другом зеркале, и отражение отражения, и отражение отражения отражения… и так до тех пор, пока девушке не померещится суженый.</p>
10
<p><strong>Предупреждение:</strong>не пытайтесь повторить эксперимент! В кадре задействованы только профессиональные каскадёры! Ни одно животное в процессе съёмки не пострадало!</p>
10
<p><strong>Предупреждение:</strong>не пытайтесь повторить эксперимент! В кадре задействованы только профессиональные каскадёры! Ни одно животное в процессе съёмки не пострадало!</p>
11
Гадание на суженого: наглядная, хотя и несколько инфернальная, демонстрация рекурсивного алгоритма<em>Фото: "ВКонтакте"</em><p>Выражаясь научным языком, рекурсия - это определение или изображение объекта или процесса внутри самого этого объекта или процесса.</p>
11
Гадание на суженого: наглядная, хотя и несколько инфернальная, демонстрация рекурсивного алгоритма<em>Фото: "ВКонтакте"</em><p>Выражаясь научным языком, рекурсия - это определение или изображение объекта или процесса внутри самого этого объекта или процесса.</p>
12
<p>Из вышеприведённого определения становится понятно, что рекурсивная функция - это такая функция, которая в процессе выполнения вызывает саму себя. Это свойство бывает полезно при выполнении некоторых задач в программировании.</p>
12
<p>Из вышеприведённого определения становится понятно, что рекурсивная функция - это такая функция, которая в процессе выполнения вызывает саму себя. Это свойство бывает полезно при выполнении некоторых задач в программировании.</p>
13
<p>Например, мы хотим написать функцию summa(n), которая считает сумму чисел от 1 до <strong>n</strong>. Если<strong>n</strong> = 2, то результат будет 1 + 2 = 3. Если<strong>n</strong> = 5, то получится 1 + 2 + 3 + 4 + 5 = 15. Реализовать такой алгоритм можно двумя способами: итерационным и рекурсивным.</p>
13
<p>Например, мы хотим написать функцию summa(n), которая считает сумму чисел от 1 до <strong>n</strong>. Если<strong>n</strong> = 2, то результат будет 1 + 2 = 3. Если<strong>n</strong> = 5, то получится 1 + 2 + 3 + 4 + 5 = 15. Реализовать такой алгоритм можно двумя способами: итерационным и рекурсивным.</p>
14
<p><strong>Итерационный, или пошаговый, способ.</strong>Напишем цикл от 1 до <strong>n</strong>, в котором на каждом следующем шаге будем прибавлять к <strong>x</strong> номер шага. На языке Python это выглядит так:</p>
14
<p><strong>Итерационный, или пошаговый, способ.</strong>Напишем цикл от 1 до <strong>n</strong>, в котором на каждом следующем шаге будем прибавлять к <strong>x</strong> номер шага. На языке Python это выглядит так:</p>
15
def summa(n): x = 0 for n in range(1, n+1): x += n return x summa(5) >>> 15<p><strong>Рекурсивный способ.</strong>Во втором варианте нужно решить задачу, используя её же саму.</p>
15
def summa(n): x = 0 for n in range(1, n+1): x += n return x summa(5) >>> 15<p><strong>Рекурсивный способ.</strong>Во втором варианте нужно решить задачу, используя её же саму.</p>
16
<p>summa(5) - то же самое, что 5 + summa(4)</p>
16
<p>summa(5) - то же самое, что 5 + summa(4)</p>
17
<p>summa(4) - то же самое, что 4 + summa(3)</p>
17
<p>summa(4) - то же самое, что 4 + summa(3)</p>
18
<p>summa(3) - то же самое, что 3 + summa(2)</p>
18
<p>summa(3) - то же самое, что 3 + summa(2)</p>
19
<p>summa(2) - то же самое, что 2 + summa(1)</p>
19
<p>summa(2) - то же самое, что 2 + summa(1)</p>
20
<p>summa(1) - это 1</p>
20
<p>summa(1) - это 1</p>
21
<p>Получается, что мы решаем задачу, используя ответ на эту же задачу, но с меньшей величиной входных данных:</p>
21
<p>Получается, что мы решаем задачу, используя ответ на эту же задачу, но с меньшей величиной входных данных:</p>
22
def summa(n): if n == 1: return 1 return n + summa(n-1) summa(5) >>> 15<p>Схематично работу функции можно обозначить так:</p>
22
def summa(n): if n == 1: return 1 return n + summa(n-1) summa(5) >>> 15<p>Схематично работу функции можно обозначить так:</p>
23
<em>Инфографика: Майя Мальгина для Skillbox Media</em><p>Из команд summa(n) и возвращает x можно составить лестницу. Это буквально шаги работы рекурсивного алгоритма. Сначала он постепенно уходит вглубь рекурсии - а потом идёт в обратную сторону, возвращаясь к первоначальной функции.</p>
23
<em>Инфографика: Майя Мальгина для Skillbox Media</em><p>Из команд summa(n) и возвращает x можно составить лестницу. Это буквально шаги работы рекурсивного алгоритма. Сначала он постепенно уходит вглубь рекурсии - а потом идёт в обратную сторону, возвращаясь к первоначальной функции.</p>
24
<p>На самом деле в большинстве случаев рекурсивную функцию можно заменить циклами: зачастую они работают быстрее и потребляют меньше памяти.</p>
24
<p>На самом деле в большинстве случаев рекурсивную функцию можно заменить циклами: зачастую они работают быстрее и потребляют меньше памяти.</p>
25
<p>Но при более сложных задачах лучше написать рекурсивное решение: так оперативнее, проще и понятнее, а ещё его легче поддерживать. Нередко это становится более важным и ценным, чем эффективность выполнения кода.</p>
25
<p>Но при более сложных задачах лучше написать рекурсивное решение: так оперативнее, проще и понятнее, а ещё его легче поддерживать. Нередко это становится более важным и ценным, чем эффективность выполнения кода.</p>
26
<p>Вернёмся к нашему примеру с summa(n). Чтобы алгоритм работал, программа должна соответствовать двум требованиям.</p>
26
<p>Вернёмся к нашему примеру с summa(n). Чтобы алгоритм работал, программа должна соответствовать двум требованиям.</p>
27
<p>Помимо рекурсивного случая, когда функция вызывает сама себя ещё раз, должно быть определённое стоп-условие, чтобы этот процесс не продолжался бесконечно и функция могла завершить работу самостоятельно. В программировании это называется базовым случаем.</p>
27
<p>Помимо рекурсивного случая, когда функция вызывает сама себя ещё раз, должно быть определённое стоп-условие, чтобы этот процесс не продолжался бесконечно и функция могла завершить работу самостоятельно. В программировании это называется базовым случаем.</p>
28
<p>У нас он произойдёт, когда<strong>n</strong>станет равным 1. Мы упростили задачу настолько, что больше нет смысла считать, - и можем просто дать ответ.</p>
28
<p>У нас он произойдёт, когда<strong>n</strong>станет равным 1. Мы упростили задачу настолько, что больше нет смысла считать, - и можем просто дать ответ.</p>
29
<p>Чтобы добраться до базового случая, рекурсивной функции приходится вызывать себя определённое количество раз. Такое число самовызовов плюс первоначальный вызов функции называется глубиной рекурсии. В случае summa(5) она равна 5.</p>
29
<p>Чтобы добраться до базового случая, рекурсивной функции приходится вызывать себя определённое количество раз. Такое число самовызовов плюс первоначальный вызов функции называется глубиной рекурсии. В случае summa(5) она равна 5.</p>
30
<p>Чтобы прийти к базовому случаю, мы должны передавать каждой новой функции изменённые данные. Другими словами, нужно изменять аргумент функции.</p>
30
<p>Чтобы прийти к базовому случаю, мы должны передавать каждой новой функции изменённые данные. Другими словами, нужно изменять аргумент функции.</p>
31
<p>В нашем коде при первом вызове<strong>n</strong>была равна 5, при втором - 4. И так до тех пор, пока<strong>n</strong>не стала равна 1. Не сделай мы этого, рекурсия никогда бы не завершилась и бесконечно порождала бы новые функции.</p>
31
<p>В нашем коде при первом вызове<strong>n</strong>была равна 5, при втором - 4. И так до тех пор, пока<strong>n</strong>не стала равна 1. Не сделай мы этого, рекурсия никогда бы не завершилась и бесконечно порождала бы новые функции.</p>
32
<p>В момент, когда функция вызывает сама себя, действие "материнской" функции приостанавливается - и начинается выполнение "дочерней".</p>
32
<p>В момент, когда функция вызывает сама себя, действие "материнской" функции приостанавливается - и начинается выполнение "дочерней".</p>
33
<p>Но так как рано или поздно программа должна вернуться к "материнской" функции, нужно сохранить данные о её работе. Для этого существует стек вызовов.</p>
33
<p>Но так как рано или поздно программа должна вернуться к "материнской" функции, нужно сохранить данные о её работе. Для этого существует стек вызовов.</p>
34
<p>Разберём это на примере вызова summa(5). Представим, что компьютер хранит данные о функции в виде блока. В нём содержится значение переменной<strong>n</strong>и кусок кода, который сейчас выполняется. Пусть он выглядит так:</p>
34
<p>Разберём это на примере вызова summa(5). Представим, что компьютер хранит данные о функции в виде блока. В нём содержится значение переменной<strong>n</strong>и кусок кода, который сейчас выполняется. Пусть он выглядит так:</p>
35
<em>Инфографика: Майя Мальгина для Skillbox Media</em><p>В момент, когда summa(5) вызывает summa(4), создаётся новый блок и кладётся поверх предыдущего:</p>
35
<em>Инфографика: Майя Мальгина для Skillbox Media</em><p>В момент, когда summa(5) вызывает summa(4), создаётся новый блок и кладётся поверх предыдущего:</p>
36
<em>Инфографика: Майя Мальгина для Skillbox Media</em><p>При каждом новом вызове функции на вершине стека оказывается новый блок. Он начинает работать, остальные же в это время ничего не делают и просто хранят данные. Так происходит, пока рекурсия не доходит до базового случая:</p>
36
<em>Инфографика: Майя Мальгина для Skillbox Media</em><p>При каждом новом вызове функции на вершине стека оказывается новый блок. Он начинает работать, остальные же в это время ничего не делают и просто хранят данные. Так происходит, пока рекурсия не доходит до базового случая:</p>
37
<em>Инфографика: Майя Мальгина для Skillbox Media</em><p>summa(1) возвращает единицу, завершает работу и покидает стек. Теперь наверху находится summa(2):</p>
37
<em>Инфографика: Майя Мальгина для Skillbox Media</em><p>summa(1) возвращает единицу, завершает работу и покидает стек. Теперь наверху находится summa(2):</p>
38
<em>Инфографика: Майя Мальгина для Skillbox Media</em><p>summa(2) продолжает свою работу с момента, на котором остановилась. Она заканчивает выполнять свою программу и тоже покидает стек. Теперь наверху summa(3). И так происходит с каждой функцией, пока стек не закончится.</p>
38
<em>Инфографика: Майя Мальгина для Skillbox Media</em><p>summa(2) продолжает свою работу с момента, на котором остановилась. Она заканчивает выполнять свою программу и тоже покидает стек. Теперь наверху summa(3). И так происходит с каждой функцией, пока стек не закончится.</p>
39
<p>При прочих равных программы с рекурсивными функциями обычно потребляют больше памяти, чем итерационные. Так происходит потому, что они хранят внутри стека данные обо всех незавершённых функциях.</p>
39
<p>При прочих равных программы с рекурсивными функциями обычно потребляют больше памяти, чем итерационные. Так происходит потому, что они хранят внутри стека данные обо всех незавершённых функциях.</p>
40
<p>Память и мощность любой техники ограничены, поэтому у стека всегда есть максимально допустимый размер. Например, в Python он равен 1000 вызовов. Если максимум достигнут, а мы пытаемся положить в стек ещё одну функцию, происходит ошибка "Переполнение стека".</p>
40
<p>Память и мощность любой техники ограничены, поэтому у стека всегда есть максимально допустимый размер. Например, в Python он равен 1000 вызовов. Если максимум достигнут, а мы пытаемся положить в стек ещё одну функцию, происходит ошибка "Переполнение стека".</p>
41
<p>Кстати, именно в честь этой ошибки назван популярный сайт для вопросов и ответов о программировании<a>Stack Overflow</a>.</p>
41
<p>Кстати, именно в честь этой ошибки назван популярный сайт для вопросов и ответов о программировании<a>Stack Overflow</a>.</p>
42
<p>Рекурсивная функция - это функция, которая вызывает сама себя.</p>
42
<p>Рекурсивная функция - это функция, которая вызывает сама себя.</p>
43
<p>Для правильной работы она должна содержать базовый случай и передавать новому уровню рекурсии изменённые данные.</p>
43
<p>Для правильной работы она должна содержать базовый случай и передавать новому уровню рекурсии изменённые данные.</p>
44
<p>Информация обо всех незавершённых функциях хранится в стеке. При этом в каждый момент времени работает только та функция, которая находится наверху стека.</p>
44
<p>Информация обо всех незавершённых функциях хранится в стеке. При этом в каждый момент времени работает только та функция, которая находится наверху стека.</p>
45
<p>Рекурсивные алгоритмы обычно медленнее итерационных, но зачастую их проще писать и поддерживать.</p>
45
<p>Рекурсивные алгоритмы обычно медленнее итерационных, но зачастую их проще писать и поддерживать.</p>
46
<p>Python для всех</p>
46
<p>Python для всех</p>
47
<p>Вы освоите Python на практике и создадите проекты для портфолио - телеграм-бот, веб-парсер и сайт с нуля. А ещё получите готовый план выхода на удалёнку и фриланс. Спикер - руководитель отдела разработки в "Сбере".</p>
47
<p>Вы освоите Python на практике и создадите проекты для портфолио - телеграм-бот, веб-парсер и сайт с нуля. А ещё получите готовый план выхода на удалёнку и фриланс. Спикер - руководитель отдела разработки в "Сбере".</p>
48
<p><a>Пройти бесплатно</a></p>
48
<p><a>Пройти бесплатно</a></p>
49
<a><b>Бесплатный курс по разработке на Python ➞</b>Пройдите бесплатный курс по Python и создайте с нуля телеграм-бот, веб-парсер и сайт. Спикер - руководитель отдела разработки в "Сбере". Пройти курс</a>
49
<a><b>Бесплатный курс по разработке на Python ➞</b>Пройдите бесплатный курс по Python и создайте с нуля телеграм-бот, веб-парсер и сайт. Спикер - руководитель отдела разработки в "Сбере". Пройти курс</a>