Manejo de números muy grandes en Python

140

He estado considerando la evaluación rápida de manos de póker en Python. Se me ocurrió que una forma de acelerar el proceso sería representar todas las caras y palos de las cartas como números primos y multiplicarlos para representar las manos. A whit:

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]

Esto le daría a cada mano un valor numérico que, a través del módulo, podría decirme cuántos reyes hay en la mano o cuántos corazones. Por ejemplo, cualquier mano con cinco o más clubes se dividiría en partes iguales por 2 ^ 5; cualquier mano con cuatro reyes se dividiría en partes iguales por 59 ^ 4, etc.

El problema es que una mano de siete cartas como AcAdAhAsKdKhKs tiene un valor hash de aproximadamente 62.7 billones, lo que tomaría considerablemente más de 32 bits para representar internamente. ¿Hay alguna manera de almacenar números tan grandes en Python que me permitan realizar operaciones aritméticas en él?

Sí, ese Jake.
fuente
13
¿Está seguro de que, una vez que comience a representar sus datos de esta manera, aún verá una mejora significativa en la velocidad? Me doy cuenta de que esto no responde a sus preguntas, pero aún así ...
Thomi
3
Tengo una sugerencia: en lugar de usar variables separadas para los valores de la tarjeta y las representaciones, sugiero usar diccionarios. (Entonces caras = {'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} y trajes = {'c': 2, 'd': 3, ' h ': 5,' s ': 7}.)
JAB

Respuestas:

177

Python admite un tipo entero "bignum" que puede funcionar con números arbitrariamente grandes. En Python 2.5+, este tipo se llama longy es independiente del inttipo, pero el intérprete usará automáticamente el que sea más apropiado. En Python 3.0+, el inttipo se ha eliminado por completo.

Sin embargo, eso es solo un detalle de implementación: siempre que tenga la versión 2.5 o superior, solo realice operaciones matemáticas estándar y cualquier número que exceda los límites de las matemáticas de 32 bits se convertirá automáticamente (y de manera transparente) en un bignum.

Puede encontrar todos los detalles sangrientos en PEP 0237 .

Ben Blank
fuente
2
La pregunta es si el rendimiento del uso de bignum en lugar de los enteros de 32 bits excede el beneficio de rendimiento del método inteligente de evaluación manual que está utilizando.
Chris Upchurch
3
En realidad, la barrera entre int y long se rompió en 2.5. 3.0 elimina int por completo, lo que hace que sea el único tipo entero.
Ignacio Vazquez-Abrams
1
¿Qué tan grande es un gran número? ¿Puede ser PHI ^ 4000000?
Mike Caron el
9
@ Mike Caron: si la estructura listada en PEP 0237 es precisa, longlas longitudes de s (en dígitos) se almacenan como enteros de 32 bits sin signo, hasta 4,294,967,295 dígitos, lo que significa que pueden contener fácilmente φ ** (4 * 10 ** 6 ), que es "solo" 832,951 dígitos. Sin embargo, φ no es un número entero, por lo que necesitará usar un Decimal (punto de coma flotante de Python) para calcular el número. longSin embargo, puede almacenar el resultado posteriormente.
Ben Blank
17
@ IgnacioVazquez-Abrams Solo un punto de aclaración, longes el único tipo entero en 3.0, pero se llama así int. (Y lo viejo intse fue.)
Michael Mior
70

Python admite enteros arbitrariamente grandes naturalmente:

ejemplo:

>>>10 ** 1000 100000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.

Incluso podría obtener, por ejemplo, un valor entero enorme, fib (4000000).

¡Pero todavía no es compatible (por ahora) con un flotador arbitrariamente grande !

Si necesita un flotador grande, grande, compruebe el módulo decimal. Hay ejemplos de uso en estos foros: OverflowError: (34, 'Resultado demasiado grande')

Otra referencia: http://docs.python.org/2/library/decimal.html

Incluso puede usar el módulo gmpy si necesita una aceleración (que probablemente sea de su interés): manejo de números grandes en el código

Otra referencia: https://code.google.com/p/gmpy/

Nuno Aniceto
fuente
33

Podrías hacer esto por diversión, pero aparte de eso, no es una buena idea. No aceleraría nada en lo que pueda pensar.

  • Obtener las tarjetas en una mano será una operación de factorización de enteros que es mucho más costosa que simplemente acceder a una matriz.

  • Agregar tarjetas sería una multiplicación y eliminar la división de tarjetas, tanto de números grandes de varias palabras, que son operaciones más caras que agregar o eliminar elementos de las listas.

  • El valor numérico real de una mano no le dirá nada. Necesitarás factorizar los números primos y seguir las reglas de Poker para comparar dos manos. h1 <h2 para tales manos no significa nada.


fuente
25

Python admite enteros arbitrariamente grandes naturalmente:

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

El intérprete de Python lo manejará por usted, solo tiene que hacer sus operaciones (+, -, *, /), y funcionará normalmente.

El intvalor es ilimitado.

Tenga cuidado al hacer la división, por defecto, el cociente se convierte float, pero floatno admite números tan grandes. Si recibe un mensaje de error que dice floatque no admite números tan grandes, significa que el cociente es demasiado grande para ser almacenado float, tendrá que usar la división de piso ( //).

Ignora cualquier decimal que viene después del punto decimal, de esta manera, el resultado será int, por lo que puede tener un resultado de gran número.

10//3 Salidas 3

10//4 salidas 2

Hedy
fuente
1
¿Cómo aborda su respuesta el problema de los números grandes en la pregunta?
StupidWolf
Significa que puede hacer las operaciones normales con grandes números, pero tenga cuidado con la división
Hedy