Рекурсивный вызов одного и того же метода дважды

2

В последовательности Фибоначчи я видел обычные реализации, которые рекурсивно называют один и тот же метод дважды:

public void fibonacci(int i)
{
fibonacci(1) + fibonacci(2);
}

Теперь этот метод не является точной копией того, что я видел, или правильным способом решения проблемы, но я видел два метода, добавленных вместе, как описано выше. Таким образом, метод не рекурсивно называется, а рекурсивно называется дважды. Что именно происходит при написании кода на С#? Оба метода работают на отдельных потоках? Что происходит под капотом?

Теги:
fibonacci

4 ответа

5
Лучший ответ

Это один из тех случаев, когда полезно подумать о том, как компьютер это делает.

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

def fib(n):
    if n == 0: return 0
    if n == 1: return 1
    if n >  1: return fib(n-2) + fib(n-1)

Теперь пройдите через это для fib (2)

fib(2) имеет n > 1, поэтому берет третью ветвь,

   return fib(2-2) + fib(2-1)

поэтому он вызывает fib() с 0, который равен 0

   return 0 + fib(2-1)

вызывает fib() с 1

   return 0 + 1

и мы видим результат 1. Теперь рассмотрим fib (3):

n > 1, поэтому

return fib(3-2)+fib(3-1)

а так как 3-2 равно 1, то для первого слагаемого получаем fib (1), который равен 1:

return 1 + fib(3-1)

и теперь, когда мы называем fib (3-1), а именно fib (2), у нас еще нет прямого ответа; п > 1. Таким образом, он становится

return 1 +
    return fib(2-2) + fib(2-1)

который становится

    return 0 + 1

ans мы возвращаемся к предыдущему вызову

return 1 + 1

и получить значение 2. Итак, вы видите, что нет отдельного потока, он просто работает через выражение. Я оставлю это как упражнение, чтобы выработать пример для fib (4); Бьюсь об заклад, если вы это сделаете, вы его пригвоздите.

Pop quiz: почему итеративная версия настолько эффективнее?

  • 1
    Сложность итерационной версии O (n), а сложность рекурсивной версии O (Fib (n)), потому что последняя всегда заканчивается 1 + 1 + 1 + ...
  • 0
    ... и закрытая версия O (1)
Показать ещё 3 комментария
8

Нет, их называют один за другим. Никакие дополнительные потоки не создаются, если вы явно не просите об этом (с помощью материала System.Threading). Я не уверен в том порядке, в котором они вызваны, но я бы догадался, что слева направо. Спецификация С# определенно имеет его в нем.

0

В С# вы можете легко реализовать метод, как вы предлагаете, - он может выглядеть примерно так.

public static int Fib(int i) {
   if (i == 0) return 0;
   if (i == 1) return 1;
   return Fib(i - 2) + Fib(i - 1);
}

Однако здесь нет никакой магии. Это только один раз рекурсивный вызов, за которым следует другой. То есть весь код запускается последовательно в одном потоке.

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

0

Они оцениваются последовательно. т.е. fibonacci(1) оценивается до fibonacci(2). Если вы хотите визуализировать его, это случай спускания первого дерева вызовов (с его собственной "раздвоенной рекурсией" ), резервное копирование, прежде чем спуститься во второй три.

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню