177 votes

Manipulation de très grands nombres en Python

J'ai réfléchi à une évaluation rapide des mains de poker en Python. J'ai pensé qu'une façon d'accélérer le processus serait de représenter toutes les faces et couleurs des cartes sous forme de nombres premiers et de les multiplier ensemble pour représenter les mains. À la rigueur :

class PokerCard:
    faces = '23456789TJQKA'
    suits = 'cdhs'
    facePrimes = [11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 53, 59, 61]
    suitPrimes = [2, 3, 5, 7]

Y

    def HashVal(self):
      return PokerCard.facePrimes[self.cardFace] * PokerCard.suitPrimes[self.cardSuit]

Cela donnerait à chaque main une valeur numérique qui, par modulo, pourrait me dire combien de rois sont dans la main ou combien de cœurs. Par exemple, toute main contenant cinq trèfles ou plus serait divisée de façon égale par 2^5 ; toute main contenant quatre rois serait divisée de façon égale par 59^4, etc.

Le problème est qu'une main de sept cartes comme AcAdAhAsKdKhKs a une valeur de hachage d'environ 62,7 quadrillions, ce qui nécessiterait considérablement plus que 32 bits pour la représenter en interne. Existe-t-il un moyen de stocker de si grands nombres en Python qui me permette d'y effectuer des opérations arithmétiques ?

19 votes

Êtes-vous sûr qu'une fois que vous aurez commencé à représenter vos données de cette manière, vous verrez toujours une amélioration significative de la vitesse ? Je réalise que cela ne répond pas à vos questions, mais quand même

3 votes

J'ai une suggestion : au lieu d'utiliser des variables séparées pour les valeurs des cartes et les représentations, je suggère d'utiliser des dictionnaires. (Ainsi faces = {'2' : 11, '3' : 13, '4' : 17, '5' : 19, '6' : 23, '7' : 29, '8' : 31, '9' : 37, 'T' : 41, 'J' : 43, 'Q' : 53, 'K' : 59, 'A' : 61} et combinaisons = {'c' : 2, 'd' : 3, 'h' : 5, 's' : 7}.)

0 votes

S'appuyer sur la multiplication et la factorisation des entiers semble être une énorme perte de vitesse, et non un gain. Il y aurait probablement aussi une perte de mémoire. Si vous voulez vraiment optimiser le stockage des mains, vous pourriez envisager d'utiliser un champ de bits avec 3 bits par rang (parce que vous pouvez avoir entre 0 et 4 cartes de chaque rang) et 4 bits par couleur. Mais je ne suis pas sûr que Python soit le meilleur langage pour ce genre d'optimisations. Peut-être qu'en utilisant un collections.Counter à la place serait plus simple.

228voto

Ben Blank Points 21786

Python prend en charge un type d'entier "bignum" qui permet de travailler avec des nombres arbitrairement grands. Dans Python 2.5+, ce type est appelé long et est séparée de la int mais l'interprète utilisera automatiquement celui qui est le plus approprié. Dans Python 3.0+, la balise int a été complètement abandonné.

Ce n'est qu'un détail d'implémentation, cependant - tant que vous avez la version 2.5 ou supérieure, il suffit d'effectuer des opérations mathématiques standard et tout nombre qui dépasse les limites des mathématiques 32 bits sera automatiquement (et de manière transparente) converti en un bignum.

Vous pouvez trouver tous les détails sanglants dans PEP 0237 .

3 votes

La question est de savoir si l'impact sur les performances de l'utilisation de bignum au lieu d'entiers 32 bits dépasse le bénéfice en termes de performances de la méthode intelligente d'évaluation manuelle qu'il utilise.

3 votes

En fait, la barrière entre int et long a été brisée en 2.5. La version 3.0 supprime complètement int, faisant de long le seul type entier.

0 votes

@Ignacio - Vous avez raison, j'avais confondu les changements 2.5 et 3.0. J'ai corrigé ma réponse.

113voto

Nuno Aniceto Points 92

Supports python arbitrairement grand site nombres entiers naturellement :

ejemplo:

>>> 10**1000
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Vous pouvez même obtenir, par exemple pour une énorme valeur entière, fib(4000000).

Mais c'est toujours le cas no (pour l'instant) prend en charge un nombre arbitrairement élevé d'utilisateurs. float ! !

Si vous avez besoin d'un gros, gros, flotteur, alors vérifiez le module décimal. Il y a des exemples d'utilisation sur ces foruns : OverflowError : (34, 'Resultat trop grand')

Une autre référence : http://docs.python.org/2/library/decimal.html

Vous pouvez même utiliser le module gmpy si vous avez besoin d'une accélération (ce qui est susceptible de vous intéresser) : Manipuler les grands nombres en code

Une autre référence : https://code.google.com/p/gmpy/

38voto

Vous pourriez le faire pour le plaisir, mais sinon, ce n'est pas une bonne idée. Cela n'accélèrerait rien de ce à quoi je pense.

  • Obtenir les cartes d'une main sera une opération de factorisation d'un nombre entier, ce qui est beaucoup plus coûteux que d'accéder simplement à un tableau.

  • L'ajout de cartes serait une multiplication, et le retrait de cartes une division, toutes deux de grands nombres à plusieurs mots, qui sont des opérations plus coûteuses que l'ajout ou le retrait d'éléments de listes.

  • La valeur numérique réelle d'une main ne vous dira rien. Vous devrez factoriser les nombres premiers et suivre les règles du poker pour comparer deux mains. h1 < h2 pour de telles mains ne signifie rien.

29voto

Autoplectic Points 4581

Python supporte naturellement les entiers de taille arbitraire :

In [1]: 59**3*61**4*2*3*5*7*3*5*7
Out[1]: 62702371781194950
In [2]: _ % 61**4
Out[2]: 0

7 votes

Ce ne sont pas de gros chiffres.

11voto

Hedy Points 879

L'interpréteur python s'en chargera pour vous, vous n'aurez qu'à effectuer vos opérations (+, -, *, /), et cela fonctionnera normalement.

El int La valeur est illimitée.

Attention lors de la division, par défaut le quotient est transformé en float mais float ne supporte pas de si grands nombres. Si vous obtenez un message d'erreur disant float ne prend pas en charge de tels grands nombres, cela signifie que le quotient est trop grand pour être stocké dans le fichier float vous devrez utiliser la division du plancher ( // ).

Il ignore toute décimale qui vient après le point décimal, de cette façon, le résultat sera int Vous pouvez donc obtenir un grand nombre de résultats.

>>>10//3
3

>>>10//4
2

3 votes

Comment votre réponse aborde-t-elle le problème des grands nombres dans la question ?

0 votes

Cela signifie que vous pouvez effectuer les opérations normales avec les grands nombres, mais faites attention à la division.

0 votes

@Hedy Que puis-je/doit-on faire en cas de division ? J'essaie de diviser (10^18 - 1) par 2, et c'est incorrect. Cela me donne un résultat rond 5 * 10^17 au lieu de 49999...99.5. Puis-je faire quelque chose à ce sujet ? J'utilise la division '/'.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X