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


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.


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.


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'

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
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

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').


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 .


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)

3
In [1]: import random                                    

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

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

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]

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

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)])
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.