Search code examples
algorithmmathrandomlanguage-agnostic

Unique (non-repeating) random numbers in O(1)?


I'd like to generate unique random numbers between 0 and 1000 that never repeat (i.e. 6 doesn't show up twice), but that doesn't resort to something like an O(N) search of previous values to do it. Is this possible?


Solution

  • Initialize an array of 1001 integers with the values 0-1000 and set a variable, max, to the current max index of the array (starting with 1000). Pick a random number, r, between 0 and max, swap the number at the position r with the number at position max and return the number now at position max. Decrement max by 1 and continue. When max is 0, set max back to the size of the array - 1 and start again without the need to reinitialize the array.

    Update: Although I came up with this method on my own when I answered the question, after some research I realize this is a modified version of Fisher-Yates known as Durstenfeld-Fisher-Yates or Knuth-Fisher-Yates. Since the description may be a little difficult to follow, I have provided an example below (using 11 elements instead of 1001):

    Array starts off with 11 elements initialized to array[n] = n, max starts off at 10:

    +--+--+--+--+--+--+--+--+--+--+--+
    | 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|
    +--+--+--+--+--+--+--+--+--+--+--+
                                    ^
                                   max    
    

    At each iteration, a random number r is selected between 0 and max, array[r] and array[max] are swapped, the new array[max] is returned, and max is decremented:

    max = 10, r = 3
               +--------------------+
               v                    v
    +--+--+--+--+--+--+--+--+--+--+--+
    | 0| 1| 2|10| 4| 5| 6| 7| 8| 9| 3|
    +--+--+--+--+--+--+--+--+--+--+--+
    
    max = 9, r = 7
                           +-----+
                           v     v
    +--+--+--+--+--+--+--+--+--+--+--+
    | 0| 1| 2|10| 4| 5| 6| 9| 8| 7: 3|
    +--+--+--+--+--+--+--+--+--+--+--+
    
    max = 8, r = 1
         +--------------------+
         v                    v
    +--+--+--+--+--+--+--+--+--+--+--+
    | 0| 8| 2|10| 4| 5| 6| 9| 1: 7| 3|
    +--+--+--+--+--+--+--+--+--+--+--+
    
    max = 7, r = 5
                     +-----+
                     v     v
    +--+--+--+--+--+--+--+--+--+--+--+
    | 0| 8| 2|10| 4| 9| 6| 5: 1| 7| 3|
    +--+--+--+--+--+--+--+--+--+--+--+
    
    ...
    

    After 11 iterations, all numbers in the array have been selected, max == 0, and the array elements are shuffled:

    +--+--+--+--+--+--+--+--+--+--+--+
    | 4|10| 8| 6| 2| 0| 9| 5| 1| 7| 3|
    +--+--+--+--+--+--+--+--+--+--+--+
    

    At this point, max can be reset to 10 and the process can continue.