Manipulando números muito grandes em Python

140

Eu estive considerando uma avaliação rápida das mãos de poker em Python. Ocorreu-me que uma maneira de acelerar o processo seria representar todos os rostos e naipes de cartas como números primos e multiplicá-los para representar as mãos. Para 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]

E

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

Isso daria a cada mão um valor numérico que, através do módulo, poderia me dizer quantos reis existem na mão ou quantos corações. Por exemplo, qualquer mão com cinco ou mais tacos dividiria uniformemente por 2 ^ 5; qualquer mão com quatro reis se dividiria uniformemente por 59 ^ 4, etc.

O problema é que uma mão de sete cartas como AcAdAhAsKdKhKs tem um valor de hash de aproximadamente 62,7 quadrilhões, o que levaria consideravelmente mais de 32 bits para representar internamente. Existe uma maneira de armazenar números tão grandes em Python que me permita executar operações aritméticas nele?

Sim - aquele Jake.
fonte
13
Tem certeza de que, depois de começar a representar seus dados dessa maneira, você ainda verá uma melhoria significativa na velocidade? Sei que isso não responder às suas perguntas, mas ainda ..
Thomi
3
Tenho uma sugestão: em vez de usar variáveis ​​separadas para os valores do cartão e as representações, sugiro usar dicionários. (Então, rostos = {'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} e naipes = {'c': 2, 'd': 3, ' h ': 5,' s ': 7}.)
JAB

Respostas:

177

O Python suporta um tipo inteiro "bignum" que pode funcionar com números arbitrariamente grandes. No Python 2.5+, esse tipo é chamado longe é separado do inttipo, mas o intérprete usará automaticamente o que for mais apropriado. No Python 3.0+, o inttipo foi descartado completamente.

Porém, isso é apenas um detalhe de implementação - desde que você tenha a versão 2.5 ou melhor, apenas execute operações matemáticas padrão e qualquer número que exceda os limites da matemática de 32 bits será automaticamente (e transparentemente) convertido em um bignum.

Você pode encontrar todos os detalhes sangrentos no PEP 0237 .

Ben Blank
fonte
2
A questão é: o desempenho atingido pelo uso de bignum em vez de números inteiros de 32 bits excede o benefício de desempenho do método inteligente de avaliação manual que ele está usando.
21720 Chris Upchurch
3
Na verdade, a barreira entre int e long foi quebrada na versão 2.5. 3.0 remove int completamente, tornando longo o único tipo inteiro.
Ignacio Vazquez-Abrams
1
Qual é o tamanho de um número grande? Pode ser PHI ^ 4000000?
Mike Caron
9
@ Mike Caron - Se a estrutura listada no PEP 0237 for precisa, longos comprimentos de s (em dígitos) serão armazenados como números inteiros de 32 bits sem sinal, até 4.294.967.295 dígitos, o que significa que eles podem facilmente segurar hold ** (4 * 10 ** 6 ), que é "apenas" 832.951 dígitos. No entanto, φ não é um número inteiro; portanto, você precisará usar um decimal (bignum de ponto flutuante do Python) para calcular o número. Você pode armazenar o resultado longposteriormente, no entanto.
Ben Blank
17
@ IgnacioVazquez-Abrams Apenas um ponto de esclarecimento, longé o único tipo inteiro no 3.0, mas é chamado int. (E o velho intse foi.)
Michael Mior
70

python suporta inteiros arbitrariamente grandes naturalmente:

exemplo:

>>>10 ** 1000 100000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Você pode até obter, por exemplo, um valor inteiro enorme, fib (4000000).

Mas ainda assim ele faz não (por enquanto) suporta uma arbitrariamente grande bóia !!

Se você precisar de um flutuador grande, grande, verifique o módulo decimal. Existem exemplos de uso nesses fóruns: OverflowError: (34, 'Resultado muito grande')

Outra referência: http://docs.python.org/2/library/decimal.html

Você pode até usar o módulo gmpy se precisar de uma aceleração (o que provavelmente é do seu interesse): Manipular grandes números no código

Outra referência: https://code.google.com/p/gmpy/

Nuno Aniceto
fonte
33

Você pode fazer isso por diversão, mas fora isso não é uma boa ideia. Não aceleraria nada que eu pudesse pensar.

  • Colocar as cartas em uma mão será uma operação de fatoração inteira que é muito mais cara do que apenas acessar uma matriz.

  • Adicionar cartões seria multiplicação e remover divisão de cartões, ambos grandes números com várias palavras, que são operações mais caras do que adicionar ou remover elementos das listas.

  • O valor numérico real de uma mão não diz nada. Você precisará fatorar os números primos e seguir as regras do Poker para comparar as duas mãos. h1 <h2 para essas mãos não significa nada.


fonte
25

python suporta inteiros 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
fonte
3

O intérprete python cuidará disso para você, você só precisa fazer suas operações (+, -, *, /) e funcionará normalmente.

O intvalor é ilimitado.

Cuidado ao fazer a divisão, por padrão, o quociente é transformado float, mas floatnão suporta números tão grandes. Se você receber uma mensagem de erro dizendo floatque não suporta números tão grandes, significa que o quociente é muito grande para ser armazenado. floatVocê precisará usar a divisão do piso ( //).

Ele ignora qualquer decimal que vem depois do ponto decimal, assim o resultado será int, para que você possa obter um resultado numérico grande.

10//3 Saídas 3

10//4 saídas 2

Hedy
fonte
1
Como sua resposta aborda a questão de grandes números na pergunta?
StupidWolf 11/11/19
Isso significa que você pode apenas fazer as operações normais com números grandes, mas tenha cuidado com a divisão
Hedy