简体   繁体   中英

Choose random numbers from a range except numbers in a list

I have a for loop where at each turn I choose a random number from a constant range. However, I never want to get a duplicate value. This is how I am doing it now:

import random
my_range = 1000
chosen = [81, 944, 576, 618, 333, 350, 579, 774, 86, 511, 619, 552, 804, 44, 894, 408, 242]
for i in range(500):
    rand_ind = random.randint(0,1000)
    while rand_ind in chosen:
        rand_ind = random.randint(0,my_range)
    chosen.append(rand_ind)
print(chosen)

Is there any way to do this in one line or more efficiently?

Directly use random.sample to get a fixed number of picks without repetition.

chosen = random.sample(range(1001), 500)

If you want to exclude some numbers before sampling, use intermediate set s for efficient differences.

excluded = {81, 944, 576, 618, 333, 350, 579, 774, 86, 511, 619, 552, 804, 44, 894, 408, 242}
chosen = random.sample(list(set(range(1001)) - excluded), 500)

看起来您想从set(range(1001)) - set(chosen) sample

Depending on the size of the range (assuming it is not too big), I would use shuffle on all the possible items.

import random

my_range = 1000
my_item_count = my_range //2

# generate all the possibilities
choices = list(range(my_range))

# randomize their order
random.shuffle(choices)

# take the first n
choices = choices[:my_item_count]

print(choices)

If I set my_range to a number like 10, we get a result like:

[7, 3, 0, 4, 8]

Is this actually faster than the original?

There was a valid question asked about if this in fact is any faster than the original answer. That seem like a perfectly reasonable question and I probably should have included timings to demonstrate.

Hint, if it was not then I would not have offered it as an answer :-)

Let's add some timing code. This will compare the original answer and this answer but I encourage others providing answers to demonstrate their improvements as well. Note I added the answer by MisterMiyagi as I suspected it would be fastest of all being a single call.

Hint, it was :-)

New Timing Code:

import timeit

setup_original='''
import random
my_range = 1_000
my_item_count = my_range // 2
def test(my_range, my_item_count):
    chosen = []
    for i in range(my_item_count):
        rand_ind = random.randint(0,my_range)
        while rand_ind in chosen:
            rand_ind = random.randint(0,my_range)
        chosen.append(rand_ind)
    return chosen
'''

setup_jonsg='''
import random
my_range = 1_000
my_item_count = my_range // 2
def test(my_range, my_item_count):
    choices = list(range(my_range))
    random.shuffle(choices)
    return choices[:my_item_count]
'''

setup_MisterMiyagi='''
import random
my_range = 1_000
my_item_count = my_range // 2
def test(my_range, my_item_count):
    return random.sample(range(my_range), my_item_count)
'''

print("Original: %s" % timeit.timeit("test(my_range, my_item_count)", setup=setup_original, number=1000))

print("jonsg: %s" % timeit.timeit("test(my_range, my_item_count)", setup=setup_jonsg, number=1000))

print("MisterMiyagi: %s" % timeit.timeit("test(my_range, my_item_count)", setup=setup_MisterMiyagi, number=1000))

On my laptop the result is:

Original: 2.9269699
jonsg: 0.3391961000000001
MisterMiyagi: 0.2070141999999997

The original is about 10x SLOWER!

Answer by MisterMiyagi is 50% FASTER than mine!

import random
def shuffle(Arr):
    arr = [i for i in Arr]
    for i in range(len(arr) - 1):
        rndIdx = random.randint(i, len(arr)-1)
        tmp = arr[rndIdx]
        arr[rndIdx] = arr[i]
        arr[i] = tmp
    return arr

In[0]: a = list(range(30)); print(a) #my_range
Out[0]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]

In[1]: b = list(range(15, 25)); print(b) #chosen
Out[1]: [15, 16, 17, 18, 19, 20, 21, 22, 23, 24]

In[2]: l = [i for i in a if i not in b]; print(l) #my_range - chosen
Out[2]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 25, 26, 27, 28, 29]

In[3]: print(shuffle(l)) #shuffled(my_range - chosen) without repeating elements
Out[3]: [5, 2, 8, 6, 1, 26, 28, 11, 4, 7, 13, 9, 14, 29, 12, 10, 0, 27, 3, 25]

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