Maneira mais leve de criar uma string aleatória e um número hexadecimal aleatório


96

Qual é a maneira mais leve de criar uma string aleatória de 30 caracteres como a seguinte?

ufhy3skj5nca0d2dfh9hwd2tbk9sw1

E um número hexadecimal de 30 dígitos como o seguinte?

8c6f78ac23b4a7b8c0182d7a89e9b1


3
Como (por quê?) Nenhuma das respostas (bem elaboradas) foi aceita?
r2evans de

Respostas:


127

Eu tenho um mais rápido para a saída hexadecimal. Usando o mesmo t1 e t2 acima:

>>> t1 = timeit.Timer("''.join(random.choice('0123456789abcdef') for n in xrange(30))", "import random")
>>> t2 = timeit.Timer("binascii.b2a_hex(os.urandom(15))", "import os, binascii")
>>> t3 = timeit.Timer("'%030x' % random.randrange(16**30)", "import random")
>>> for t in t1, t2, t3:
...     t.timeit()
... 
28.165037870407104
9.0292739868164062
5.2836320400238037

t3 faz apenas uma chamada para o módulo aleatório, não tem que construir ou ler uma lista e então faz o resto com formatação de string.


5
Agradável. Basta gerar um número aleatório de 30 dígitos hexadecimais e imprimi-lo. Óbvio quando apontado. Agradável.
eemz

Interessante, eu meio que esqueci que Python (e o módulo aleatório) lida com bigints nativamente.
wump

3
Veja a resposta de yaronf abaixo sobre como usar string.hexdigits: stackoverflow.com/a/15462293/311288 " string.hexdigitsretorna 0123456789abcdefABCDEF(tanto em minúsculas quanto em maiúsculas), [...]. Em vez disso, apenas use random.choice('0123456789abcdef')."
Thomas

3
Use em getrandbitsvez de randrangepara torná-lo ainda mais rápido.
robinst

@robinst tem um bom argumento. '%030x' % random.getrandbits(60)é ainda mais rápido do que '%030x' % random.randrange(16**30), provavelmente porque não precisa fazer nenhuma conversão de / para big-ints
Dan Lenski

81

String hexadecimal de 30 dígitos:

>>> import os,binascii
>>> print binascii.b2a_hex(os.urandom(15))
"c84766ca4a3ce52c3602bbf02ad1f7"

A vantagem é que ele obtém aleatoriedade diretamente do sistema operacional, que pode ser mais seguro e / ou mais rápido do que random (), e você não precisa gerá-lo.


isso é interessante e provavelmente uma boa escolha para gerar o número hexadecimal de 30 dígitos que ele deseja. provavelmente poderia usar urandom e um operador de fatia para gerar a string alfanumérica também.
eemz

Eu dei uma olhada nas outras funções em binascii, elas têm base64 e uuencode, mas nenhuma maneira de gerar o primeiro tipo de string que ele quer (base36).
wump

1
Isso é aleatório / exclusivo o suficiente para ser usado em, digamos, tokens de sessão?
moraes

1
R: Não. A resposta foi encontrada
moraes

Como especificar o comprimento?
3kstc

55

No Py3.6 +, outra opção é usar o novo secretsmódulo padrão :

>>> import secrets
>>> secrets.token_hex(15)
'8d9bad5b43259c6ee27d9aadc7b832'
>>> secrets.token_urlsafe(22)   # may include '_-' unclear if that is acceptable
'teRq7IqhaRU0S3euX1ji9f58WzUkrg'

28
import string
import random
lst = [random.choice(string.ascii_letters + string.digits) for n in xrange(30)]
str = "".join(lst)
print str
ocwbKCiuAJLRJgM1bWNV1TPSH0F2Lb

6
e random.choice (string.hexdigits)
eemz

1
Pode-se preferir o mais criptograficamente segurorandom.SystemRandom().choice
Brian M. Hunt

1
xrange () deve ser range () - NameError: nome 'xrange' não está definido
Caleb Bramwell

xrange está correto (e geralmente melhor) em python 2.x
jcdyer

25

Solução dramaticamente mais rápida do que as aqui:

timeit("'%0x' % getrandbits(30 * 4)", "from random import getrandbits")
0.8056681156158447

Um usuário deve tentar todos os métodos na mesma máquina para obter uma linha de base precisa. As especificações de hardware podem fazer uma grande diferença. >>>> timeit.timeit ("'% 0x'% getrandbits (30 * 4)", "de importação aleatória getrandbits") 0,2471246949999113 </pre>
ptay

