Generating non-repeating random numbers in Python

前端 未结 17 1739
粉色の甜心
粉色の甜心 2020-11-30 19:56

Ok this is one of those trickier than it sounds questions so I\'m turning to stack overflow because I can\'t think of a good answer. Here is what I want: I need Python to ge

相关标签:
17条回答
  • 2020-11-30 20:47

    If it is enough for you that a casual observer can't guess the next value, you can use things like a linear congruential generator or even a simple linear feedback shift register to generate the values and keep the state in the database in case you need more values. If you use these right, the values won't repeat until the end of the universe. You'll find more ideas in the list of random number generators.

    If you think there might be someone who would have a serious interest to guess the next values, you can use a database sequence to count the values you generate and encrypt them with an encryption algorithm or another cryptographically strong perfect has function. However you need to take care that the encryption algorithm isn't easily breakable if one can get hold of a sequence of successive numbers you generated - a simple RSA, for instance, won't do it because of the Franklin-Reiter Related Message Attack.

    0 讨论(0)
  • 2020-11-30 20:48

    You could use Format-Preserving Encryption to encrypt a counter. Your counter just goes from 0 upwards, and the encryption uses a key of your choice to turn it into a seemingly random value of whatever radix and width you want.

    Block ciphers normally have a fixed block size of e.g. 64 or 128 bits. But Format-Preserving Encryption allows you to take a standard cipher like AES and make a smaller-width cipher, of whatever radix and width you want (e.g. radix 10, width 9 for the parameters of the question), with an algorithm which is still cryptographically robust.

    It is guaranteed to never have collisions (because cryptographic algorithms create a 1:1 mapping). It is also reversible (a 2-way mapping), so you can take the resulting number and get back to the counter value you started with.

    AES-FFX is one proposed standard method to achieve this.

    I've experimented with some basic Python code for AES-FFX--see Python code here (but note that it doesn't fully comply with the AES-FFX specification). It can e.g. encrypt a counter to a random-looking 7-digit decimal number. E.g.:

    0000000   0731134
    0000001   6161064
    0000002   8899846
    0000003   9575678
    0000004   3030773
    0000005   2748859
    0000006   5127539
    0000007   1372978
    0000008   3830458
    0000009   7628602
    0000010   6643859
    0000011   2563651
    0000012   9522955
    0000013   9286113
    0000014   5543492
    0000015   3230955
    ...       ...
    

    For another example in Python, using another non-AES-FFX (I think) method, see this blog post "How to Generate an Account Number" which does FPE using a Feistel cipher. It generates numbers from 0 to 2^32-1.

    0 讨论(0)
  • 2020-11-30 20:48

    You are stating that you store the numbers in a database.

    Wouldn't it then be easier to store all the numbers there, and ask the database for a random unused number? Most databases support such a request.

    Examples

    MySQL:

    SELECT column FROM table
    ORDER BY RAND()
    LIMIT 1
    

    PostgreSQL:

    SELECT column FROM table
    ORDER BY RANDOM()
    LIMIT 1
    
    0 讨论(0)
  • 2020-11-30 20:49

    Bit late answer, but I haven't seen this suggested anywhere.

    Why not use the uuid module to create globally unique identifiers

    0 讨论(0)
  • 2020-11-30 20:51

    This is a neat problem, and I've been thinking about it for a while (with solutions similar to Sjoerd's), but in the end, here's what I think:

    Use your point 1) and stop worrying.

    Assuming real randomness, the probability that a random number has already been chosen before is the count of previously chosen numbers divided by the size of your pool, i.e. the maximal number.

    If you say you only need a billion numbers, i.e. nine digits: Treat yourself to 3 more digits, so you have 12-digit serial numbers (that's three groups of four digits – nice and readable).

    Even when you're close to having chosen a billion numbers previously, the probability that your new number is already taken is still only 0,1%.

    Do step 1 and draw again. You can still check for an "infinite" loop, say don't try more than 1000 times or so, and then fallback to adding 1 (or something else).

    You'll win the lottery before that fallback ever gets used.

    0 讨论(0)
提交回复
热议问题