Найлегший спосіб створити випадковий рядок і випадкове шістнадцяткове число


94

Який найлегший спосіб створити випадковий рядок із 30 символів, як показано нижче?

ufhy3skj5nca0d2dfh9hwd2tbk9sw1

І шістнадцяткове число з 30 цифр, як наступне?

8c6f78ac23b4a7b8c0182d7a89e9b1


3
Як (чому?) Так, що жодна з (добре продуманих) відповідей не прийнята?
r2evans

Відповіді:


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,

Цікаво, я якось забув, що Python (і випадковий модуль) обробляє бігінти власноруч.
wump

3
Дивіться відповідь yaronf нижче щодо використання string.hexdigits: stackoverflow.com/a/15462293/311288 " string.hexdigitsповертає 0123456789abcdefABCDEF(як малу, так і велику), [...]. Натомість просто використовуйте random.choice('0123456789abcdef')."
Томас

3
Використовуйте getrandbitsзамість того, randrangeщоб зробити це ще швидшим.
Робінст

@robinst має хороший момент. '%030x' % random.getrandbits(60)навіть швидше, ніж '%030x' % random.randrange(16**30), мабуть, тому, що йому не потрібно робити жодних перетворень в / з великих інтів
Ден Ленскі

79

30-значний шістнадцятковий рядок:

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

Перевага полягає в тому, що це отримує випадковість безпосередньо з ОС, яка може бути більш безпечною та / або швидшою, ніж random (), і вам не потрібно її засівати.


це цікаво, і, мабуть, хороший вибір для створення 30-значного шістнадцяткового числа, яке він хоче. ймовірно, міг також використовувати urandom та оператор зрізу для генерації буквено-цифрового рядка.
eemz,

Я подивився інші функції в binascii, вони мають base64 і uuencode, але жодним чином не можна створити перший тип рядків, який він хоче (base36).
wump

1
Чи достатньо цього випадкового / унікального, щоб використовувати його, скажімо, у маркерах сесій?
moraes


Як можна вказати довжину?
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,

1
Можна віддати перевагу більш криптографічно безпечномуrandom.SystemRandom().choice
Брайан М. Хант,

1
xrange () має бути діапазоном () - NameError: ім'я 'xrange' не визначено
Caleb Bramwell

xrange правильний (і зазвичай кращий) в python 2.x
jcdyer 03.03.15

25

Різко швидше рішення, ніж тут:

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

Один користувач повинен спробувати всі методи на одній машині, щоб отримати точну базову лінію. Характеристики апаратного забезпечення можуть мати велике значення. >>>> timeit.timeit ("'% 0x'% getrandbits (30 * 4)", "з випадкових імпортних getrandbits") 0,2471246949999113 </pre>
птаї

Дякую, це набагато швидше, ніж інші вище. %timeit '%030x' % randrange(16**30)дає 1000000 петель, найкраще 3: 1,61 мкс на петлю, тоді як %timeit '%0x' % getrandbits(30 * 4)дає 1000000 петель, найкраще 3: 396 нс на петлю
frmdstryr

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'

FYI: Ця відповідь була позначена як неякісна, можливо, ви захочете її покращити.
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 (останній аргумент), щоб змінювати вихідну довжину залежно від ваших вимог. Отже, для 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)])
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.