ランダムな文字列とランダムな16進数を作成する最も軽量な方法


94

次のような30文字のランダムな文字列を作成する最も軽量な方法は何ですか?

ufhy3skj5nca0d2dfh9hwd2tbk9sw1

そして、次のような30桁の16進数?

8c6f78ac23b4a7b8c0182d7a89e9b1


3
(よく練られた)答えのどれも受け入れられなかったのはどうして(なぜ?)?
r2evans

回答:


124

私は16進出力用に速いものを手に入れました。上記と同じ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 ランダムモジュールを1回呼び出すだけで、リストを作成したり読み取ったりする必要はなく、残りは文字列形式で実行されます。


5
いいね。30桁の長さの乱数を生成して印刷するだけです。指摘されれば明らかです。良いですね。
eemz 2010年

興味深いことに、Python(およびランダムモジュール)がbigintをネイティブに処理することをちょっと忘れていました。
wump 2010年

3
以下のyaronfの回答を参照してくださいstring.hexdigitsstackoverflow.com/a/15462293/311288 " string.hexdigitsreturns 0123456789abcdefABCDEF(小文字と大文字の両方)、[...]。代わりに、単にrandom.choice('0123456789abcdef')。"を使用してください。
トーマス

3
getrandbits代わりにrandrangeを使用して、さらに高速にします。
robinst 2016年

@robinstには良い点があります。'%030x' % random.getrandbits(60)よりもさらに高速です'%030x' % random.randrange(16**30)。おそらく、big-intとの間で変換を行う必要がないためです
Dan Lenski

79

30桁の16進文字列:

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

利点は、これがOSから直接ランダム性を取得することです。これは、random()よりも安全で高速である可能性があり、シードする必要がありません。


これは興味深いことであり、おそらく彼が望む30桁の16進数を生成するための良い選択です。おそらく、urandomとslice演算子を使用して、英数字の文字列を生成することもできます。
eemz 2010年

私はbinasciiの他の関数を調べました。それらには、base64とuuencodeがありますが、彼が必要とする最初の種類の文字列(base36)を生成する方法はありません。
wump 2010年

1
これは、たとえばセッショントークンで使用するのに十分なランダム/一意ですか?
moraes 2011

1
A:いいえ、答えが見つかりました:stackoverflow.com/questions/817882/unique-session-id-in-python/...は
モラエス

長さをどのように指定しますか?
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

xrangeはpython2.xで正しい(そして通常はより良い)
jcdyer 2015

25

ここにあるものよりも劇的に速い解決策:

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

正確なベースラインを取得するには、1人のユーザーが同じマシンですべての方法を試す必要があります。ハードウェアの仕様は大きな違いを生む可能性があります。>>>> timeit.timeit( "'%0x'%getrandbits(30 * 4)"、 "from random import getrandbits")0.2471246949999113 </ pre>
ptay

ありがとう、これは上記の他のものよりはるかに速いです。%timeit '%030x' % randrange(16**30)一方、ループあたり1.61マイクロ秒:1000000のループ、3の最高を与える%timeit '%0x' % getrandbits(30 * 4)ループごとに396ナノ秒:1000000のループ、3の最高を提供します
frmdstryr

15

注:戻り値(小文字と大文字の両方)があるためrandom.choice(string.hexdigits)、正しくありません。そのため、16進数の「c」が数字の「7」の2倍表示されるという偏った結果が得られます。代わりに、を使用してください。string.hexdigits0123456789abcdefABCDEFrandom.choice('0123456789abcdef')


6

別の方法:

from Crypto import Random
import binascii

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

重要なのは、バイト値は常に16進数の値と等しいということです。


5

1行関数:

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'

参考:この回答は低品質としてフラグが付けられました。改善することをお勧めします。
oguzismail19年

改善のための提案はありますか?
ボブ

わからない。私はあなたがそれを知っているべきだと思った
oguzismail19年

2

ちなみに、これは使用した結果です timeit提案されている2つのアプローチをし。

使用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(最後の引数)に渡されるサイズパラメーターを変更して、要件に基づいて出力の長さを変更できます。したがって、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ソリューションよりも高速で、完全に英数字であるミックスにもう1つの回答を追加します。これは16進数の答えを与えないことに注意してください。

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)])
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.