Как следует из названия, у меня есть очень фундаментальный вопрос программирования, который я еще не смог разобрать. Отфильтровываем все (очень умно) «Чтобы понять рекурсию, вы должны сначала понять рекурсию». ответы из различных онлайн-тем, я все еще не совсем понимаю.
Понимая, что, когда мы сталкиваемся с незнанием того, чего мы не знаем, мы можем задавать неправильные вопросы или неправильно задавать правильные вопросы, я поделюсь тем, что я «думаю», мой вопрос в надежде, что кто-то с аналогичным мировоззрением может поделиться некоторыми немного знаний, которые помогут мне включить рекурсивную лампочку!
Вот функция (синтаксис написан на Swift):
func sumInts(a: Int, b: Int) -> Int {
if (a > b) {
return 0
} else {
return a + sumInts(a: a + 1, b: b)
}
}
Мы будем использовать 2 и 5 в качестве аргументов:
println(sumInts(a: 2, b: 5))
Очевидно, ответ - 14. Но я не понимаю, как это значение достигается.
Это мои 2 зависания:
Функция вызывается рекурсивно, пока не будет выполнено условие. Это условие a> b. Когда это условие выполнено, верните 0. На первый взгляд, я ожидал, что возвращаемое значение будет 0, что явно неверно.
Распечатка значения 'a' на каждой итерации дает значение, которое я ожидал: 2, 3, 4, 5 (в этой точке 5 + 1> b, что соответствует первому условию: a> b), но я все еще не делаю этого. Посмотрим, как достигается значение 14.
Моя первая мысль заключается в том, что волшебным образом происходит нечто подобное следующему:
var answer = a;
answer += a+1 until a > b;
return answer;
Так что исключая магию, я просто чего-то не понимаю. Я хотел бы понять, что происходит, больше, чем просто косвенно.
Если бы кто-нибудь мог любезно объяснить, что технически происходит во время такого рода функций и почему результат не равен 0 и как, в конечном итоге a + sumInts(a: a + 1, b: b) = 14
, я был бы навсегда в вашем долгу.
источник
LearnYouARecursion
, полные комплекты задач от профессора мирового уровня!Ответы:
Я думаю, что путаница возникает из-за того, что мы думаем об этом как об «одной и той же функции», вызываемой много раз. Если вы подумаете об этом как о «вызываемом множестве копий одной и той же функции», тогда это будет яснее:
Только одна копия функции всегда возвращает 0, и она не первая (последняя). Таким образом, результат вызова первого не равен 0.
Что касается второго недоразумения, я думаю, будет проще изложить рекурсию на английском языке. Прочтите эту строку:
как «вернуть значение« а »плюс (возвращаемое значение другой копии функции, которое является значением копии« а »плюс (возвращаемое значение другой копии функции, которое является значением второй копии из ' a 'plus (... ", при этом каждая копия функции порождает новую копию самой себя с увеличенным на 1, пока не будет выполнено условие a> b.
К тому времени, когда вы достигнете истинности условия a> b, у вас будет (потенциально произвольно) длинный стек копий функции, все в середине выполнения, все ожидают результата следующей копии, чтобы узнать, что они следует добавить к «а».
(edit: также нужно знать, что стек копий упомянутой функции - это реальная вещь, которая занимает реальную память и приведет к сбою вашей программы, если она станет слишком большой. Компилятор может оптимизировать ее в некоторых случаев, но исчерпание пространства стека является существенным и досадным ограничением рекурсивных функций во многих языках)
источник
a
иb
.Вот что
sumInts(2,5)
подумали бы компьютерные вычисления , если бы могли:Как видите, некоторые вызовы функции на
sumInts
самом деле возвращают 0, однако это не окончательное значение, потому что компьютеру все равно нужно прибавить 5 к этому 0, затем 4 к результату, затем 3, затем 2, как описано в четырех последних предложениях мысли нашего компьютера. Обратите внимание, что в рекурсии компьютер не только должен вычислять рекурсивный вызов, он также должен помнить, что делать со значением, возвращаемым рекурсивным вызовом. В памяти компьютера есть специальная область, называемая стеком, в которой сохраняется такая информация, это пространство ограничено, а функции, которые слишком рекурсивны, могут исчерпать стек: это переполнение стека, дающее название нашему любимому веб-сайту.Ваше утверждение, кажется, делает неявное предположение, что компьютер забывает, что он делал, выполняя рекурсивный вызов, но это не так, поэтому ваш вывод не соответствует вашему наблюдению.
Это связано с тем, что возвращаемое значение - это не
a
само, а сумма значенияa
и значения, возвращаемого рекурсивным вызовом.источник
sumInts
так, чтобы он действительно записывал «компьютерные мысли». Как только вы написали от руки такие функции, вы, вероятно, «поняли»!Чтобы понять рекурсию, вы должны подумать о проблеме по-другому. Вместо большой логической последовательности шагов, которая имеет смысл в целом, вы вместо этого берете большую проблему и разбиваете ее на более мелкие проблемы и решаете их, когда у вас есть ответ на подзадачи, вы объединяете результаты подзадач, чтобы сделать решение более серьезной проблемы. Представьте, что вам и вашим друзьям нужно подсчитать количество шариков в огромном ведре. Вы берете ведро поменьше и начинаете считать их по отдельности, а когда закончите, складываете итоги вместе ... Что ж, если каждый из вас найдет друга и разделит ведра дальше, то вам просто нужно подождать, пока эти друзья вычислите их итоги, верните их каждому из вас, вы сложите их. И так далее.
Вы должны помнить, что каждый раз, когда функция вызывает себя рекурсивно, она создает новый контекст с подмножеством проблемы, после того как эта часть решена, он возвращается, чтобы можно было завершить предыдущую итерацию.
Позвольте мне показать вам шаги:
после выполнения sumInts (a: 6, b: 5) результаты могут быть вычислены, поэтому, возвращаясь вверх по цепочке с результатами, вы получите:
Другой способ представить структуру рекурсии:
источник
Рекурсия - непростая тема для понимания, и я не думаю, что смогу в полной мере передать ее здесь. Вместо этого я попытаюсь сосредоточиться на конкретном фрагменте кода, который у вас есть, и попытаться описать как интуитивное понимание того, почему решение работает, так и механизм вычисления результата кодом.
Приведенный здесь код решает следующую проблему: вы хотите узнать сумму всех целых чисел от a до b включительно. В вашем примере вам нужна сумма чисел от 2 до 5 включительно, т.е.
При попытке рекурсивного решения проблемы одним из первых шагов должно быть выяснение того, как разбить проблему на меньшую проблему с той же структурой. Предположим, вы хотите просуммировать числа от 2 до 5 включительно. Один из способов упростить это - заметить, что указанную выше сумму можно переписать как
Здесь (3 + 4 + 5) оказывается суммой всех целых чисел от 3 до 5 включительно. Другими словами, если вы хотите узнать сумму всех целых чисел от 2 до 5, начните с вычисления суммы всех целых чисел от 3 до 5, а затем добавьте 2.
Так как же вычислить сумму всех целых чисел от 3 до 5 включительно? Что ж, эта сумма
который можно рассматривать как
Здесь (4 + 5) - это сумма всех целых чисел от 4 до 5 включительно. Итак, если вы хотите вычислить сумму всех чисел от 3 до 5 включительно, вы должны вычислить сумму всех целых чисел от 4 до 5, а затем добавить 3.
Здесь есть шаблон! Если вы хотите вычислить сумму целых чисел от a до b включительно, вы можете сделать следующее. Сначала вычислите сумму целых чисел от a + 1 до b включительно. Затем добавьте к этой сумме. Вы заметите, что «вычислить сумму целых чисел от a + 1 до b включительно» - это примерно та же проблема, которую мы уже пытаемся решить, но с немного другими параметрами. Вместо того, чтобы вычислять от a до b включительно, мы вычисляем от a + 1 до b включительно. Это рекурсивный шаг - чтобы решить большую проблему («сумма от a до b, включительно»), мы уменьшаем проблему до меньшей версии самой себя («сумма от a + 1 до b, включительно»).
Если вы посмотрите на приведенный выше код, вы заметите, что в нем есть следующий шаг:
Этот код является просто переводом вышеупомянутой логики - если вы хотите суммировать от a до b включительно, начните с суммирования a + 1 до b включительно (это рекурсивный вызов
sumInt
s), а затем добавьтеa
.Конечно, сам по себе такой подход не работает. Например, как бы вы вычислили сумму всех целых чисел от 5 до 5 включительно? Итак, используя нашу текущую логику, вы бы вычислили сумму всех целых чисел от 6 до 5 включительно, а затем прибавили бы 5. Итак, как вы вычислите сумму всех целых чисел от 6 до 5 включительно? Итак, используя нашу текущую логику, вы должны вычислить сумму всех целых чисел от 7 до 5 включительно, а затем добавить 6. Вы заметите здесь проблему - это просто продолжается и продолжается!
При рекурсивном решении проблемы должен быть какой-то способ перестать упрощать проблему и вместо этого просто решить ее напрямую. Как правило, вы найдете простой случай, когда ответ может быть определен немедленно, а затем структурируете свое решение для решения простых случаев сразу же, когда они возникают. Обычно это называется базовым случаем или рекурсивным базисом .
Итак, каков базовый случай в этой конкретной проблеме? Когда вы суммируете целые числа от a до b включительно, если a оказывается больше b, то ответ равен 0 - в этом диапазоне нет чисел! Поэтому мы структурируем наше решение следующим образом:
Теперь сравните этот псевдокод с вашим реальным кодом:
Обратите внимание, что между решением, описанным в псевдокоде, и настоящим кодом существует почти однозначное соответствие. Первый шаг - это базовый случай - в случае, если вы запрашиваете сумму для пустого диапазона чисел, вы получаете 0. В противном случае вычислите сумму между a + 1 и b, затем добавьте a.
Пока что я дал лишь общее представление о коде. Но у вас было два других очень хороших вопроса. Во-первых, почему это не всегда возвращает 0, учитывая, что функция требует вернуть 0, если a> b? Во-вторых, откуда на самом деле 14? Давайте посмотрим на них по очереди.
Давайте попробуем очень и очень простой случай. Что будет, если вы позвоните
sumInts(6, 5)
? В этом случае, прослеживая код, вы видите, что функция просто возвращает 0. Это правильно, чтобы - в диапазоне не было никаких чисел. А теперь попробуйте что-нибудь посложнее. Что происходит, когда вы звонитеsumInts(5, 5)
? Что ж, вот что происходит:sumInts(5, 5)
. Мы попадаем вelse
Попадаем ветку, которая возвращает значение `a + sumInts (6, 5).sumInts(5, 5)
определить, чтоsumInts(6, 5)
есть, нам нужно приостановить то, что мы делаем, и позвонитьsumInts(6, 5)
.sumInts(6, 5)
называется. Он входит вif
филиал и возвращается0
. Однако этот экземплярsumInts
был вызван пользователемsumInts(5, 5)
, поэтому возвращаемое значение передается обратноsumInts(5, 5)
, а не вызывающей стороне верхнего уровня.sumInts(5, 5)
теперь можно вычислить,5 + sumInts(6, 5)
чтобы вернуться5
. Затем он возвращает его вызывающей стороне верхнего уровня.Обратите внимание, как здесь сформировалось значение 5. Мы начали с одного активного звонка в
sumInts
. Это запустило еще один рекурсивный вызов, и значение, возвращенное этим вызовом, передало информацию обратноsumInts(5, 5)
. Вызов to,sumInts(5, 5)
в свою очередь, произвел некоторые вычисления и вернул значение вызывающей стороне.Если вы попробуете это сделать
sumInts(4, 5)
, вот что произойдет:sumInts(4, 5)
пытается вернуться4 + sumInts(5, 5)
. Для этого он звонитsumInts(5, 5)
.sumInts(5, 5)
пытается вернуться5 + sumInts(6, 5)
. Для этого он звонитsumInts(6, 5)
.sumInts(6, 5)
возвращает 0 обратно вsumInts(5, 5).</li> <li>
sumInts (5, 5)now has a value for
sumInts (6, 5), namely 0. It then returns
5 + 0 = 5`.sumInts(4, 5)
теперь имеет значениеsumInts(5, 5)
, а именно 5. Затем он возвращается4 + 5 = 9
.Другими словами, возвращаемое значение формируется путем суммирования значений по одному, каждый раз беря одно значение, возвращаемое конкретным рекурсивным вызовом,
sumInts
и добавляя текущее значениеa
. Когда рекурсия достигает дна, самый глубокий вызов возвращает 0. Однако это значение не сразу выходит из цепочки рекурсивных вызовов; вместо этого он просто возвращает значение рекурсивному вызову на один уровень выше. Таким образом, каждый рекурсивный вызов просто добавляет еще одно число и возвращает его выше в цепочке, что завершается общим суммированием. В качестве упражнения попробуйте отследить это дляsumInts(2, 5)
, с чего вы и хотели начать.Надеюсь это поможет!
источник
Пока что у вас есть несколько хороших ответов, но я добавлю еще один, который придерживается другой линии.
Во-первых, я написал много статей о простых рекурсивных алгоритмах, которые могут вас заинтересовать; видеть
http://ericlippert.com/tag/recursion/
http://blogs.msdn.com/b/ericlippert/archive/tags/recursion/
Они расположены в порядке «новые сверху», поэтому начните снизу.
Во-вторых, до сих пор все ответы описывали рекурсивную семантику с учетом активации функции . Каждый, каждый вызов производит новую активацию , и рекурсивный вызов выполняется в контексте этой активации. Это хороший способ думать об этом, но есть другой, эквивалентный способ: интеллектуальный поиск и замена текста .
Позвольте мне переписать вашу функцию в более компактной форме; не думайте об этом как о каком-то конкретном языке.
Я надеюсь, что в этом есть смысл. Если вы не знакомы с условным оператором, он имеет форму,
condition ? consequence : alternative
и его значение станет ясным.Теперь мы хотим оценить.Мы
s(2,5)
делаем это, выполняя текстовую замену вызова телом функции, затем заменяемa
на2
иb
на5
:Теперь оцените условное. Мы текстуально заменяем
2 > 5
наfalse
.Теперь текстуально замените все ложные условные выражения альтернативой, а все истинные условные выражения - следствием. У нас есть только ложные условные выражения , поэтому мы текстуально заменяем это выражение альтернативой:
Теперь, чтобы избавить меня от необходимости вводить все эти
+
знаки, текстуально замените арифметику констант ее значением. (Это немного обман, но я не хочу отслеживать все скобки!)Теперь выполните поиск и замену, на этот раз с телом вызова,
3
дляa
и5
для b. Мы заключим замену вызова в скобки:А теперь мы просто продолжаем делать те же шаги текстовой замены:
Все, что мы здесь сделали, было простой текстовой заменой . На самом деле мне не следовало заменять «3» на «2 + 1» и так далее, пока не пришлось бы, но с педагогической точки зрения это было бы трудно читать.
Активация функции - это не что иное, как замена вызова функции телом вызова и замена формальных параметров соответствующими аргументами. Вы должны быть осторожны с разумным введением скобок, но в остальном это просто замена текста.
Конечно, большинство языков фактически не реализуют активацию как замену текста, но логически именно это и есть.
Так что же тогда такое неограниченная рекурсия? Рекурсия, в которой текстовая подстановка не прекращается! Обратите внимание, как в конце концов мы подошли к этапу, на котором больше нечего
s
было заменять, и тогда мы могли просто применить правила для арифметики.источник
Обычно я выясняю, как работает рекурсивная функция, глядя на базовый случай и работая в обратном направлении. Вот техника, примененная к этой функции.
Сначала базовый случай:
Затем вызов чуть выше этого в стеке вызовов :
Затем вызов чуть выше этого в стеке вызовов:
И так далее:
И так далее:
Обратите внимание, что мы пришли к нашему исходному вызову функции
sumInts(2, 5) == 14
Порядок, в котором выполняются эти вызовы:
Порядок возврата этих вызовов:
Обратите внимание, что мы пришли к выводу о том, как работает функция, отслеживая вызовы в том порядке, в котором они возвращаются .
источник
Я попробую.
Выполняя уравнение a + sumInts (a + 1, b), я покажу, как окончательный ответ равен 14.
Дайте нам знать, если у вас возникнут дополнительные вопросы.
Вот еще один пример рекурсивных функций в следующем примере.
Мужчина только что закончил колледж.
t - количество времени в годах.
Общее фактическое количество лет, отработанных до выхода на пенсию, можно рассчитать следующим образом:
И этого должно быть достаточно, чтобы угнетать кого угодно, лол. ;-П
источник
Рекурсия. В информатике рекурсия подробно рассматривается в разделе «Конечные автоматы».
В простейшей форме это ссылка на себя. Например, высказывание «моя машина - это машина» является рекурсивным утверждением. Проблема в том, что это утверждение является бесконечной рекурсией и никогда не закончится. Определение в утверждении «автомобиль» состоит в том, что это «автомобиль», поэтому его можно заменить. Однако этому нет конца, потому что в случае замены это все равно становится «моя машина есть машина».
Все могло бы быть иначе, если бы было сказано: «Моя машина - Bentley. Моя машина - синяя». В этом случае заменой во второй ситуации для автомобиля может быть "bentley", в результате чего "мой bentley синий". Эти типы замен математически объясняются в компьютерных науках с помощью контекстно-свободных грамматик .
Фактическое замещение - это правило производства. Учитывая, что оператор представлен буквой S и эта машина является переменной, которая может быть «бентли», это утверждение можно рекурсивно реконструировать.
Это можно построить разными способами, поскольку каждый из них
|
означает, что есть выбор.S
может быть заменен любым из этих вариантов, и S всегда начинается с нуля. Способε
прекращения производства. ТакжеS
могут быть заменены другие переменные (есть только одна, и онаC
будет представлять "bentley").Итак, начиная с
S
пустого значения и заменяя его первым вариантом,"my"S
S
становитсяS
все еще можно заменить, поскольку он представляет собой переменную. Мы могли бы снова выбрать «мой» или ε, чтобы закончить его, но давайте продолжим наше первоначальное утверждение. Выбираем пространство, значитS
заменяется на" "S
Далее выбираем C
И у C есть только один вариант для замены
И снова место для S
И так далее
"my bentley is"S
,"my bentley is "S
,"my bentley is blue"S
,"my bentley is blue"
(замена S для е заканчивает производство) и мы рекурсивно построили наше утверждение «мой Бентли синий».Думайте о рекурсии как об этих продуктах и заменах. Каждый шаг в процессе заменяет своего предшественника для получения конечного результата. В точном примере с рекурсивной суммой от 2 до 5 вы получите результат
Это становится
источник
Я думаю, что лучший способ понять рекурсивные функции - это понять, что они созданы для обработки рекурсивных структур данных. Но в вашей исходной функции,
sumInts(a: Int, b: Int)
которая рекурсивно вычисляет сумму чисел отa
доb
, она, похоже, не является рекурсивной структурой данных ... Давайте попробуем немного измененную версию, вsumInts(a: Int, n: Int)
которойn
сколько чисел вы добавите.Теперь sumInts рекурсивно повторяет
n
натуральное число. Все еще не рекурсивные данные, правда? Ну, натуральное число можно рассматривать как рекурсивную структуру данных, используя аксиомы Пеано:Итак, 0 = ноль, 1 = Succesor (ноль), 2 = Succesor (Succesor (Zero)) и так далее.
Если у вас есть рекурсивная структура данных, у вас есть шаблон для функции. Для каждого нерекурсивного случая вы можете вычислить значение напрямую. Для рекурсивных случаев вы предполагаете, что рекурсивная функция уже работает, и используете ее для вычисления случая, но деконструируете аргумент. В случае Natural это означает, что вместо
Succesor(n)
мы будем использоватьn
или, что эквивалентно, вместоn
мы будем использоватьn - 1
.Теперь рекурсивную функцию проще программировать. Во-первых, базовый случай,
n=0
. Что мы должны вернуть, если не хотим складывать числа? Ответ, конечно же, 0.А как насчет рекурсивного случая? Если мы хотим складывать
n
числа, начинающиеся с,a
и у нас уже есть рабочаяsumInts
функция, которая работаетn-1
? Что ж, нам нужно добавить,a
а затем вызватьsumInts
с помощьюa + 1
, поэтому мы заканчиваем:Приятно то, что теперь вам не нужно думать о низком уровне рекурсии. Вам просто нужно убедиться, что:
источник
Возможно, вас заинтересуют Нисан и Шоккен реализация функций . Связанный PDF-файл является частью бесплатного онлайн-курса. В нем описывается вторая часть реализации виртуальной машины, в которой учащийся должен написать компилятор языка виртуальной машины на язык машины. Предлагаемая ими реализация функции способна к рекурсии, потому что она основана на стеке.
Чтобы познакомить вас с реализацией функции: рассмотрим следующий код виртуальной машины:
Если Swift скомпилирован на этот язык виртуальной машины, то следующий блок кода Swift:
будет компилироваться до
Язык виртуальной машины разработан на основе глобального стека .
push constant n
помещает целое число в этот глобальный стек.После выполнения строк 1 и 2 стек выглядит так:
256
и257
являются адресами памяти.call mult
помещает номер строки возврата (3) в стек и выделяет место для локальных переменных функции.... и это идет на этикетку
function mult
. Код внутриmult
выполнен. В результате выполнения этого кода мы вычисляем произведение 2 и 3, которое сохраняется в 0-й локальной переменной функции.Непосредственно перед
return
входом в mult вы заметите строку:Запихиваем товар в стопку.
Когда мы возвращаемся, происходит следующее:
После возврата мы готовы выполнить строку 4, и наш стек выглядит так:
Теперь помещаем 4 в стек.
sub
это примитивная функция языка виртуальных машин. Он принимает два аргумента и возвращает свой результат по обычному адресу: адрес 0-го аргумента.Теперь у нас есть
Теперь, когда вы знаете, как работает вызов функции, относительно просто понять, как работает рекурсия. Никакой магии , только стопка.
Я реализовал вашу
sumInts
функцию на этом языке виртуальной машины:Теперь я назову это:
Код выполняется, и мы доходим до точки остановки, откуда
lte
возвращаетсяfalse
. Вот как выглядит стек в этот момент:Теперь давайте «раскрутим» нашу рекурсию.
return
0 и перейти к строке 15 и вперед.Строка 16:
add
Строка 17:
return
5 и переход к строке 15 и вперед.Строка 16:
add
Строка 17:
return
9 и переход к строке 15 и вперед.Строка 16:
add
Строка 17:
return
12 и переход к строке 15 и вперед.Строка 16:
add
Строка
return
17:14 и переход к строке 21 и вперед.Вот и все. Рекурсия: Прославленная
goto
.источник
Один действительно хороший совет, с которым я столкнулся, изучая и действительно понимая рекурсию, - это потратить некоторое время на изучение языка, который не имеет никакой формы конструкции цикла, кроме как через рекурсию. Таким образом, вы на практике научитесь использовать рекурсию.
Я подписался на http://www.htdp.org/ который не только является учебным пособием по схеме, но и является отличным введением в разработку программ с точки зрения архитектуры и дизайна.
Но в основном вам нужно потратить некоторое время. Без «твердого» понимания рекурсии определенные алгоритмы, такие как возврат с возвратом, всегда будут казаться вам «сложными» или даже «волшебными». Итак, проявите настойчивость. :-D
Надеюсь, это поможет и удачи!
источник
Есть уже много хороших ответов. Тем не менее я пытаюсь.
При вызове функции выделяется пространство памяти , которое накладывается на пространство памяти вызывающей функции. В этом пространстве памяти функция хранит переданные ей параметры, переменные и их значения. Это пространство памяти исчезает вместе с завершающим обратным вызовом функции. По идее стека становится активным пространство памяти вызывающей функции.
Для рекурсивных вызовов одна и та же функция накладывает несколько пространств памяти друг на друга. Вот и все. Простое представление о том, как стек работает в памяти компьютера, должно помочь вам понять, как происходит рекурсия при реализации.
источник
Я знаю, что это немного не по теме, но ... попробуйте поискать рекурсию в Google ... Вы увидите на примере, что это означает :-)
Более ранние версии Google возвращали следующий текст (цитируется по памяти):
10 сентября 2014 года был обновлен анекдот про рекурсию:
Другой ответ см. В этом ответе .
источник
Думайте о рекурсии как о нескольких клонах, делающих одно и то же ...
Вы просите клонировать [1]: «суммировать числа от 2 до 5»
и вуаля !!
источник
Многие из приведенных выше ответов очень хороши. Однако полезный метод решения рекурсии состоит в том, чтобы сначала объяснить, что мы хотим сделать, и закодировать так, как это решит человек. В приведенном выше случае мы хотим суммировать последовательность последовательных целых чисел (используя числа, указанные выше):
Теперь обратите внимание, что эти строки сбивают с толку (не неправильно, но сбивают с толку).
Почему тест
a>b
? И почемуreturn 0
Давайте изменим код, чтобы более точно отразить то, что делает человек.
Можем ли мы сделать это еще более человечно? Да! Обычно суммируем слева направо (2 + 3 + ...). Но приведенная выше рекурсия суммируется справа налево (... + 4 + 5). Измените код, чтобы отразить это (
-
может быть немного пугающим, но не сильно)Некоторые могут найти эту функцию более запутанной, так как мы начинаем с «дальнего» конца, но практика может сделать ее более естественной (и это еще один хороший метод «мышления»: пробовать «обе» стороны при решении рекурсии). И снова функция отражает то, что делает человек (большинство?): Берет сумму всех левых целых чисел и складывает «следующее» правое целое число.
источник
Мне было трудно понять рекурсию, тогда я нашел этот блог и уже видел этот вопрос, поэтому подумал, что должен поделиться. Вы должны прочитать этот блог, я нашел это чрезвычайно полезным, он объясняет со стеком, и даже он объясняет, как две рекурсии работают со стеком, шаг за шагом. Я рекомендую вам сначала понять, как работает стек, что очень хорошо объясняется здесь: путь к стеку
then now you will understand how recursion works now take a look of this post
: Пошаговое понимание рекурсииЭто программа:
источник
Рекурсия стала для меня понятной, когда я перестал читать то, что о ней говорят другие, или рассматривал ее как нечто, чего я могу избежать, и просто написал код. Я обнаружил проблему с решением и попытался продублировать решение, не глядя. Я посмотрел на решение только тогда, когда беспомощно застрял. Затем я вернулся к попыткам воспроизвести его. Я проделал это снова с множеством проблем, пока не выработал собственное понимание и понимание того, как идентифицировать рекурсивную проблему и решать ее. Когда я дошел до этого уровня, я начал придумывать проблемы и решать их. Это помогло мне больше. Иногда чему-то можно научиться, только пробуя это самостоятельно и борясь; пока вы не «получите это».
источник
Позвольте мне рассказать вам на примере ряда Фибоначчи, Фибоначчи - это
так что давайте посмотрим , как рекуррентные работает, я просто заменить
n
вt(n)
сn-1
и так далее. это выглядит:мы знаем,
t(0)=(n-k)
равно ли1
тогда,n-k=0
поэтомуn=k
заменяемk
наn
:если опустить,
n-n
то:так
3+2+1+(n-1)+n
натуральное число. он рассчитывается какΣ3+2+1+(n-1)+n = n(n+1)/2 => n²+n/2
результат для fib:
O(1 + n²) = O(n²)
Это лучший способ понять рекурсивное отношение
источник