Obrigado, isso muito mais rápido do que os outros acima. %timeit '%030x' % randrange(16**30)dá 1.000.000 de loops, melhor de 3: 1,61 µs por loop, enquanto %timeit '%0x' % getrandbits(30 * 4)dá 1.000.000 de loops, melhor de 3: 396 ns por loop
frmdstryr

15

Nota: random.choice(string.hexdigits)está incorreto, porque string.hexdigitsretorna 0123456789abcdefABCDEF(tanto em minúsculas quanto em maiúsculas), então você obterá um resultado tendencioso, com o dígito hex 'c' duas vezes mais provável de aparecer do que o dígito '7'. Em vez disso, basta usar random.choice('0123456789abcdef').


6

Outro Método:

from Crypto import Random
import binascii

my_hex_value = binascii.hexlify(Random.get_random_bytes(30))

O ponto é: o valor do byte é sempre igual ao valor em hexadecimal .


5

função de uma linha:

import random
import string

def generate_random_key(length):
    return ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(length))

print generate_random_key(30)

3
In [1]: import random                                    

In [2]: hex(random.getrandbits(16))                      
Out[2]: '0x3b19'

Para sua informação: esta resposta foi sinalizada como de baixa qualidade, você pode querer melhorá-la.
oguz ismail

Alguma sugestão de melhoria?
Bob

Nenhuma idéia. Achei que você deveria saber disso
oguz ismail

2

A propósito, este é o resultado do uso timeitdas duas abordagens que foram sugeridas:

Usando random.choice():

>>> t1 = timeit.Timer("''.join(random.choice(string.hexdigits) for n in xrange(30))", "import random, string")
>>> t1.timeit()
69.558588027954102

Usando binascii.b2a_hex():

>>> t2 = timeit.Timer("binascii.b2a_hex(os.urandom(15))", "import os, binascii")
>>> t2.timeit()
16.288421154022217

2

Existe um mais rápido em comparação com o que jcdyer mencionou. Isso leva cerca de 50% de seu método mais rápido.

from numpy.random.mtrand import RandomState
import binascii
rand = RandomState()

lo = 1000000000000000
hi = 999999999999999999
binascii.b2a_hex(rand.randint(lo, hi, 2).tostring())[:30]

>>> timeit.Timer("binascii.b2a_hex(rand.randint(lo,hi,2).tostring())[:30]", \
...                 'from __main__ import lo,hi,rand,binascii').timeit()
1.648831844329834         <-- this is on python 2.6.6
2.253110885620117         <-- this on python 2.7.5

Se você quiser em base64:

binascii.b2a_base64(rand.randint(lo, hi, 3).tostring())[:30]

Você pode alterar o parâmetro de tamanho passado para randint (último argumento) para variar o comprimento de saída com base em seus requisitos. Então, para um de 60 caracteres:

binascii.b2a_hex(rand.randint(lo, hi, 4).tostring())[:60]

Uma ligeira variação: binascii.b2a_hex(np.random.rand(np.ceil(N/16)).view(dtype=int))[:N]onde N=30.
dan-man

@ dan-man Obrigado por este método opcional. No entanto, acho que ele consome pelo menos 5 vezes mais tempo. Você percebe isso também?
Ethan

0

adicionando mais uma resposta à combinação que tem um desempenho mais rápido do que a solução @eemz e também é totalmente alfanumérica. Observe que isso não fornece uma resposta hexadecimal.

import random
import string

LETTERS_AND_DIGITS = string.ascii_letters + string.digits

def random_choice_algo(width):
  return ''.join(random.choice(LETTERS_AND_DIGITS) for i in range(width))

def random_choices_algo(width):
  return ''.join(random.choices(LETTERS_AND_DIGITS, k=width))


print(generate_random_string(10))
# prints "48uTwINW1D"

um rápido benchmark rende

from timeit import timeit
from functools import partial

arg_width = 10
print("random_choice_algo", timeit(partial(random_choice_algo, arg_width)))
# random_choice_algo 8.180561417000717
print("random_choices_algo", timeit(partial(random_choices_algo, arg_width)))
# random_choices_algo 3.172438014007639

0

Esta certamente não é a versão mais leve, mas é aleatória e é fácil de ajustar o alfabeto / comprimento que você deseja:

import random

def generate(random_chars=12, alphabet="0123456789abcdef"):
    r = random.SystemRandom()
    return ''.join([r.choice(alphabet) for i in range(random_chars)])
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.