Python 中有没有一种简单的方法来生成一个范围内的随机数,不包括该范围内的某些数字子集?
例如,我知道您可以使用以下命令生成 0 到 9 之间的随机数:
from random import randint
randint(0,9)
如果我有一个列表怎么办?
exclude=[2,5,7]
,我不想被退回?
试试这个:
from random import choice
print(choice([i for i in range(0,9) if i not in [2,5,7]]))
尝试这样的事情:
from random import randint
def random_exclude(*exclude):
exclude = set(exclude)
randInt = randint(0,9)
return my_custom_random() if randInt in exclude else randInt
print(random_exclude(2, 5, 7))
如果您有更大的列表,我建议使用集合操作,因为它们比推荐的答案明显更快。
random.choice(list(set([x for x in range(0, 9)]) - set(to_exclude)))
我对接受的答案和上面的代码进行了一些测试。
对于每个测试,我进行了 50 次迭代并测量了平均时间。 为了进行测试,我使用了 999999 的范围。
排除大小为 10 的元素:
接受的答案 = 0.1782s
这个答案 = 0.0953s
排除大小为 100 的元素:
接受的答案 = 01.2353s
这个答案 = 00.1117s
要排除大小为 1000 个的元素:
接受的答案= 10.4576s
这个答案 = 00.1009s
这是另一种方法,不使用 random.choice 或重复自身,直到正确为止:
import random
def random_exclusion(start, stop, excluded) -> int:
"""Function for getting a random number with some numbers excluded"""
excluded = set(excluded)
value = random.randint(start, stop - len(excluded)) # Or you could use randrange
for exclusion in tuple(excluded):
if value < exclusion:
break
value += 1
return value
它的作用是获取开始和结束之间的数字减去排除的数字数量。然后它会将数字加 1,直到它高于任何排除项。
让我们使用 0 到 5 之间的随机数(不包括 3)作为示例。由于我们将 5 减去 1,所以我们可以得到 0、1、2、3 或 4。但是我们想将最后的 2 向前移动 1为了防止出现 3,给我们 0、1、2、4 或 5。这不会创建一个包含排除值的列表,然后从中随机选择一个,它会获取一个值并添加到该值中,这是一个重要的时间,并且节省内存。
我进行了 Dominic Nagel 的测试(使用 time.perf_counter())来看看哪个更快:
对于 10 个元素:
这边的时间:0.00000162599899340421
他的时间:0.19212667199899441384
对于 100 个元素:
0.00000543000060133636
0.18264625200070441768
对于 1000 个元素:
0.00004090999893378467
0.21630024799902458632
对于 10000 个元素:
0.00087945000152103605
0.19593418199801818091
这个值呈指数级增长,而他的值保持在 0.2 秒左右,因此如果你要删除 10 亿个元素,我很可能会坚持使用他的值,除非你使用的是编译型编程语言。不过,这种方法可以节省大量内存,所以如果您打算这样做,那么您应该坚持使用我的方法。
这个效果很好:
from random import choice
exclude_this = [2, 5, 7]
my_random_int = choice(list(set(range(0, 10)) - set(exclude_this)))
使用集合(和
choice()
)是比理解列表更快的解决方案。如:
from numpy.random import randint
from random import choice
from typing import Set
def my_rand(start: int, end: int, exclude_values: Set[int] = None):
if not exclude_values: # in this case, there are no values to exclude so there is no point in filtering out any
return randint(start, end)
return choice(list(set(range(start, end)).difference(exclude_values)))
如
timeit
所示(这些测试中的语句很混乱,并且包含冗余语句,例如if True
,但这是为了模拟上面函数所做的检查):
>>> timeit.timeit(setup="from random import choice", stmt="choice(list(set(range(0, 300)).difference(set({1, 2, 3, 80, 189, 273}) if True else set())))", number=10000)
0.15672149998135865
>>> timeit.timeit(setup="from random import choice", stmt="choice(list(set(range(0, 300)).difference(set({1, 2, 3, 80, 189, 273}) if True else set())))", number=10000)
0.1651422999566421
>>> timeit.timeit(setup="from random import choice", stmt="choice(list(set(range(0, 300)).difference(set({1, 2, 3, 80, 189, 273}) if True else set())))", number=10000)
0.16615699999965727
>>> timeit.timeit(setup="from random import choice", stmt="choice([i for i in range(0, 300) if True and i not in set({1, 2, 3, 80, 189, 273})])", number=10000)
0.8613313999958336
>>> timeit.timeit(setup="from random import choice", stmt="choice([i for i in range(0, 300) if True and i not in set({1, 2, 3, 80, 189, 273})])", number=10000)
0.9154910000506788
>>> timeit.timeit(setup="from random import choice", stmt="choice([i for i in range(0, 300) if True and i not in set({1, 2, 3, 80, 189, 273})])", number=10000)
0.9114390999311581
这是一篇旧文章,但也许其他人需要一个想法。 为了避免浪费时间循环获取有用的随机数,我建议您使用 for 循环
[0,1,....9,]
创建一个从 0 到 9 的列表。然后你随机洗牌一次这个列表。
[ 4,8,0,....1]
要获得随机数,只需每次需要随机数时“轮询”此列表中的第一个数字(下次读取时该随机数将不存在于列表中)。
您可以从列表中删除该数字或使用索引来解析打乱后的列表。
import random
def randint_with_exclude(x : int, y: int, exclude=None):
if exclude is None:
exclude = []
v: int = random.randint(x, y)
while v in exclude:
v: int = random.randint(x, y)
else:
return v