понимание основной рекурсии

public static void main (String[] args)
{
    System.out.println(factorial(5));
}

public int factorial(int n)
{
    if(n <= 1){
        return 1;
    }
    else{
        return n * factorial(n - 1);
    }
}

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

Может кто-либо breiefly объяснять, как это работает в том смысле, что, как он хранится? Это начинается путем вычисления 5 * (5-1), затем вниз к 4 * (4-1) затем 3 * (3-1)....., пока это не добирается до 1, который просто возвратит 1 право? жаль о том, что был настолько поверхностным мне просто было бы интересно узнавать, как это работает точно

спасибо

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

5* (5-1) 4 * (4-1).........

как они хранятся и затем получили назад, или я пропускаю что-то?

7
задан Brian Agnew 22 December 2009 в 22:01
поделиться

8 ответов

Представьте, что вы компьютер, и кто-то протягивает вам бумагу с надписью

factorial(3)

. Затем вы выполняете процедуру, глядя на аргумент. Поскольку он> 1, вы пишете

factorial(2) 

на другом листе бумаги и «отдаете его себе», ожидая ответа на этот вопрос, прежде чем продолжить.

Вы снова выполняете процедуру. Поскольку 2 по-прежнему> 1, вы пишете

factorial(1)

на еще одном листе бумаги и передаете его себе, ожидая ответа на этот вопрос, прежде чем продолжить.

Вы снова выполняете процедуру. На этот раз ввод равен 1, поэтому вы берете первую ветвь и возвращаете 1. Вызов, который обрабатывал factorial (2), теперь имеет ответ, поэтому он умножает 2 на этот ответ (1) и возвращает. Теперь вызов, который обрабатывал factorial (3), получает свой ответ (2) и умножает его на 3, получая 6. Затем он возвращает этот ответ тому, кто начал всю операцию.

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

Теперь вызов, который обрабатывал factorial (3), получает свой ответ (2) и умножает его на 3, получая 6. Затем он возвращает этот ответ тому, кто начал всю операцию.

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

Теперь вызов, который обрабатывал factorial (3), получает свой ответ (2) и умножает его на 3, получая 6. Затем он возвращает этот ответ тому, кто начал всю операцию.

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

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

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

27
ответ дан 6 December 2019 в 04:49
поделиться

Да, это правильно в коде, сначала проверяется значение n , если оно меньше или равно 1, это то, что называется вашим базовый вариант . Они важны, они сообщают вашей рекурсивной функции, когда следует остановиться.

Если значение n не меньше или равно, возвращается значение n , умноженное на рекурсивное вызов факториала , но со значением n-1 до тех пор, пока он не достигнет своего базового случая: if (n <= 1) , где он возвращает 1

Ваш базовый случай был установлен факториальным определением 0! и 1! , которые оба равны 1.

Возможно, эта диаграмма поможет понять, как звонки работают.

5 * fact(5-1) ->
          4 * fact(4-1) ->
                    3 * fact(3-1) ->
                              2 * fact(1) 
                                       1

Это то же самое, что 5!

9
ответ дан 6 December 2019 в 04:49
поделиться
  1. При первоначальном вызове факториала n = 5, и помещается в стек.
  2. Затем запускается else, поэтому 4 становится передается в факториал, а также помещается в стек.
  3. Затем срабатывает else, поэтому 3 становится передается в факториал, а также помещается в стек.
  4. Затем срабатывает else, поэтому 2 становится передается в факториал, а также помещается в стек.
  5. Затем срабатывает else, поэтому 1 становится передается в факториал, а также помещается в стек.
  6. Затем запускается else, поэтому 0 становится передается в факториал, а также помещается в стек.
  7. if срабатывает и 1 возвращается в вызывающий факториал.
  8. if запускается и 2 * 1 возвращается в вызывающий факториал.
  9. if запускается и 3 * 2 возвращается в вызывающий факториал.
  10. if запускается и 4 * 3 возвращается в вызывающий факториал.
  11. Если запускается и 5 * 4 является возвращается к вызывающему факториалу.

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

5
ответ дан 6 December 2019 в 04:49
поделиться

.... затем 3 * (3-1) ..... пока он не дойдет до 1, который просто вернет 1, верно?

верно, но он возвращает это '1 'к предпоследнему вызову, который умножается на два, возвращая' 2 '... к предпоследнему, который умножается на три .....

1
ответ дан 6 December 2019 в 04:49
поделиться

Вы спрашиваете, как рекурсия работает внутри? Ответ в виде одного предложения состоит в том, что каждый поток имеет «стек вызовов», и каждый раз, когда вызывается метод, в этот стек помещается новая запись, в которой содержится информация о том, какой метод вызывается и какие аргументы были. Когда метод завершает свою работу, он помещает возвращаемое значение обратно в тот же стек, и вызывающий метод извлекает его. Таким образом, на высоте ваш стек будет выглядеть как

factorial (1) вызывается факториалом (2) вызывается факториалом (3) вызывается факториалом (4) вызывается факториалом (5)

Статья Википедии о стеках вызовов на первый взгляд кажется довольно подробной.

7
ответ дан 6 December 2019 в 04:49
поделиться

Практический подход, требующий хорошей IDE (eclipse, netbeans, IntelliJ):

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

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

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

0
ответ дан 6 December 2019 в 04:49
поделиться

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

до тех пор, пока она не дойдет до 1, которая просто вернет 1 вправо?

Я предполагаю, что вы имеете в виду, "если она просто вернет 1, то почему результат функции , а не 1?"

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

Если я скажу "дайте мне факториал(5)", то факториал(5) вернёт мне значение, но прежде чем он сможет вернуть мне значение, он должен спросить факториал(4) о его значении, факториал(5) по сути говорит "дайте мне факториал(4), чтобы я мог умножить его на 5 и вернуть его парню, который попросил факториал(5)". Теперь факториал(4) вернёт своё значение факториалу(5), который может умножить его на n и вернуть мне его значение. Напомним, что I не спрашивал значения factorial(4), меня это даже не волнует, и оно не вернулось ко мне, а вернулось к факториалу(5).

К тому времени, как вы нажмёте на факториал(1), у вас уже будут факториал(2), факториал(3), факториал(4) и факториал(5), которые будут ждать ответа обратно. Factorial(1) вернёт своё значение (которое равно 1, из-за вашего базового варианта) к факториалу(2), который в конце концов может вернуться к факториалу(3) и т.д., после чего рекурсия завершится и вы получите значение факториала(5) обратно.

.
1
ответ дан 6 December 2019 в 04:49
поделиться

Важно отметить, что "рекурсия" в java (процедурном языке) работает иначе, чем, скажем, в Haskell или F# (функциональных языках).

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

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

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

Ответ Джима - отличная физическая метафора для этого.

1
ответ дан 6 December 2019 в 04:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: