简体   繁体   中英

Generate random numbers without using the last n values in Python

I have a Python function that generates a random number between 0 and 100:

def get_next_number():
    value = randint(0,100)

Every time I call this function, I need it to return a random number but that number cannot be one of the last n random numbers it returned (lets say 5 for this example).

Here are some examples:

55, 1, 67, 12, 88, 91, 100, 54 (This is fine as there are no duplicates in the last 5 numbers returned)

77, 42, 2, 3, 88, 2... (When the function gets a random number of 2, I need it to try again since 2 was already returned 3 numbers prior)

89, 23, 29, 81, 99, 100, 6, 8, 23... (This one is fine because 23 occurred more than 5 times before)

Is there something built into the random function to accomplish this?

Think of it the other way round .

Instead of generating a random number and then checking if it is already generated before, you can generate the set of non-duplicate numbers first to be picked up one by one - thus removing the possibility of generating duplicate number at all.

And you also need to track the last 5 items generated to exclude them from the picked items.

Something like this will do:

s = set(range(0, 100))
last5 = []
def get_next_number():
    reduced_list = list(s - set(last5))
    i = randint(0, len(reduced_list) - 1)
    last5.append(reduced_list[i])
    if len(last5) > 5:
        last5.pop(0)
    return reduced_list[i]

To test:

result = []
for i in range(0, 5000):
    result.append(get_next_number())
print(result)

Step-by-step explanations:

  1. Generate the set of numbers to be picked up (say, 0 to 99) and generate an empty list to store the last 5 picked numbers:

     s = set(range(0, 100)) last5 = [] 
  2. In the method, exclude the last 5 picked numbers from the possibility from being picked:

     reduced_list = list(s - set(last5)) 
  3. Pick random number from the reduced_list , all numbers left in the reduced_list is valid for picking. Append the number to the last5 list

     i = randint(0, len(reduced_list) - 1) #get any valid index. -1 is needed because randint upperbound is inclusive last5.append(reduced_list[i]) #the number is as what it pointed by the index: reduced_list[i], append that number to the last 5 list 
  4. Check if the last5 list already have members > 5. If it does, you need to remove its first member:

     if len(last5) > 5: last5.pop(0) 
  5. return you selected member:

     return reduced_list[i] 

For achieving this, you can use a iterator. Below is the sample code for that:

import random

class UniqueRandom(object):
    def __init__(self, num):
        self.unused_nums = range(num)
        self.num = num

    def __iter__(self):
        return self

    def next(self):
        if self.num:
            random_num = random.randrange(self.num)
            unique_num = self.unused_nums[random_num]
            self.unused_nums[random_num], self.unused_nums[self.num-1] = self.unused_nums[self.num-1], self.unused_nums[random_num]
            self.num -= 1
            return unique_num
        else:
            raise StopIteration

For example to generate the random numbers between 0 to 100:

random_num = UniqueRandom(100)
print next(random_num)  # 54
print next(random_num)  # 33

To flush the values, create new iterator object:

random_num = UniqueRandom(100)
print next(random_num)  # 92
print next(random_num)  # 33 (repeated)

You may also use it within a list as:

>>> for i in UniqueRandom(5):
...     print i
... 
2
1
0
3
4

How many random numbers do you need?

Use Python's builtin random.sample method. It lists unique elements chosen from the population sequence or set and is used for random sampling without replacement.

>>> import random
>>> random.sample(range(1, 100), 3)
[56, 43, 74]

Create a large list of random integers. Then, pop() when required.

>>> rands = random.sample(range(1, 100), 20)
>>> rands
[63, 23, 68, 72, 18, 83, 56, 34, 19, 7, 51, 5, 89, 47, 26, 49, 2, 1, 93, 84]
>>> rands.pop()
84
>>> rands.pop()
93

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM