2016-09-23 5 views
3

Рекурсивное вычисление факториала должно быть медленным из-за высокой сложности проблемы. Почему моя основная реализация не очень медленная? Мне любопытно, так как это должен быть пример учебника плохого подхода.Рекурсивный факториал является подозрительно быстрым

Это потому, что некоторые внутренние оптимизации или кэширования приводят к программе на C#?

using System; 
using System.Diagnostics; 
using System.Numerics; 

namespace FactorialRecursion 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Stopwatch stopwatch = new Stopwatch(); 
      for (int i = 0; i < 4000; i++) 
      { 
       stopwatch.Reset(); 
       stopwatch.Start(); 
       Factorial(i); 
       stopwatch.Stop(); 
       Console.WriteLine($"{i}! = ({stopwatch.Elapsed})"); 
      } 
      Console.ReadKey(); 
     } 

     static BigInteger Factorial(BigInteger number) 
     { 
      if (number <= 1) 
       return 1; 
      return number * Factorial(number - 1); 
     } 
    } 
} 

Результаты здесь:

3990! = (00:00:00.0144319) 
3991! = (00:00:00.0149198) 
3992! = (00:00:00.0159502) 
3993! = (00:00:00.0116784) 
3994! = (00:00:00.0104608) 
3995! = (00:00:00.0122931) 
3996! = (00:00:00.0128695) 
3997! = (00:00:00.0131792) 
3998! = (00:00:00.0142510) 
3999! = (00:00:00.0145544) 
+1

Или у вас есть быстрый процессор? –

+0

1) отлаживайте его или 2) распечатайте значение factoral, и вы можете сами убедиться. – Will

+3

Для ввода N вы выполняете операции умножения N. Почему бы не так быстро? – SlimsGhost

ответ

3

Рекурсивный подход в факторном расчета считается плохим из-за потребления стека памяти, а не из-за скорости.

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

+0

Как это отвечает на вопрос OPs? – Sylwester

+2

@ Sylwester: OP говорит, что рекурсивный подход считается плохим, но (s) он не знает почему. Вот что отвечает этот ответ. Плохость рекурсивного подхода заключается в потреблении памяти, а не в скорости. – displayName

+0

Перечитайте вопрос. ОП не понимает, почему это не хуже его измерений. Вопросы OPs действительно «Почему моя основная реализация не очень медленная?» и «Это из-за того, что некоторая внутренняя оптимизация или кэширование приводит к программе C#?». На кого это ответ? – Sylwester

2

Временная сложность факторного функционала с использованием как рекурсии, так и итерации одинакова, хотя рекурсия с использованием дополнительного пространства памяти для хранения вызовов функций в стеке. Но, что касается временной сложности, то в этот случай, потому что рекурсия действует косвенно как цикл. Временная сложность рекурсии становится хуже, чем итеративная копия, когда рекурсия пытается снова и снова вычислять одно и то же значение, которое избегается на итерации (например: - Fibonacci num gen using рекурсия без memoization имеет O (2^n) худшую временную сложность, тогда как ее итеративный экземпляр имеет сложную временную сложность O (n).

0

Существует вероятность того, что JIT использует оптимизацию хвостовой рекурсии, которая помогает для ускорения движения, see this for more information. Трудно сказать наверняка, но без доступа к вашей среде.

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

+0

Не думайте так. C# не имеет TCO, и даже если бы этот конкретный код не возвращался в хвостовое положение. – Sylwester

+0

Согласно этой ссылке компилятор C# не поддерживает хвостовую рекурсию, однако JIT делает это, и вы можете более или менее принудительно использовать его, используя определенные параметры компилятора, иначе это зависит от JIT. Это заставило меня думать, что это возможно из-за того, что последняя строка является рекурсивным вызовом, но, скорее всего, это не так. Поскольку я не знал точно, я думал, что добавлю его с отказом от ответственности. – Cody

Смежные вопросы