n! négatif avec python

Bonjour à tous,

Pour comparer les temps d'exécution de deux petits programmes calculant $n!$, j'ai assez naturellement tapé un petit code en python (voir la pièce jointe).

A partir de 20!, on me renvoie des nombres négatifs! Quelqu'un a une explication? Si ça peut aider j'ai fait ça dans un jupyter notebook avec python 3.77498

Réponses

  • np n’utilise pas des entiers illimités.
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Aaaaaaah, merci Nicolas! Je connaissais pas cette histoire d'entiers illimités. Ceci dit 20! c'est pas incroyable comme nombre.
  • Non mais si tu as un int8, tu es limité à 5!.
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Hello,

    Je viens de regarder, np.prod(range(1,6)) fabrique un int64.

    En tout cas j'apprends des trucs grâce à ce souci. Merci encore !
  • Pour que ça marche avec des entiers Python (illimités), il te suffit d'utiliser np.arange et le type dtype=object :
    >>> import numpy as np
    >>> np.prod(np.arange(1,20, dtype=object))
    121645100408832000
    >>> 
    
  • Ou, sans numpy :
    >>> eval("*".join(map(str,range(1,20))))
    121645100408832000
    
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Pas de fonction built-in prod en Python (ça été rejeté par BDFL). Une alternative à eval consiste à utiliser reduce et mul. Par ailleurs, on peut rappeler qu'on a plus à se soucier à recoder factorielle en Python à partir de la version 3.5 puisque la fonction est accessible depuis le module math :
    from functools import reduce 
    from operator import mul
    from math import factorial
    
    N=20
    print(reduce(mul, range(1, N+1), 1))
    print(factorial(N))
    
    2432902008176640000
    2432902008176640000
    
  • @Nicolas : ça fait deux fois que tu nous sers ton "eval" d'une chaine de caractères... Euh, c'est super crade !

    Dans l'autre fil, tu donnes cette variante :
    def prod(liste):
        return eval("*".join(map(str,liste)))
    

    En plus de se comporter de façon erratique sur certaines entrées (que renvoie ton prod(["1+2", 2]) ?), eval est une fonction hyper dangereuse dans la vraie vie, ouvrant la porte en grand aux injections de code.

    Par exemple, quel serait l'effet de :
    #A NE SURTOUT PAS FAIRE !!!!
    prod(["os.system('rm -rf ~')"])
    
    ... Hum ??

    Si on veut à tout prix un one-liner, la solution de Pascal est la bonne.
  • Le code répond 5 parce qu’il évalue d’abord les trucs dans la liste.
    Et les injections de code… certes mais qui va utiliser un tel code dans un endroit où c’est dangereux de le faire ?
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Le code répond 5
    Ok... C'est un résultat "acceptable" ? Je m'attendrais plutôt à 6 (ou même encore mieux une erreur car on essaie de faire un produit entre des quantités de type différents....).
    un tel code

    Tu parles du tien ou de mon exemple "à ne surtout pas faire". Car le mien, ok aucun développeur même débile ne mettrait ça dans son code. Mais si un développeur un peu neuneu prend ta fonction, la fout sur son serveur et propose comme service de calculer des produits de liste, un attaquant pourrait alors proposer de calculer le produit de :
    ["os.system('rm -rf ~')"]
    

    Et badaboum.... Donc non, eval n'est pas une solution pour faire un produit.
  • Pour revenir à la question de Lucas : 20 ! est encore bien calculé par numpy, mais ça crash à partir de 21!. Pas étonnant car $\ln_2(21!) \approx 65$, donc 21 ! ne tient pas sur 64 bits.
  • sebsheep a écrit:
    Et badaboum.... Donc non, eval n'est pas une solution pour faire un produit.

    La solution (que j’ai d’ailleurs attrapée sur ce forum) sert pour calculer un produit d’entiers ; c’était la question initiale, non ?
    Cela dit, la meilleure solution est d’utiliser factorial importé parce que plus rapide et testée par les développeurs de Python.
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Oui, et ? La question n'est pas d’écrire du code professionnel avec toutes les bonnes pratiques de professionnel qui te font écrire trois lignes de code pour choper toutes les éventualités.
    J’ai l’impression d’entendre un prof qui interdit a priori d’utiliser goto, break, continue ou return en milieu de fonction parce que tel livre d’un ponte qu’ils ont feuilleté a dit que c’était mal et que ça tuait des chatons.
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Je ne vois pas la logique entre accepter et promouvoir du code pourri (qui crash hyper rapidement comme je l'ai montré, et ce sans avoir lu le livre d'un quelconque ponte) d'un côté et défendre d'un autre l'introduction de la limite par sa "véritable" définition en 1ere sous prétexte que c'est plus "propre".

    Même sans utiliser "reduce", on peut très simplement et proprement écrire cette fonction "prod" avec une simple boucle sans utiliser "eval". Le forum est aussi lu par des étudiants, il me semble dommageable de leur proposer des solutions dégueu.
  • Elle ne plante pas dans le contexte de la question posée.
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • Je suis completement d'accord avec sebsheep. Ce type de code (avec eval) est peut-etre compact, mais du coup obscur, difficile a maintenir, de surcroit il utilise toute une machinerie inutile (chaines+interpreteur) et est donc inefficace. Pourquoi vouloir eviter d'ecrire explicitement une boucle? C'est clair, facile a maintenir, on controle precisement ce qui se passe et c'est efficace.
  • Ça peut éventuellement servir quand tu as peu de place, par exemple dans un code golf ou quand, justement, tu veux obscurcir ton code (mais là, c’est en C).
    Algebraic symbols are used when you do not know what you are talking about.
            -- Schnoebelen, Philippe
  • L'intérêt d'utiliser eval est pour la culture générale et pour l'anecdote, comme ça on sait au moins que ça existe : eval is evil et donc sinon, on a toutes les raisons de ne jamais savoir ce que c'est (:P)

    Après eval a ses usages, il suffit de faire une recherche dans le code source de CPython, numpy, ntkl, sympy et sans doute beaucoup d'autres.

    Dans le livre de Swinnen il y a une utilisation d' eval dans le contexte de réaliser en Tkinter une "calculatrice" ce qui évite à l'apprenti programmeur d'avoir à écrire un parseur d'expressions arithmétiques.

    Pour le C, pas de fonction standard eval peut-être en C interprété.
Connectez-vous ou Inscrivez-vous pour répondre.