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.

Community
  • 1
  • 1
Axel Ekeberg
  • 370
  • 1
  • 4
  • 15
  • like it return same id when called by two user at same time?? – Hackaholic Sep 21 '15 at 09:54
  • 9
    I'd suggest using `uuid` to generate random user ids. – hjpotter92 Sep 21 '15 at 09:55
  • Hackaholic - exactly, it returns the same "token" which is the same string of randomletters when being called at by two users. – Axel Ekeberg Sep 21 '15 at 09:59
  • How do you call the function multiple times at the same time? Spawning threads, multiprocessing, something like that? – Marius Sep 21 '15 at 10:01
  • *"at the same time"* - are we talking about a multi-threaded application then? – Lukas Graf Sep 21 '15 at 10:01
  • do you seed your random function manually somewhere (`random.seed(n)`)? – hiro protagonist Sep 21 '15 at 10:07
  • 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. – Axel Ekeberg Sep 21 '15 at 10:07
  • I do not seed the random function anywhere, could that be the problem? – Axel Ekeberg Sep 21 '15 at 10:09
  • 2
    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())`. – Yann Vernier Sep 21 '15 at 10:16
  • So if initiating random.seed(random.SystemRandom().random()) above my code it should not return the same value? When looking at https://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? – Axel Ekeberg Sep 21 '15 at 11:21
  • If possible, let your DB generate a unique ID using a built-in autoincrement or identity column. – jarmod Sep 21 '15 at 11:49
  • But I want the random generator function to be used. This is to avoid easily-manipulated variables/rows/columns in DB. – Axel Ekeberg Sep 21 '15 at 11:52
  • use the same random object and seed it only once. – njzk2 Sep 21 '15 at 13:55
  • Instead of supplying your own list of alphanumeric characters you could use `import string; string.letters + string.digits` – kuzzooroo Sep 21 '15 at 14:24

3 Answers3

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())
Martin Evans
  • 45,791
  • 17
  • 81
  • 97
  • 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! – Axel Ekeberg Sep 21 '15 at 11:58
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
LetzerWille
  • 5,355
  • 4
  • 23
  • 26
  • 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. – SuperBiasedMan Sep 21 '15 at 11:29
  • 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. – Myk Willis Sep 21 '15 at 13:00
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.

allo
  • 3,955
  • 8
  • 40
  • 71