Calcul de factorielle de grands nombres en C

Dans mon code C, je souhaite calculer la factorielle pour les nombres compris entre 1 et 100. Pour les petits nombres, la fonction fonctionne, mais pour les grands nombres, par exemple 100! il retourne un résultat incorrect. Tout moyen de gérer factorielle de grands nombres dans C?. Le compilateur qui utilise est gcc v4.3.3. Mon code est le suivant:

#include  #include  double print_solution(int); int main(void) { int no_of_inputs,n ; int ctr = 1; scanf("%d",&no_of_inputs); //Read no of inputs do { scanf("%d",&n); //Read the input printf("%.0f\n",print_solution(n)); ctr++; }while(ctr <= no_of_inputs); return 0; } double print_solution(int n) { if(n == 0 || n == 1) return 1; else return n*print_solution(n-1); } 

    Aucun type de données C standard ne traitera avec précision des nombres allant jusqu’à 100 !. Votre seule option si vous utilisez une arithmétique entière de précision arbitraire , via une bibliothèque ou par vous-même.

    S’il ne s’agit que d’un projet de loisir, je vous conseillerais de l’essayer vous-même. C’est un exercice amusant. Si cela est lié au travail, utilisez une bibliothèque préexistante.

    Le type de données C le plus important que vous obtiendrez normalement est un entier de 64 bits. 100! est de l’ordre de 10 157 , ce qui prend la meilleure partie de 500 bits à stocker avec précision sous forme d’entier.

    100 factorial est énorme, pour être précis c’est 93326215443944152681699238856266700490715968264381621468592963895217 5999932299156089414639761565282825256259256259256259

    Peut-être devriez-vous utiliser une bibliothèque bignum comme GMP . Il a une belle documentation, une interface assez cohérente, rapide et si vous êtes sous Linux, votre dissortingbution a probablement un paquet (je pense que le mien l’installe par défaut)

    Pour calculer approximativement les factorielles de grands nombres, vous pouvez utiliser cette méthode:

     n!  = n * (n-1)!
     alors log (n!) = log (n) + log (n-1!)
    

    Vous pouvez maintenant utiliser la programmation dynamic pour calculer log (n!) Et calculer
    n! as (base) ^ (valeur logarithmique)

    Si vous ne souhaitez pas utiliser une bibliothèque bigint, le mieux que vous puissiez faire avec stdlib utilise long double et tgammal() de math.h :

     long double fact(unsigned n) { return tgammal(n + 1); } 

    Cela vous donnera 100! avec une précision de 18 décimales sur x86 (soit un long double 80 bits).

    Une implémentation exacte n’est pas compliquée non plus:

     #include  #include  #include  void multd(char * s, size_t len, unsigned n) { unsigned values[len]; memset(values, 0, sizeof(unsigned) * len); for(size_t i = len; i--; ) { unsigned x = values[i] + (s[i] - '0') * n; s[i] = '0' + x % 10; if(i) values[i - 1] += x / 10; } } void factd(char * s, size_t len, unsigned n) { memset(s, '0', len - 1); s[len - 1] = '1'; for(; n > 1; --n) multd(s, len, n); } int main(void) { unsigned n = 100; size_t len = ceill(log10l(tgammal(n + 1))); char dstr[len + 1]; dstr[len] = 0; factd(dstr, len, n); puts(dstr); } 

    Tout le monde vous dit la bonne réponse, mais quelques points supplémentaires.

    1. Votre idée initiale d’utiliser un double pour obtenir une plage plus large ne fonctionne pas car un double ne peut pas stocker ces données avec précision. Il peut faire les calculs avec beaucoup d’arrondi. C’est pourquoi les librairies bigint existent.

    2. Je sais que ceci est probablement un exemple tiré d’un site de tutoriel ou d’exemples, mais une récursion sans limite vous mordra à un moment donné. Vous avez une solution récursive pour ce qui est essentiellement un processus itératif. Vous comprendrez pourquoi ce site porte le même nom lorsque vous essayez d’exécuter votre programme avec des valeurs plus grandes (Try 10000).

    Une approche itérative simple est la suivante

      int answer, idx; for (answer = 1, idx = 1; idx <= no_of_inputs; idx++ ) { answer = answer * idx; } printf("Factorial of %3d = %d\n", no_of_inputs, answer); 

    Voici ce que j’ai fait pour résoudre une énigme Google il ya quelques années, elle utilise la bibliothèque GMP http://gmplib.org/ :

     #include  #include "gmp.h" void fact(mpz_t r,int n){ unsigned int i; mpz_t temp; mpz_init(temp); mpz_set_ui(r,1); for(i=1;i<=n;i++){ mpz_set_ui(temp,i); mpz_mul(r,r,temp); } mpz_clear(temp); } int main(void) { mpz_t r; mpz_init(r); fact(r,188315); /* fact(r,100); */ gmp_printf("%Zd\n",r); mpz_clear(r); return(0); } 

    gcc -lgmp -o fact fact.c

    ./fait

    vous pouvez essayer le type “unsigned long long”, mais c’est le maximum que vous pouvez obtenir avec les types intégrés. Je suggérerais (comme Cletus l’a déjà mentionné) soit d’utiliser une implémentation connue de grands nombres, soit d’en écrire un vous-même. “c’est un bel exercice” x 2.

    Si vous souhaitez utiliser uniquement les types de données standard et que vous n’avez pas besoin de la réponse exacte, calculez le logarithme de n! au lieu de n! lui-même. Le logarithme de n! se glisse facilement dans un double (sauf si n est énorme).

    Des moyens de gérer factorielle de grands nombres en C?

    Étant donné que les factorielles peuvent rapidement dépasser la plage des entiers standard à largeur fixe et même des types à virgule flottante tels que double , Code doit envisager un type d’utilisateur permettant une précision entière sans bornes pour une réponse exacte .

    Il existe diverses bibliothèques de précision de nombre entier, mais si le code nécessite une solution simple, envisagez d’utiliser une chaîne .

    L’inférieur n’est pas rapide, ni conscient des limites des tableaux, mais illustre bien l’idée. La conversion de '0'-'9' en / de 0-9 à 0-9 est une perte de temps, mais elle permet un débogage facile, étape par étape.

     #include  #include  #include  static char *strfact_mult(char *s, unsigned x) { unsigned sum = 0; size_t len = strlen(s); size_t i = len; while (i > 0) { sum += (s[--i] - '0') * x; s[i] = sum % 10 + '0'; sum /= 10; } while (sum) { len++; memmove(&s[1], s, len); s[i] = sum % 10 + '0'; sum /= 10; } return s; } char *str_fact(char *dest, unsigned n) { strcpy(dest, "1"); while (n > 1) { strfact_mult(dest, n--); } return dest; } void test_fact(unsigned n) { char s[1000]; printf("%3u %s\n", n, str_fact(s, n)); } int main(void) { test_fact(0); test_fact(4); test_fact(54); test_fact(100); } 

    Sortie

      0 1 4 24 54 230843697339241380472092742683027581083278564571807941132288000000000000 100 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 

    Je suppose que c’est parce que vous débordez de la plage int, qui est jusqu’à env. 2 milliards Vous pouvez obtenir jusqu’à 4 milliards si vous utilisez unsigned int, mais au-delà, vous devez utiliser la bibliothèque bigint .

    100! = 933262154439441526816992388562667004907159282643816214685929 63895217599993229915608941439628625628925289258859296859299

    Vous ne pouvez pas représenter un nombre aussi grand avec un int ou un long.

    Ceci est très certainement dû au débordement. Vous avez besoin d’un moyen de représenter de grands nombres ( unsigned long long ne couvrira même pas jusqu’à 25!).

    En plus des conseils des autres utilisateurs, je vous conseillerais de vous familiariser avec les limites de stockage des types de base (int, long, long long, …) pour l’ordinateur / la plateforme que vous utilisez réellement. (“En cas de doute, imprimez plus!”)

    Une précédente affiche faisait référence à une limite de précision de 80 bits, mais cela est propre à un processeur x86.

    Une autre personne a cité ISO C90 à plusieurs resockets, bien que C99 soit la dernière norme en date; même si de nombreux compilateurs n’ont pas complètement implémenté C99, vous constaterez probablement qu’ils ont au moins un support très long, ce qui devrait correspondre à une précision> = 64 bits.

    Calculer de grands factoriels sans aucune bibliothèque externe

    C’est vraiment un vieux problème. Je vois la plupart des réponses suggérant une bibliothèque externe ou un résultat approximatif , montrant une limitation de la mémoire. Mais, pensez légèrement différent – vous n’avez pas toujours besoin d’utiliser un integer un double ou un unsigned long long pour programmer des mathématiques!


    J’ai utilisé int[] pour calculer de grands factoriels . Ce petit code Java peut (théoriquement) trouver une factorielle d’ un nombre quelconque

     public class BigFactorial { public static int[] calculateFactorial(int inputNumber) { int[] factorial = initializeFactorial(inputNumber); for(int i=inputNumber-1, j, k; i>0; i--){ for(j=factorial.length-1, k=0; factorial[j] >= 0; j--){ k += i*factorial[j]; factorial[j] = k%10; k /= 10; } factorial[j] = k%10; k /= 10; factorial[j-1] = k; for(j=0; factorial[j]<1; j++){ factorial[j] = -1; } } return factorial; } private static int[] initializeFactorial(int inputNumber){ int digits = (int) Math.ceil(inputNumber*Math.log10(inputNumber/2))+2; int[] factorial = new int[digits+1]; for(int i=0; i0; j--){ factorial[j] = i%10; i /= 10; } return factorial; } public static void showOutput(int[] factorial){ int i=0; while(factorial[i]<1){ i++; } for(; i 

    N’utilisez pas l’algorithme récursif, je pense, il est super lent, même s’il est mis en cache, il sera lent. C’est juste quelque chose que vous devriez considérer.

    La raison en est que lorsque vous appelez fact (100), vous ne l’exécutez pas réellement 100 fois, vous exécutez cette fonction 5050 fois. Ce qui est mauvais, s’il est mis en cache, cela pourrait être 100 fois. Cependant, il est encore plus lent d’exécuter un appel de fonction avec les instructions if puis d’exécuter une boucle.

     double print_solution(int n) { double rval = 1; unsigned int i; for( i = 1; i <= n; i++ ) { rval *= i; } return rval; } 

    En utilisant une arithmétique de précision arbitraire, vous pouvez la faire monter très haut. Cependant, vous devez utiliser une bibliothèque pour le faire, ou vous pouvez créer votre propre bibliothèque, mais cela prendrait beaucoup de temps.