创建随机字符串和随机十六进制数的最轻巧的方法


94

如下创建30个字符的随机字符串的最轻巧的方法是什么?

ufhy3skj5nca0d2dfh9hwd2tbk9sw1

还有一个30位的十六进制数字,如下所示?

8c6f78ac23b4a7b8c0182d7a89e9b1


3
如何(为什么?)没有(精心设计的)答案被接受?
r2evans

Answers:


124

我得到了更快的十六进制输出。使用与上述相同的t1和t2:

>>> 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 只需对随机模块进行一次调用,而不必构建或读取列表,然后对其余部分进行字符串格式化。


5
真好 只需生成一个长度为30个十六进制数字的随机数并将其打印出来即可。指出时很明显。好东西。
eemz 2010年

有趣的是,我有点忘记了Python(和随机模块)本机处理bigints。
2010年

3
请参阅下面有关使用的yaronf答案string.hexdigitsstackoverflow.com/a/15462293/311288 string.hexdigits返回0123456789abcdefABCDEF(小写和大写),[...]。而是使用random.choice('0123456789abcdef')。”
托马斯

3
使用getrandbits而不是randrange使其更快。
罗宾斯特

@robinst有一个好处。'%030x' % random.getrandbits(60)甚至比更快'%030x' % random.randrange(16**30),可能是因为它不必进行与大整数之间的任何转换
Dan Lenski

79

30位十六进制字符串:

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

这样做的好处是,它可以直接从OS获取随机性,它可能比random()更安全和/或更快速,并且您不必播种它。


这很有趣,并且可能是生成他想要的30位十六进制数字的不错的选择。可能也可以使用urandom和slice运算符来生成字母数字字符串。
eemz 2010年

我确实看了binascii中的其他函数,它们确实具有base64和uuencode,但是无法生成他想要的第一种字符串(base36)。
2010年

1
这样的随机性/唯一性足以用于会话令牌吗?
moraes 2011年


如何指定长度?
3kstc

53

在Py3.6 +中,另一个选择是使用新的标准secrets模块:

>>> 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
和random.choice(string.hexdigits)
eemz,2010年

1
一个人可能更喜欢加密的安全性random.SystemRandom().choice
Brian M. Hunt

1
xrange()应该是range()-NameError:名称“ xrange”未定义
Caleb Bramwell 2015年

xrange在python 2.x中是正确的(通常更好)
jcdyer

25

比这里的解决方案快得多的解决方案:

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

一个用户必须在同一台计算机上尝试所有方法才能获得准确的基准。硬件规格可能会有很大的不同。>>>> timeit.timeit( “ '%0X' %getrandbits(30 * 4)”, “从随机导入getrandbits”)0.2471246949999113 </ PRE>
ptay

谢谢,这比上面的其他要快得多。%timeit '%030x' % randrange(16**30)给出1000000循环,最好是3:每个循环1.61 µs,%timeit '%0x' % getrandbits(30 * 4)给出1000000循环,最好是3:每个循环396 ns
frmdstryr 19-3-28

15

注意:这random.choice(string.hexdigits)是不正确的,因为string.hexdigits返回0123456789abcdefABCDEF(小写和大写),所以您将得到有偏差的结果,十六进制数字“ c”出现的可能性是数字“ 7”的两倍。而是使用random.choice('0123456789abcdef')


6

另一种方法:

from Crypto import Random
import binascii

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

关键是:字节值始终等于十六进制值


5

一线功能:

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'

仅供参考:此答案被标记为低质量,您可能需要改善它。
oguz ismail

有什么改进建议吗?
鲍勃

不知道。我只是认为您应该知道
oguz ismail

2

顺便说一句,这是在timeit建议的两种方法上使用的结果:

使用random.choice()

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

使用binascii.b2a_hex()

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

2

与jcdyer提到的相比,它的速度更快。这需要他最快方法的50%。

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

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

您可以更改传递给randint(last arg)的size参数,以根据需要更改输出长度。因此,对于一个60字符的字符:

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

细微变化: binascii.b2a_hex(np.random.rand(np.ceil(N/16)).view(dtype=int))[:N]哪里N=30
dan-man

@ dan-man感谢您的可选方法。但是,我发现它至少要多消耗5倍的时间。你也注意到了吗?
伊桑

0

向比@eemz解决方案执行速度更快且也是全字母数字的混合添加更多答案。请注意,这并没有给你一个十六进制的答案。

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"

快速基准收益

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

当然,这不是最轻量的版本,但是它是随机的,可以轻松调整所需的字母/长度:

import random

def generate(random_chars=12, alphabet="0123456789abcdef"):
    r = random.SystemRandom()
    return ''.join([r.choice(alphabet) for i in range(random_chars)])
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.