it-swarm.com.ru

Факториал с использованием рекурсии в Java

Я изучаю Java, используя книгу «Java: полный справочник». В настоящее время я работаю над темой «Рекурсия».

Пожалуйста, обратите внимание: Есть похожие вопросы по stackoverflow. Я искал их, но не нашел решения своего вопроса. Я запутался с логикой в ​​следующей программе.

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

  • Я не понял логику в следующей строке: результат = факт (n-1) * n;
  • Насколько мне известно, если мы передадим значение n = 4, как показано в приведенной ниже программе, 
  • Затем 3 * 4 сохраняется в результате, то есть 12. 
  • Опять факт (n-1) называется. Тогда n становится 3.
  • Затем 2 * 3 сохраняется в результате замены предыдущих 12.
  • Я думаю, вы поняли, где я застрял/запутался.

  • Спасибо.

class Calculation
{
    int fact(int n)
    {
        int result;

       if(n==1)
         return 1;

       result = fact(n-1) * n;
       return result;
    }
}

public class Factorial
{
     public static void main(String args[])
     {
       Calculation obj_one = new Calculation();

       int a = obj_one.fact(4);
       System.out.println("The factorial of the number is : " + a);
     }
}
25
user907629

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

Таким образом, когда факт вызывается с 3 в качестве аргумента, вы можете себе представить, что его результат

 result3 = fact(2) * 3
 result3 = result2 * 3
 result3 = 1 * 2 * 3
10
JB Nizet

Сначала вы должны понять, как работает факториал.

Давайте возьмем 4! В качестве примера.

4! = 4 * 3 * 2 * 1 = 24

Давайте смоделируем код, используя пример выше:

int fact(int n)
    {
        int result;
       if(n==0 || n==1)
         return 1;

       result = fact(n-1) * n;
       return result;
    }

В большинстве языков программирования у нас есть то, что мы называем function stack. Это похоже на колоду карт, где каждая карта помещается над другой, и каждая карта может рассматриваться как функция. Итак, передача метода fact:

Уровень стека 1: fact(4) // n = 4 and is not equal to 1. So we call fact(n-1)*n 

Уровень стека 2: fact(3) 

Уровень стека 3: fact(2)

Уровень стека 4: fact(1) // теперь n = 1. поэтому мы возвращаем 1 из этой функции.

возвращаемые значения ...

Уровень стека 3: 2 * fact(1) = 2 * 1 = 2

Уровень стека 2: 3 * fact(2) = 3 * 2 = 6

Уровень стека 1: 4 * fact(3) = 4 * 6 = 24

итак, мы получили 24.

Обратите внимание на следующие строки:

result = fact(n-1) * n;
           return result;

или просто:

return fact(n-1) * n;

Это вызывает саму функцию. Используя 4 в качестве примера,

По порядку в соответствии со стеками функций.

return fact(3) * 4;
return fact(2) * 3 * 4
return fact(1) * 2 * 3 * 4

Подставляя результаты ...

return 1 * 2 * 3 * 4 = return 24

Я надеюсь, вы поняли суть.

50
Neigyl R. Noval

Вот еще одно объяснение того, как работает факториал с использованием рекурсии.

Давайте немного изменим исходный код:

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

Вот расчет 3! подробно:

 enter image description here

Источник: РЕКУРСИЯ (Java, C++) | Алгоритмы и структуры данных

16
Eugene Matiyuk

Я полагаю, что ваша путаница связана с тем, что вы думаете, что существует только одна переменная result, тогда как на самом деле для каждого вызова функции существует переменная result. Поэтому старые результаты не заменяются, а возвращаются.

РАЗРАБОТАТЬ:

int fact(int n)
{
    int result;

   if(n==1)
     return 1;

   result = fact(n-1) * n;
   return result;
}

Предположим, вызов fact(2):

int result;
if ( n == 1 ) // false, go to next statement
result = fact(1) * 2; // calls fact(1):
|    
|fact(1)
|    int result;  //different variable
|    if ( n == 1 )  // true
|        return 1;  // this will return 1, i.e. call to fact(1) is 1
result = 1 * 2; // because fact(1) = 1
return 2;

Надеюсь, теперь стало понятнее.

9
Luchian Grigore

Что происходит, так это то, что сам рекурсивный вызов приводит к дальнейшему рекурсивному поведению. Если вы записали это, вы получите:

 fact(4)
 fact(3) * 4;
 (fact(2) * 3) * 4;
 ((fact(1) * 2) * 3) * 4;
 ((1 * 2) * 3) * 4;
5
rsp
public class Factorial {

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

    private static long factorial(int i) {

        if(i<0)  throw new IllegalArgumentException("x must be >= 0"); 
        return i==0||i==1? 1:i*factorial(i-1);
    }
}
5
SanA

Ключевой момент, который вы здесь упускаете, заключается в том, что переменная «result» является переменной стека и, как таковая, она не «заменяется». Чтобы уточнить, каждый раз, когда вызывается факт, в интерпретаторе внутренне создается новая переменная с именем «result» и связана с этим вызовом методов. Это контрастирует с полями объекта, которые связаны с экземпляром объекта, а не с конкретным вызовом метода.

3
idanzalz

Рекурсивное решение с использованием троичных операторов.

public static int fac(int n) {
    return (n < 1) ? 1 : n*fac(n-1);
}
1
martynas

Несмотря на то, что он старый, он все еще продолжает появляться в Google. Я решил упомянуть об этом. Никто не упомянул, чтобы проверить, когда x = 0

0! и 1! оба = 1. 

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

public static int fact(int x){
    if (x==1 | x==0)
        return 1;
    return fact(x-1) * x;
}// fact
1
prasanthv

По моему мнению, и поскольку это мнение кого-то со знанием Java на начальном уровне, я бы предложил изменить n == 1 на n <= 1 или (n == 0) || (n == 1) потому что факториал 0 равен 1. 

0
user3255993

Правильный:

int factorial(int n)
{
    if(n==0||n==1)
        return 1;
    else 
        return n*factorial(n-1);
}

Это вернуло бы 1 для факториала 0. Поверьте мне. Я усвоил это трудным путем .... Просто не соблюдая условие 0, не смог отменить собеседование.

0
Bikram Kundu

ИМХО, ключом к пониманию действий, связанных с рекурсией, является:

  1. Сначала мы рекурсивно погружаемся в стек, и с каждым вызовом мы Каким-то образом изменяем значение (например, n-1 in func(n-1);), которое определяет, должна ли рекурсия Идти все глубже и глубже.
  2. После выполнения RecursionStopCondition (например, n == 0) рекурсии прекращаются, И методы выполняют фактическую работу и возвращают значения вызывающему методу в верхних стеках .__, что приводит к пузырю на вершине стека.
  3. Важно перехватить значение, возвращенное из более глубокого стека, каким-то образом Изменить его (умножив на n в вашем случае), а затем вернуть это Измененное значение сверху стека. Распространенной ошибкой является то, что значение Из самого глубокого фрейма стека возвращается прямо в начало Стека, поэтому все вызовы методов игнорируются.

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

0
Konstantin

Чтобы понять это, вы должны объявить метод как можно более простым способом, и Мартины прибили его 6 мая:

int fact(int n) {
    if(n==0) return 1;
    else return n * fact(n-1);
}

прочитайте приведенную выше реализацию, и вы поймете. 

0
Eric Espino