Gérer de très grands nombres en Python

140

J'ai envisagé une évaluation rapide des mains de poker en Python. Il m'est apparu qu'une façon d'accélérer le processus serait de représenter toutes les faces et combinaisons de cartes sous forme de nombres premiers et de les multiplier ensemble pour représenter les mains. Pour Pentecôte:

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]

ET

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

Cela donnerait à chaque main une valeur numérique qui, via modulo, pourrait me dire combien de rois sont dans la main ou combien de cœurs. Par exemple, n'importe quelle main avec cinq clubs ou plus se diviserait également par 2 ^ 5; n'importe quelle main avec quatre rois se diviserait également par 59 ^ 4, etc.

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

Oui - ce Jake.
la source
13
Êtes-vous sûr qu'une fois que vous aurez commencé à représenter vos données de cette manière, vous constaterez toujours une amélioration significative de la vitesse? Je me rends compte que cela ne répond pas à vos questions, mais quand même ..
Thomi
3
J'ai une suggestion: au lieu d'utiliser des variables séparées pour les valeurs de la carte et les représentations, je suggère d'utiliser des dictionnaires. (Donc, 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 costumes = {'c': 2, 'd': 3, ' h ': 5,' s ': 7}.)
JAB

Réponses:

177

Python prend en charge un type entier "bignum" qui peut fonctionner avec des nombres arbitrairement grands. Dans Python 2.5+, ce type est appelé longet est distinct du inttype, mais l'interpréteur utilisera automatiquement celui qui est le plus approprié. Dans Python 3.0+, le inttype a été complètement supprimé.

Ce n'est qu'un détail d'implémentation, cependant - tant que vous avez la version 2.5 ou supérieure, effectuez simplement 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 bignum.

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

Ben Blank
la source
2
La question est de savoir si la performance obtenue grâce à l'utilisation de bignum au lieu d'entiers 32 bits dépasse les avantages de performance de la méthode intelligente d'évaluation manuelle qu'il utilise.
Chris Upchurch
3
En fait, la barrière entre int et long a été brisée en 2.5. 3.0 supprime complètement int, faisant de long le seul type entier.
Ignacio Vazquez-Abrams
1
Quelle est la taille d'un grand nombre? Peut-il s'agir de PHI ^ 4000000?
Mike Caron
9
@Mike Caron - Si la structure répertoriée dans PEP 0237 est précise, longles longueurs de s (en chiffres) sont stockées sous forme d'entiers 32 bits non signés, jusqu'à 4294967295 chiffres, ce qui signifie qu'ils peuvent facilement contenir φ ** (4 * 10 ** 6 ), qui ne contient «que» 832 951 chiffres. Cependant, φ n'est pas un entier, vous devrez donc utiliser un décimal (bignum à virgule flottante de Python) pour calculer le nombre. longCependant, vous pouvez stocker le résultat dans une suite.
Ben Blank
17
@ IgnacioVazquez-Abrams Juste un point de clarification, longest le seul type entier dans 3.0, mais il est nommé int. (Et l'ancien intest parti.)
Michael Mior
70

python prend naturellement en charge les entiers arbitrairement grands :

exemple:

>>>10 ** 1000 100000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000

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

Mais il ne supporte toujours pas (pour l'instant) un flotteur arbitrairement grand !!

Si vous avez besoin d'un grand, grand flotteur, vérifiez le module décimal. Il existe des exemples d'utilisation sur ces foruns: OverflowError: (34, 'Result too large')

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): Gestion de gros nombres dans le code

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

Nuno Aniceto
la source
33

Vous pouvez le faire pour le plaisir, mais à part ça, ce n'est pas une bonne idée. Cela n'accélérerait rien à quoi je puisse penser.

  • Obtenir les cartes dans une main sera une opération de factorisation entière qui est beaucoup plus coûteuse que d'accéder simplement à un tableau.

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

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


la source
25

python prend naturellement en charge les entiers arbitrairement grands:

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

L'interpréteur python le gérera pour vous, il vous suffit de faire vos opérations (+, -, *, /), et cela fonctionnera normalement.

La intvaleur est illimitée.

Attention lors de la division, par défaut, le quotient est converti en float, mais floatne prend pas en charge ces grands nombres. Si vous obtenez un message d'erreur indiquant que floatne prend pas en charge ces grands nombres, cela signifie que le quotient est trop grand pour être stocké dans floatvous devrez utiliser floor division ( //).

Il ignore toute décimale qui vient après la virgule décimale, de cette façon, le résultat sera int, donc vous pouvez avoir un résultat en grand nombre.

10//3 Les sorties 3

10//4 les sorties 2

Hedy
la source
1
Comment votre réponse aborde-t-elle le problème des grands nombres dans la question?
StupidWolf
Cela signifie que vous pouvez simplement faire les opérations normales avec un grand nombre, mais soyez prudent avec la division
Hedy