วิธีที่มีน้ำหนักเบาที่สุดในการสร้างสตริงแบบสุ่มและเลขฐานสิบหกแบบสุ่ม


94

วิธีใดเป็นวิธีที่มีน้ำหนักเบาที่สุดในการสร้างสตริงสุ่ม 30 อักขระดังต่อไปนี้

ufhy3skj5nca0d2dfh9hwd2tbk9sw1

และเลขฐานสิบหก 30 หลักเหมือนกับ followin?

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 (และโมดูลสุ่ม) จัดการ bigints โดยกำเนิด
wump

3
ดูคำตอบของ yaronf ด้านล่างเกี่ยวกับการใช้string.hexdigits: stackoverflow.com/a/15462293/311288 " string.hexdigitsส่งกลับ0123456789abcdefABCDEF(ทั้งตัวพิมพ์เล็กและตัวพิมพ์ใหญ่), [... ] ให้ใช้random.choice('0123456789abcdef')แทน"
Thomas

3
ใช้getrandbitsแทนrandrangeเพื่อให้เร็วยิ่งขึ้น
robinst

@robinst มีจุดที่ดี '%030x' % random.getrandbits(60)เร็วกว่า'%030x' % random.randrange(16**30)ด้วยซ้ำน่าจะเป็นเพราะไม่ต้องทำการแปลงใด ๆ เป็น / จาก big-ints
Dan Lenski

79

สตริงเลขฐานสิบหก 30 หลัก:

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

ข้อดีคือสิ่งนี้ได้รับการสุ่มโดยตรงจากระบบปฏิบัติการซึ่งอาจปลอดภัยกว่าและ / หรือเร็วกว่าการสุ่ม () และคุณไม่จำเป็นต้องเริ่มต้น


นั่นน่าสนใจและอาจเป็นทางเลือกที่ดีในการสร้างเลขฐานสิบหก 30 หลักที่เขาต้องการ อาจใช้ urandom และตัวดำเนินการ slice เพื่อสร้างสตริงตัวอักษรและตัวเลขได้ด้วย
eemz

ฉันดูฟังก์ชั่นอื่น ๆ ใน binascii พวกเขามี base64 และ uuencode แต่ไม่มีวิธีสร้างสตริงประเภทแรกที่เขาต้องการ (base36)
wump

1
สุ่ม / ไม่ซ้ำกันนี้เพียงพอที่จะใช้ในโทเค็นเซสชันหรือไม่?
moraes

1
ตอบ: ไม่พบคำตอบ: stackoverflow.com/questions/817882/unique-session-id-in-python/…
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


1
ใคร ๆ ก็ชอบความปลอดภัยในการเข้ารหัสมากกว่าrandom.SystemRandom().choice
Brian M. Hunt

1
xrange () ควรเป็นช่วง () - NameError: ไม่ได้กำหนดชื่อ 'xrange'
Caleb Bramwell

xrange ถูกต้อง (และมักจะดีกว่า) ใน python 2.x
jcdyer

25

โซลูชันที่เร็วกว่าอย่างมากที่นี่:

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

ผู้ใช้รายหนึ่งต้องลองใช้ทุกวิธีในเครื่องเดียวกันเพื่อให้ได้ข้อมูลพื้นฐานที่ถูกต้อง รายละเอียดฮาร์ดแวร์สามารถสร้างความแตกต่างได้มาก >>>> timeit.timeit ("'% 0x'% getrandbits (30 * 4)", "from random import 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

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

ข้อเสนอแนะในการปรับปรุง?
Bob

ไม่มีความเห็น. ฉันแค่คิดว่าคุณควรรู้ว่า
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 เท่า คุณสังเกตเห็นเช่นกัน?
Ethan

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)])
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.