xRobot
xRobot

Reputation: 26567

Most lightweight way to create a random string and a random hexadecimal number

What is the most lightweight way to create a random string of 30 characters like the following?

ufhy3skj5nca0d2dfh9hwd2tbk9sw1

And an hexadecimal number of 30 digits like the followin?

8c6f78ac23b4a7b8c0182d7a89e9b1

Upvotes: 126

Views: 157548

Answers (15)

laanwj
laanwj

Reputation: 4377

30 digit hex string:

>>> import os
>>> os.urandom(15).hex()
"c84766ca4a3ce52c3602bbf02ad1f7"

The advantage is that this gets randomness directly from the OS, which might be more secure and/or faster than the random(), and you don't have to seed it.

Upvotes: 98

Deadjim
Deadjim

Reputation: 59

Random String (letters and numbers):

import random
import string

def generate_random_string(length=30):
    characters = string.ascii_letters + string.digits
    result = ''.join(random.choice(characters) for _ in range(length))
    return result

random_string = generate_random_string()
print(random_string)

Random Hexadecimal String:

import random
import string

def generate_random_hex_string(length=30):
    characters = string.digits + 'abcdef'  # Hexadecimal digits
    result = ''.join(random.choice(characters) for _ in range(length))
    return result

random_hex_string = generate_random_hex_string()
print(random_hex_string)

For basic random string generation, the random and string modules are very lightweight and efficient because they are part of Python's standard library. This means you don't need to install any external packages. So that will be most lightweight and quickest way.


If you need these strings for security-sensitive purposes, like generating passwords or API tokens, consider using the secrets module instead:

import secrets
import string

# Random string of 30 characters
random_string = ''.join(secrets.choice(string.ascii_lowercase + string.digits) for _ in range(30))

# Hexadecimal number of 30 digits
random_hex = hex(secrets.randbits(120))[2:]  # 120 bits is 30 hexadecimal digits

print(random_string) # 03ulu22q2yxxel4h1a4xwxuf15xemx
print(random_hex) # 539aaca57247132fd08a325a514aea

Upvotes: 0

AChampion
AChampion

Reputation: 30268

In Py3.6+, another option is to use the new standard secrets module:

Random alphanumeric string:

In []:
import secrets
n = 30*3//4 # see note
secrets.token_urlsafe(n)

Out[]:
'teRq7IqhaRU0S3euX1ji9f58WzUkrg'

Note: token_urlsafe() uses base64 encoding which means you need to reduce the requested number by 3//4 to get exactly 30. Alternatively, split the returned string to the first 30.
It may also include _- - unclear if that is acceptable

Random hexadecimal string:

In []:
secrets.token_hex(15)

Out[]:
'8d9bad5b43259c6ee27d9aadc7b832'

Timings:

In []:
%timeit secrets.token_urlsafe(n)

Out[]:
788 ns ± 4.32 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

In []:
%timeit secrets.token_hex(15)

Out[]:
631 ns ± 2.86 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

Upvotes: 96

mti2935
mti2935

Reputation: 12027

Compact way of generating a random hex string 32 characters in length, using the random module:

print(format(random.getrandbits(128), '032x'))

Upvotes: 1

eemz
eemz

Reputation: 1211

import string
import random
lst = [random.choice(string.ascii_letters + string.digits) for n in xrange(30)]
s = "".join(lst)
print s
ocwbKCiuAJLRJgM1bWNV1TPSH0F2Lb

Upvotes: 35

Martin Thoma
Martin Thoma

Reputation: 136437

This is for sure not the most lightweight version, but it is random and it's easy to adjust the alphabet / length you want:

import random

def generate(random_chars=12, alphabet="0123456789abcdef"):
    r = random.SystemRandom()
    return ''.join([r.choice(alphabet) for i in range(random_chars)])

Upvotes: 2

jcdyer
jcdyer

Reputation: 19155

I got a faster one for the hex output. Using the same t1 and t2 as above:

>>> 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 only makes one call to the random module, doesn't have to build or read a list, and then does the rest with string formatting.

Upvotes: 144

andykais
andykais

Reputation: 1074

adding one more answer to the mix that performs faster than @eemz solution and is also fully alphanumeric. Note that this does not give you a hexidecimal answer.

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"

a quick benchmark yields

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

Upvotes: 0

Bob
Bob

Reputation: 1075

In [1]: import random                                    

In [2]: hex(random.getrandbits(16))                      
Out[2]: '0x3b19'

Upvotes: 9

Bacara
Bacara

Reputation: 7223

one-line function:

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)

Upvotes: 6

dsgdfg
dsgdfg

Reputation: 1520

Another Method :

from Crypto import Random
import binascii

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

The point is : byte value is always equal to the value in hex.

Upvotes: 5

Kurt Spindler
Kurt Spindler

Reputation: 1321

Dramatically faster solution than those here:

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

Upvotes: 28

Ethan
Ethan

Reputation: 4995

There's a faster one compared to what jcdyer has mentioned. This takes ~50% of his fastest method.

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

If you want in base64:

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

You can change the size parameter passed to randint (last arg) to vary the output length based on your requirement. So, for a 60 char one:

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

Upvotes: 2

yaronf
yaronf

Reputation: 303

Note: random.choice(string.hexdigits) is incorrect, because string.hexdigits returns 0123456789abcdefABCDEF (both lowercase and uppercase), so you will get a biased result, with the hex digit 'c' twice as likely to appear as the digit '7'. Instead, just use random.choice('0123456789abcdef').

Upvotes: 17

David Narayan
David Narayan

Reputation: 5438

Incidentally, this is the result of using timeit on the two approaches that have been suggested:

Using random.choice():

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

Using binascii.b2a_hex():

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

Upvotes: 4

Related Questions