Il modo più leggero per creare una stringa casuale e un numero esadecimale casuale


94

Qual è il modo più leggero per creare una stringa casuale di 30 caratteri come la seguente?

ufhy3skj5nca0d2dfh9hwd2tbk9sw1

E un numero esadecimale di 30 cifre come il seguente?

8c6f78ac23b4a7b8c0182d7a89e9b1


3
Com'è (perché?) Nessuna delle risposte (ben congegnate) è stata accettata?
r2evans

Risposte:


124

Ne ho uno più veloce per l'uscita esadecimale. Usando gli stessi t1 e t2 come sopra:

>>> 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 effettua solo una chiamata al modulo random, non deve creare o leggere un elenco, quindi fa il resto con la formattazione delle stringhe.


5
Bello. Basta generare un numero casuale di 30 cifre esadecimali e stamparlo. Ovvio quando indicato. Ben fatto.
eemz

Interessante, ho dimenticato che Python (e il modulo random) gestisce nativamente i bigint.
wump

3
Vedi la risposta di yaronf di seguito sull'uso string.hexdigits: stackoverflow.com/a/15462293/311288 " string.hexdigitsrestituisce 0123456789abcdefABCDEF(sia minuscolo che maiuscolo), [...]. Invece, usa solo random.choice('0123456789abcdef')."
Thomas,

3
Usa getrandbitsinvece di randrangeper renderlo ancora più veloce.
robinst

@robinst ha un buon punto. '%030x' % random.getrandbits(60)è anche più veloce di '%030x' % random.randrange(16**30), probabilmente perché non deve eseguire alcuna conversione da / a big-int
Dan Lenski

79

Stringa esadecimale di 30 cifre:

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

Il vantaggio è che questo ottiene la casualità direttamente dal sistema operativo, che potrebbe essere più sicuro e / o più veloce di random (), e non è necessario inizializzarlo.


è interessante e probabilmente è una buona scelta per generare il numero esadecimale di 30 cifre che desidera. probabilmente potrebbe usare urandom e un operatore slice per generare anche la stringa alfanumerica.
eemz

Ho dato un'occhiata alle altre funzioni in binascii, hanno base64 e uuencode, ma non c'è modo di generare il primo tipo di stringhe che vuole (base36).
wump

1
È abbastanza casuale / unico da poter essere utilizzato, ad esempio, nei token di sessione?
moraes


Come si specifica la lunghezza?
3kstc

53

In Py3.6 +, un'altra opzione è usare il nuovo secretsmodulo standard :

>>> 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
Si potrebbe preferire il più sicuro crittograficamenterandom.SystemRandom().choice
Brian M. Hunt

1
xrange () dovrebbe essere range () - NameError: il nome 'xrange' non è definito
Caleb Bramwell,

xrange è corretto (e di solito migliore) in python 2.x
jcdyer

25

Soluzione notevolmente più veloce di quelle qui:

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

Un utente deve provare tutti i metodi sulla stessa macchina per ottenere una linea di base accurata. Le specifiche hardware possono fare una grande differenza. >>>> timeit.timeit ("'% 0x'% getrandbits (30 * 4)", "from random import getrandbits") 0.2471246949999113 </pre>
ptay

Grazie, molto più velocemente degli altri sopra. %timeit '%030x' % randrange(16**30)fornisce 1000000 loop, meglio di 3: 1,61 µs per loop mentre %timeit '%0x' % getrandbits(30 * 4)dà 1000000 loop, meglio di 3: 396 ns per loop
frmdstryr

15

Nota: random.choice(string.hexdigits)non è corretto, perché string.hexdigitsrestituisce 0123456789abcdefABCDEF(sia minuscolo che maiuscolo), quindi otterrai un risultato sbilanciato, con la cifra esadecimale "c" il doppio delle probabilità che appaia la cifra "7". Invece, usa solo random.choice('0123456789abcdef').


6

Un altro metodo:

from Crypto import Random
import binascii

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

Il punto è: il valore del byte è sempre uguale al valore in esadecimale .


5

funzione di una riga:

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: Questa risposta è stata contrassegnata come di bassa qualità, potresti volerla migliorare.
oguz ismail

Qualche suggerimento per migliorare?
Bob

Nessuna idea. Ho solo pensato che dovresti saperlo
oguz ismail

2

Per inciso, questo è il risultato dell'utilizzo timeitdei due approcci che sono stati suggeriti:

Utilizzando random.choice():

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

Utilizzando binascii.b2a_hex():

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

2

Ce n'è uno più veloce rispetto a quello che ha menzionato jcdyer. Questo richiede circa il 50% del suo metodo più veloce.

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 vuoi in base64:

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

È possibile modificare il parametro size passato a randint (last arg) per variare la lunghezza dell'output in base alle proprie esigenze. Quindi, per uno da 60 caratteri:

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

Una leggera variazione: binascii.b2a_hex(np.random.rand(np.ceil(N/16)).view(dtype=int))[:N]dove N=30.
dan-man

@ dan-man Grazie per questo metodo opzionale. Tuttavia, trovo che consuma almeno 5 volte più tempo. Lo noti anche tu?
Ethan

0

aggiungendo un'altra risposta al mix che funziona più velocemente della soluzione @eemz ed è anche completamente alfanumerica. Nota che questo non ti dà una risposta esadecimale.

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

Questa non è sicuramente la versione più leggera, ma è casuale ed è facile regolare l'alfabeto / lunghezza che desideri:

import random

def generate(random_chars=12, alphabet="0123456789abcdef"):
    r = random.SystemRandom()
    return ''.join([r.choice(alphabet) for i in range(random_chars)])
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.