Rastgele bir dize ve rastgele bir onaltılık sayı oluşturmanın en hafif yolu


94

Aşağıdakine benzer rastgele 30 karakterlik bir dizi oluşturmanın en basit yolu nedir?

ufhy3skj5nca0d2dfh9hwd2tbk9sw1

Ve aşağıdaki gibi 30 basamaklı bir onaltılık sayı?

8c6f78ac23b4a7b8c0182d7a89e9b1


3
Nasıl (neden?) (İyi hazırlanmış) yanıtların hiçbiri kabul edilmedi?
r2evans

Yanıtlar:


124

Hex çıktı için daha hızlı bir tane aldım. Yukarıdaki ile aynı t1 ve t2'yi kullanarak:

>>> 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 rastgele modüle yalnızca bir çağrı yapar, bir liste oluşturması veya okuması gerekmez ve gerisini dize biçimlendirme ile yapar.


5
Güzel. Sadece 30 onaltılık basamak uzunluğunda rastgele bir sayı oluşturun ve yazdırın. Gösterildiğinde apaçık ortada. Güzel.
eemz

İlginç, Python'un (ve rastgele modülün) büyük noktaları yerel olarak ele aldığını unutmuşum.
49'da wump

3
Yaronf'un aşağıdaki kullanımla ilgili cevabına bakın string.hexdigits: stackoverflow.com/a/15462293/311288 " string.hexdigitsdöner 0123456789abcdefABCDEF(hem küçük hem de büyük harf), [...]. Bunun yerine, sadece kullanın random.choice('0123456789abcdef')."
Thomas

3
Daha da hızlı hale getirmek için getrandbitsyerine kullanın randrange.
robinst

@robinst iyi bir noktaya sahip. '%030x' % random.getrandbits(60)bundan daha hızlı '%030x' % random.randrange(16**30), büyük olasılıkla büyük girişler için herhangi bir dönüşüm yapmak zorunda olmadığı için
Dan Lenski

79

30 basamaklı onaltılık dize:

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

Bunun avantajı, rastgele () 'den daha güvenli ve / veya daha hızlı olabilecek rasgeleliği doğrudan işletim sisteminden alır ve onu tohumlamanız gerekmez.


bu ilginç ve istediği 30 basamaklı onaltılık sayıyı oluşturmak için muhtemelen iyi bir seçim. muhtemelen urandom ve bir dilim operatörü de alfasayısal dizeyi oluşturmak için kullanabilir.
eemz

Binascii'deki diğer işlevlere bir göz attım, base64 ve uuencode'a sahipler, ancak istediği ilk tür dizgileri üretmenin yolu yok (base36).
49'da wump

1
Bu rastgele / benzersiz, örneğin oturum belirteçlerinde kullanılacak kadar mı?
moraes

1
Y: Hayır. Cevabı
buldum

Uzunluk nasıl belirlenir?
3kstc

53

Py3.6 + 'da başka bir seçenek de yeni standart secretsmodülü kullanmaktır:

>>> 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
and random.choice (string.hexdigits)
eemz

1
Kriptografik açıdan daha güvenli random.SystemRandom().choice
Brian

1
xrange (), range () olmalıdır - NameError: name 'xrange' tanımlı değil
Caleb Bramwell

xrange python 2.x'te doğru (ve genellikle daha iyi)
jcdyer

25

Buradakilerden çok daha hızlı çözüm:

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

Doğru bir temel elde etmek için bir kullanıcının aynı makinedeki tüm yöntemleri denemesi gerekir. Donanım özellikleri büyük bir fark yaratabilir. >>>> timeit.timeit ("'% 0x'% getrandbits (30 * 4)", "rasgele içe aktarım getrandbits'ten") 0.2471246949999113 </pre>
ptay

Teşekkür ederim, yukarıdaki diğerlerinden çok daha hızlı. %timeit '%030x' % randrange(16**30)1000000 döngü verir, döngü başına en iyisi 3: 1.61 µs iken %timeit '%0x' % getrandbits(30 * 4)1000000 döngü verir, döngü başına en iyi 3: 396 ns
frmdstryr

15

Not: random.choice(string.hexdigits)yanlıştır, çünkü string.hexdigitsdöner 0123456789abcdefABCDEF(hem küçük hem de büyük harf), bu nedenle onaltılık 'c' rakamının '7' rakamının iki katı görünme olasılığına sahip önyargılı bir sonuç alırsınız. Bunun yerine sadece kullanın random.choice('0123456789abcdef').


6

Diğer yöntem :

from Crypto import Random
import binascii

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

Buradaki nokta şudur: bayt değeri her zaman onaltılıktaki değere eşittir .


5

tek satırlık işlev:

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'

Bilginize: Bu yanıt düşük kaliteli olarak işaretlendi, onu iyileştirmek isteyebilirsiniz.
oguz ismail

İyileştirme için herhangi bir öneriniz var mı?
Bob

Fikrim yok. Sadece bilmen gerektiğini düşündüm
oguz ismail

2

Bu arada, bu, timeitönerilen iki yaklaşımın kullanılmasının sonucudur :

Kullanarak random.choice():

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

Kullanarak binascii.b2a_hex():

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

2

Jcdyer'in bahsettiğine kıyasla daha hızlı bir tane var. Bu, en hızlı yönteminin ~% 50'sini alıyor.

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

Base64'te istiyorsanız:

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

Çıktı uzunluğunu gereksiniminize göre değiştirmek için randint'e (son argüman) iletilen boyut parametresini değiştirebilirsiniz. Yani, 60 karakterlik bir için:

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

Küçük bir değişiklik: binascii.b2a_hex(np.random.rand(np.ceil(N/16)).view(dtype=int))[:N]nerede N=30.
dan-man

@ dan-man Bu isteğe bağlı yöntem için teşekkürler. Ancak, en az 5 kat daha fazla zaman harcadığını görüyorum. Bunu da fark ettiniz mi?
Ethan

0

@eemz çözümünden daha hızlı performans gösteren ve ayrıca tamamen alfanümerik olan karışıma bir yanıt daha eklemek. Bu unutmayın değil sana bir onaltılık bir cevap verir.

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"

hızlı bir kıyaslama getirisi

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

Bu kesinlikle en hafif versiyon değil, ancak rastgele ve istediğiniz alfabeyi / uzunluğu ayarlamak çok kolay:

import random

def generate(random_chars=12, alphabet="0123456789abcdef"):
    r = random.SystemRandom()
    return ''.join([r.choice(alphabet) for i in range(random_chars)])
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.