Reputation: 151
I'm trying to write a python script to test the randomness of /dev/random, but I can't get it to give me any number. My code looks like this
with open("/dev/random", 'rb') as file:
print f.read(10)
which I believe is supposed to print out 10 bytes from /dev/random, but instead of numbers, it prints out weird characters (non-standard letters and no numbers). Any idea what I'm doing wrong?
Upvotes: 12
Views: 15975
Reputation: 142206
Python has a builtin function for this (which will also use the appropriate method on other OS's as well)...
import os
print os.urandom(10)
# '\xf1\x11xJOl\xab\xcc\xf0\xfd'
From the docs at http://docs.python.org/2/library/os.html#os.urandom
This function returns random bytes from an OS-specific randomness source. The returned data should be unpredictable enough for cryptographic applications, though its exact quality depends on the OS implementation. On a UNIX-like system this will query /dev/urandom, and on Windows it will use CryptGenRandom. If a randomness source is not found, NotImplementedError will be raised.
If you then wanted those bytes to be a number, you can do so by converting as such:
>>> rand = os.urandom(10)
>>> int(binascii.hexlify(rand), 16)
1138412584848598544216317L
Or using Python 2:
>>> int(rand.encode('hex'), 16)
1138412584848598544216317L
Although, /dev/random
and /dev/urandom
are slightly different, so you can use your existing .read()
op and just do the int
conversion if the difference is significant to you.
Upvotes: 20
Reputation: 1003
In Python 3.2 and higher, the following is shorter and probably faster than the solutions in the older answers:
with open("/dev/random", 'rb') as f:
print(int.from_bytes(f.read(10), 'big'))
This prints a single 80-bit number (range 0 to 2^80-1 inclusive).
Upvotes: 1
Reputation: 606
It's printing random characters, so just convert them to ints using the ord()
function. Something like:
with open("/dev/random", 'rb') as file: print [ord(x) for x in file.read(10)]
This will print a list of 10 random ints from 0 to 255. (I got: [117, 211, 225, 24, 134, 145, 51, 234, 153, 89]
).
Upvotes: 1
Reputation: 76725
You are getting 10 bytes. Python won't automatically turn them into numbers.
I recommend you grab the bytes in multiples of 4, then turn them into 32-bit unsigned integers, then scale them to whatever you need.
EDIT: the old code showed the idea but was poorly divided into functions. Here is the same basic idea but now conveniently packaged into functions.
import os
import struct
_random_source = open("/dev/random", "rb")
def random_bytes(len):
return _random_source.read(len)
def unpack_uint32(bytes):
tup = struct.unpack("I", bytes)
return tup[0]
UINT32_MAX = 0xffffffff
def randint(low, high):
"""
Return a random integer in the range [low, high], including
both endpoints.
"""
n = (high - low) + 1
assert n >= 1
scale_factor = n / float(UINT32_MAX + 1)
random_uint32 = unpack_uint32(random_bytes(4))
result = int(scale_factor * random_uint32) + low
return result
def randint_gen(low, high, count):
"""
Generator that yields random integers in the range [low, high],
including both endpoints.
"""
n = (high - low) + 1
assert n >= 1
scale_factor = n / float(UINT32_MAX + 1)
for _ in range(count):
random_uint32 = unpack_uint32(random_bytes(4))
result = int(scale_factor * random_uint32) + low
yield result
if __name__ == "__main__":
# roll 3 dice individually with randint()
result = [randint(1, 6) for _ in range(3)]
print(result)
# roll 3 dice more efficiently with randint_gen()
print(list(randint_gen(1, 6, 3)))
Upvotes: 9