web-dev-qa-db-fra.com

Complexité du programme factoriel récursif

Quelle est la complexité d'un programme récursif pour trouver la factorielle d'un nombre n? Mon intuition est que ce pourrait être O(n).

27
Karan

Si vous prenez la multiplication comme O(1), alors oui, O(N) est correct. Cependant, notez que la multiplication de deux nombres de longueur arbitraire x n'est pas O(1) sur le matériel fini - comme x tend vers l'infini, le temps nécessaire à la multiplication s'allonge (par exemple si vous utilisez Karatsuba multiplication , c'est O(x ** 1.585)).

Vous pouvez théoriquement faire mieux pour des nombres suffisamment grands avec Schönhage-Strassen , mais j'avoue que je n'ai aucune expérience du monde réel avec celui-ci. x, la "longueur" ou le "nombre de chiffres" (quelle que soit la base, peu importe pour big-O de toute façon N, grandit avec O(log N), bien sûr.

Si vous voulez limiter votre question à des factorielles de nombres suffisamment courts pour être multipliées dans O(1), alors il n'y a aucun moyen que N puisse "tendre vers l'infini" et donc la notation big-O est inappropriée.

36
Alex Martelli

Lorsque vous exprimez la complexité d'un algorithme, c'est toujours en fonction de la taille d'entrée. Il est uniquement valable de supposer que la multiplication est une opération O(1) si les nombres que vous multipliez sont de taille fixe. Par exemple, si vous souhaitez déterminer la complexité d'un algorithme qui calcule les produits matriciels, vous pouvez supposer que les composants individuels des matrices étaient de taille fixe. Il serait alors valide de supposer que la multiplication de deux composants de matrice individuels était O(1), et vous calculeriez la complexité en fonction du nombre d'entrées dans chaque matrice.

Cependant, lorsque vous voulez comprendre la complexité d'un algorithme pour calculer N!, Vous devez supposer que N peut être arbitrairement grand, il n'est donc pas valide de supposer que la multiplication est un O(1) opération.

Si vous voulez multiplier un nombre de n bits par un nombre de m bits, l'algorithme naïf (le genre que vous faites à la main) prend du temps O(mn), mais il existe des algorithmes plus rapides.

Si vous souhaitez analyser la complexité de l'algorithme simple de calcul N!

    factorial(N)
       f=1
       for i = 2 to N
          f=f*i

       return f

puis à la k-ème étape de la boucle for, vous multipliez (k-1)! par k. Le nombre de bits utilisés pour représenter (k-1)! Est O(k log k) et le nombre de bits utilisés pour représenter k est O(log k). Ainsi, le temps nécessaire pour multiplier (k-1)! Et k est O(k (log k)^2) (en supposant que vous utilisez l'algorithme de multiplication naïf). Ensuite, le temps total pris par l'algorithme est la somme du temps pris à chaque étape:

sum k = 1 to N [k (log k)^2] <= (log N)^2 * (sum k = 1 to N [k]) =
O(N^2 (log N)^2)

Vous pouvez améliorer ces performances en utilisant un algorithme de multiplication plus rapide, comme Schönhage-Strassen, qui prend du temps O(n*log(n)*log(log(n))) pour les nombres à 2 n bits.

L'autre façon d'améliorer les performances est d'utiliser un meilleur algorithme pour calculer N!. Le plus rapide que je connaisse calcule d'abord la factorisation première de N!, Puis multiplie tous les facteurs premiers.

21
gwilkins

En supposant que vous parlez de l'algorithme factoriel le plus naïf de tous les temps:

factorial (n):
  if (n = 0) then return 1
  otherwise return n * factorial(n-1)

Oui, l'algorithme est linéaire, s'exécutant en O(n) fois. C'est le cas car il s'exécute une fois à chaque fois qu'il décrémente la valeur n, et il décrémente la valeur n jusqu'à ce qu'il atteigne 0, ce qui signifie que la fonction est appelée récursivement n fois. Cela suppose, bien sûr, que la décrémentation et la multiplication sont des opérations constantes.

Bien sûr, si vous implémentez le factoriel d'une autre manière (par exemple, en utilisant l'addition de manière récursive au lieu de la multiplication), vous pouvez vous retrouver avec un algorithme beaucoup plus complexe dans le temps. Je ne conseillerais cependant pas d'utiliser un tel algorithme.

16
Welbog

La complexité temporelle de la factorielle récursive serait:

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

Alors,

La complexité temporelle d'un appel récursif serait:

T(n) = T(n-1) + 3   (3 is for As we have to do three constant operations like 
                 multiplication,subtraction and checking the value of n in each recursive 
                 call)

     = T(n-2) + 6  (Second recursive call)
     = T(n-3) + 9  (Third recursive call)
     .
     .
     .
     .
     = T(n-k) + 3k
     till, k = n

     Then,

     = T(n-n) + 3n
     = T(0) + 3n
     = 1 + 3n

Pour représenter en notation Big-Oh,

T (N) est directement proportionnelle à n,

Par conséquent, la complexité temporelle de la factorielle récursive est O (n). Comme il n'y a pas d'espace supplémentaire pris pendant les appels récursifs, la complexité de l'espace est O (N).

2
nitin aditya