Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Random Function without using random module

I need to write the function -

random_number(minimum,maximum)

Without using the random module and I did this:

import time

def random_number(minimum,maximum):
    now = str(time.clock())
    rnd = float(now[::-1][:3:])/1000
    return minimum + rnd*(maximum-minimum)

I am not sure this is fine.. is there a known way to do it with the time?

like image 718
oridamari Avatar asked Feb 24 '15 20:02

oridamari


3 Answers

The thing is I need to do something that somehow uses the time

You could generate randomness based on a clock drift:

import struct
import time

def lastbit(f):
    return struct.pack('!f', f)[-1] & 1

def getrandbits(k):
    "Return k random bits using a relative drift of two clocks."
    # assume time.sleep() and time.clock() use different clocks
    # though it might work even if they use the same clock
    #XXX it does not produce "good" random bits, see below for details
    result = 0
    for _ in range(k):
        time.sleep(0)
        result <<= 1
        result |= lastbit(time.clock())
    return result

Once you have getrandbits(k), it is straigforward to get a random integer in range [a, b], including both end points. Based on CPython Lib/random.py:

def randint(a, b):
    "Return random integer in range [a, b], including both end points."
    return a + randbelow(b - a + 1)

def randbelow(n):
    "Return a random int in the range [0,n).  Raises ValueError if n<=0."
    # from Lib/random.py
    if n <= 0:
       raise ValueError
    k = n.bit_length()  # don't use (n-1) here because n can be 1
    r = getrandbits(k)          # 0 <= r < 2**k
    while r >= n: # avoid skew
        r = getrandbits(k)
    return r

Example, to generate 20 random numbers from 10 to 110 including:

print(*[randint(10, 110) for _ in range(20)])

Output:

11 76 66 58 107 102 73 81 16 58 43 107 108 98 17 58 18 107 107 77

If getrandbits(k) returns k random bits then randint(a, b) should work as is (no skew due to modulo, etc).

To test the quality of getrandbits(k), dieharder utility could be used:

$ python3 random-from-time.py | dieharder -a -g 200

where random-from-time.py generates infinite (random) binary stream:

#!/usr/bin/env python3

def write_random_binary_stream(write):
    while True:
        write(getrandbits(32).to_bytes(4, 'big'))

if __name__ == "__main__":
    import sys
    write_random_binary_stream(sys.stdout.buffer.write)

where getrandbits(k) is defined above.


The above assumes that you are not allowed to use os.urandom() or ssl.RAND_bytes(), or some known PRNG algorithm such as Mersenne Twister to implement getrandbits(k).


getrandbits(n) implemented using "time.sleep() + time.clock()" fails dieharder tests (too many to be a coincidence).

The idea is still sound: a clock drift may be used as a source of randomness (entropy) but you can't use it directly (the distribution is not uniform and/or some bits are dependent); the bits could be passed as a seed to a PRNG that accepts an arbitrary entropy source instead. See "Mixing" section.

like image 104
jfs Avatar answered Oct 21 '22 04:10

jfs


Are you allowed to read random data in some special file? Under Linux, the file `/dev/urandom' provides a convenient way to get random bytes. You could write:

import struct
f = open("/dev/urandom","r")
n = struct.unpack("i",f.read(4))[0]

But this will not work under Windows however.

like image 35
Thomas Baruchel Avatar answered Oct 21 '22 03:10

Thomas Baruchel


Idea is to get number between 0 and 1 using time module and use that to get a number in range.Following will print 20 numbers randomly in range 20 and 60

from time import time

def time_random():
 return time() - float(str(time()).split('.')[0])

def gen_random_range(min, max):
 return int(time_random() * (max - min) + min)

if __name__ == '__main__':
 for i in range(20):
     print gen_random_range(20,60)
like image 21
sai gangaraju Avatar answered Oct 21 '22 03:10

sai gangaraju