La forma más ligera de crear una cadena aleatoria y un número hexadecimal aleatorio

94

¿Cuál es la forma más ligera de crear una cadena aleatoria de 30 caracteres como la siguiente?

ufhy3skj5nca0d2dfh9hwd2tbk9sw1

¿Y un número hexadecimal de 30 dígitos como el siguiente?

8c6f78ac23b4a7b8c0182d7a89e9b1

xRobot
fuente
3
¿Cómo (¿por qué?) Es que ninguna de las respuestas (bien elaboradas) ha sido aceptada?
r2evans

Respuestas:

124

Conseguí uno más rápido para la salida hexadecimal. Usando los mismos t1 y t2 que arriba:

>>> 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 solo hace una llamada al módulo aleatorio, no tiene que construir o leer una lista, y luego hace el resto con formato de cadena.

jcdyer
fuente
5
Agradable. Simplemente genere un número aleatorio de 30 dígitos hexadecimales e imprímalo. Obvio cuando se señala. Buena esa.
eemz
Interesante, me olvidé de que Python (y el módulo aleatorio) maneja bigints de forma nativa.
wump
3
Vea la respuesta de yaronf a continuación sobre el uso string.hexdigits: stackoverflow.com/a/15462293/311288 " string.hexdigitsdevuelve 0123456789abcdefABCDEF(tanto en minúsculas como en mayúsculas), [...]. En su lugar, solo use random.choice('0123456789abcdef')".
Thomas
3
Úselo en getrandbitslugar de randrangepara hacerlo aún más rápido.
Robinst
@robinst tiene un buen punto. '%030x' % random.getrandbits(60)es incluso más rápido que '%030x' % random.randrange(16**30), probablemente porque no tiene que hacer ninguna conversión hacia / desde grandes empresas
Dan Lenski
79

Cadena hexadecimal de 30 dígitos:

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

La ventaja es que obtiene la aleatoriedad directamente del sistema operativo, que puede ser más seguro y / o más rápido que random (), y no es necesario que lo sembre.

wump
fuente
eso es interesante y probablemente una buena opción para generar el número hexadecimal de 30 dígitos que desea. probablemente podría usar urandom y un operador de corte para generar también la cadena alfanumérica.
eemz
Eché un vistazo a las otras funciones en binascii, tienen base64 y uuencode, pero no hay forma de generar el primer tipo de cadenas que él quiere (base36).
wump
1
¿Es esto lo suficientemente aleatorio / único para usarse en, digamos, tokens de sesión?
moraes
¿Cómo se especifica la longitud?
3kstc
53

En Py3.6 +, otra opción es usar el nuevo secretsmódulo estándar :

>>> import secrets
>>> secrets.token_hex(15)
'8d9bad5b43259c6ee27d9aadc7b832'
>>> secrets.token_urlsafe(22)   # may include '_-' unclear if that is acceptable
'teRq7IqhaRU0S3euX1ji9f58WzUkrg'
Un campeón
fuente
28
import string
import random
lst = [random.choice(string.ascii_letters + string.digits) for n in xrange(30)]
str = "".join(lst)
print str
ocwbKCiuAJLRJgM1bWNV1TPSH0F2Lb
eemz
fuente
6
y random.choice (string.hexdigits)
eemz
1
Uno podría preferir el más seguro criptográficamenterandom.SystemRandom().choice
Brian M. Hunt
1
xrange () debe ser range () - NameError: el nombre 'xrange' no está definido
Caleb Bramwell
xrange es correcto (y generalmente mejor) en python 2.x
jcdyer
25

Solución dramáticamente más rápida que las de aquí:

timeit("'%0x' % getrandbits(30 * 4)", "from random import getrandbits")
0.8056681156158447
Kurt Spindler
fuente
Un usuario tiene que probar todos los métodos en la misma máquina para obtener una línea de base precisa. Las especificaciones de hardware pueden marcar una gran diferencia. >>>> timeit.timeit ("'% 0x'% getrandbits (30 * 4)", "de importación aleatoria getrandbits") 0.2471246949999113 </pre>
ptay el
Gracias, esto es mucho más rápido que los otros anteriores. %timeit '%030x' % randrange(16**30)da 1000000 bucles, el mejor de 3: 1,61 µs por bucle mientras que %timeit '%0x' % getrandbits(30 * 4)da 1000000 bucles, el mejor de 3: 396 ns por bucle
frmdstryr
15

Nota: random.choice(string.hexdigits)es incorrecto, porque string.hexdigitsdevuelve 0123456789abcdefABCDEF(tanto en minúsculas como en mayúsculas), por lo que obtendrá un resultado sesgado, con el dígito hexadecimal 'c' el doble de probabilidades de aparecer que el dígito '7'. En su lugar, solo use random.choice('0123456789abcdef').

yaronf
fuente
6

Otro método :

from Crypto import Random
import binascii

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

El punto es: el valor del byte siempre es igual al valor en hexadecimal .

dsgdfg
fuente
5

función de una línea:

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)
Boubakr
fuente
3
In [1]: import random                                    

In [2]: hex(random.getrandbits(16))                      
Out[2]: '0x3b19'
Beto
fuente
FYI: Esta respuesta se marcó como de baja calidad, es posible que desee mejorarla.
oguz ismail
¿Alguna sugerencia de mejora?
Bob
Ni idea. Pensé que deberías saber eso
oguz ismail
2

Por cierto, este es el resultado del uso timeitde los dos enfoques que se han sugerido:

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
David Narayan
fuente
2

Hay uno más rápido en comparación con lo que ha mencionado jcdyer. Esto requiere ~ 50% de su método más 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

Si quieres en base64:

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

Puede cambiar el parámetro de tamaño pasado a randint (último argumento) para variar la longitud de salida según sus requisitos. Entonces, para uno de 60 caracteres:

binascii.b2a_hex(rand.randint(lo, hi, 4).tostring())[:60]
Ethan
fuente
Una ligera variación: binascii.b2a_hex(np.random.rand(np.ceil(N/16)).view(dtype=int))[:N]dónde N=30.
dan-man
@ dan-man Gracias por este método opcional. Sin embargo, encuentro que consume al menos 5 veces más tiempo. ¿Lo notas también?
Ethan
0

agregando una respuesta más a la mezcla que funciona más rápido que la solución @eemz y también es completamente alfanumérica. Tenga en cuenta que esto no le da una respuesta 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"

un rendimiento de referencia rápido

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
andykais
fuente
0

Sin duda, esta no es la versión más liviana, pero es aleatoria y es fácil ajustar el alfabeto / longitud que desee:

import random

def generate(random_chars=12, alphabet="0123456789abcdef"):
    r = random.SystemRandom()
    return ''.join([r.choice(alphabet) for i in range(random_chars)])
Martín Thoma
fuente