random.choice() returns same value at the same second, how does one avoid it?
Asked Answered
M

3

18

I have been looking at similar questions regarding how to generate random numbers in python. Example: Similar Question - but i do not have the problem that the randomfunction returns same values every time.

My random generator works fine, the problem is that it returns the same value when calling the function at, what I think, the same second which is undesireable.

My code looks like this

def getRandomID():
    token = ''
    letters = "abcdefghiklmnopqrstuvwwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
    for i in range(1,36):
        token = token + random.choice(letters)
    return token

As I mentioned this function returns different values when being called at on different times but returns the same value when calling the function at the same time. How do I avoid this problem?

I use this function in a back-end-server to generate unique IDs for users in front-end to insert in a database so I cannot control the time intervals when this happens. I must have random tokens to map the users in the database to be able to insert them correctly with queuenumbers in the database.

Musketry answered 21/9, 2015 at 9:51 Comment(14)
like it return same id when called by two user at same time??Haemin
I'd suggest using uuid to generate random user ids.Knoxville
Hackaholic - exactly, it returns the same "token" which is the same string of randomletters when being called at by two users.Musketry
How do you call the function multiple times at the same time? Spawning threads, multiprocessing, something like that?Exclave
"at the same time" - are we talking about a multi-threaded application then?Teens
do you seed your random function manually somewhere (random.seed(n))?Bizet
Actually i do not know if it is multithreaded/multiprocessed. I have a webpage where the users call the function from using Flask for the HTTPRequests. I deploy the application from Heroku so when two users call the function at the same time, the function runs the random.coice(letters)-row at the same time which generates same random-number.Musketry
I do not seed the random function anywhere, could that be the problem?Musketry
Yes. Python defaults to seeding the random number generator from the system time, while SystemRandom requests random data from the operating system. The usual compromise is to seed the default random number generator from system random sources, for instance random.seed(random.SystemRandom().random()).Scanner
So if initiating random.seed(random.SystemRandom().random()) above my code it should not return the same value? When looking at docs.python.org/2/library/datetime.html i see that it goes down to microseconds, is it the same when using the solution above with system time? Does that mean that if users call the function at the same microsecond it will still be the same problem?Musketry
If possible, let your DB generate a unique ID using a built-in autoincrement or identity column.Baptize
But I want the random generator function to be used. This is to avoid easily-manipulated variables/rows/columns in DB.Musketry
use the same random object and seed it only once.Siusiubhan
Instead of supplying your own list of alphanumeric characters you could use import string; string.letters + string.digitsAdigun
I
12

You could possibly improve matters by using random.SystemRandom() as follows:

import random

sys_random = random.SystemRandom()

def getRandomID():
    token = ''
    letters = "abcdefghiklmnopqrstuvwwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
    for i in range(1, 36):
        token = token + sys_random.choice(letters)
    return token

print(getRandomID())

This attempts to use the os.urandom() function which generates random numbers from sources provided by the operating system. The .choices() function could also be used to return a list of choices in a single call, avoiding the string concatenation:

import random

sys_random = random.SystemRandom()

def getRandomID():
    letters = "abcdefghiklmnopqrstuvwwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
    return ''.join(sys_random.choices(letters, k=35))

print(getRandomID())
Iorio answered 21/9, 2015 at 10:11 Comment(1)
I think this solved the problem at first sight. I will try to perform a bigger-scale-test where I can confirm that the solution works!Musketry
D
5
def getRandomID(n):

    import datetime
    import random

    random.seed(datetime.datetime.now())

    letters = "abcdefghiklmnopqrstuvwwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"

    idList = [ ''.join([random.choice(letters) for j in range(1,36)]) for i in range(n)]

    return idList

this script in the 3rd test of 10 million ids again have made them all unique

changing for loop to list comprehension did speedup quite a bit.

>>> listt = getRandomID(10000000)
>>> print(len(listt))
10000000

>>> setOfIds = set(listt)
>>> print(len(setOfIds))
10000000

this script uses permutations with repetition: 62 choose 35, to theoretically total number of ids is quite big it is pow(62,35)

541638008296341754635824011376225346986572413939634062667808768
Disruption answered 21/9, 2015 at 10:18 Comment(2)
Please consider editing your post to add more explanation about what your code does and why it will solve the problem. An answer that mostly just contains code (even if it's working) usually wont help the OP to understand their problem. From what I can see, this is redundant though as this is what (I believe) Python normally uses as the basis for its seed. And even if it's not, this will still provide the same seed at the same time resulting in the same output.Seleucid
You don't want to call random.seed from within the routine that generates the random numbers. You will (occasionally, non-deterministically) end up with the same value coming back from datetime.now on subsequent calls, which will cause random.choice to return identical sequences.Haynes
E
1

Another option would be to update the seed with the previous result to get a pseudorandom sequence. An option would be old_seed XOR result or just the result.

Exceeding answered 21/9, 2015 at 14:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.