在一个范围内生成“n”个唯一的随机数

365
我知道如何在Python中生成指定范围内的随机数。
random.randint(numLow, numHigh)

我知道我可以把这个放在循环中生成n个这样的数字

for x in range (0, n):
    listOfNumbers.append(random.randint(numLow, numHigh))

然而,我需要确保该列表中的每个数字都是唯一的。除了大量的条件语句之外,是否有一种直接生成 n 个唯一随机数的方法?

重要的是,列表中的每个数字与其他数字都不同。

因此

[12, 5, 6, 1] = 好

但是

[12, 5, 5, 1] = 不好,因为数字5出现两次。


4个回答

581

如果你只需要无放回地进行抽样:

>>> import random
>>> random.sample(range(1, 100), 3)
[77, 52, 45]

random.sample 接受一个总体和一个样本大小 k,并返回总体中随机选择的k 个成员。

如果您必须控制 k 大于 len(population) 的情况,您需要准备捕获 ValueError

>>> try:
...   random.sample(range(1, 2), 3)
... except ValueError:
...   print('Sample size exceeded population size.')
... 
Sample size exceeded population size

36
使用random.sample(xrange(1, 100), 3)代替range可以极大地提高代码运行速度,尤其是当你需要生成的数字范围很大时。因为它只会按需生成所需的3个数字(或者如果需要无重复抽样更多数字),而不是整个范围内的所有数字。例如:%timeit random.sample(xrange(10000), 3)=每次循环4.92微秒,%timeit random.sample(range(10000), 3)=每次循环126微秒。 - gaborous
53
如果你正在使用Python2,是的。如果你正在使用我的回答中提到的Python 3,它已经做到了这一点,因为在Py3k中xrange被替换成了range。 - Two-Bit Alchemist
我们可以不用将 random.sample() 调用放在 try...except 块中,而是检查样本的大小(上面的 3)是否小于或等于(<=)总体的大小(上面的 range(1, 2))。 - Hakim
@h4k1m 一般来说,在Python中,EAFP(try/except)比LBYL(if/else)更符合风格。 - Two-Bit Alchemist
3 can be replaced with randint(1, N) - DanielBell99
这不适用于浮点数,要创建一个浮点列表 sampl = np.random.uniform(low=0.5, high=13.3, size=(50,)),请参阅此链接 https://dev59.com/BWEh5IYBdhLWcg3wvVp4 - Tom Tom

33

首先生成数据范围,然后像这样洗牌

import random
data = list(range(numLow, numHigh))
random.shuffle(data)
print data

这样做,您将得到特定范围内的所有数字,但以随机顺序呈现。

不过您可以使用random.sample来从一系列数字中获取所需数量的元素,像这样:

print random.sample(range(numLow, numHigh), 3)

8
在Python 3中洗牌一个范围,你需要先将它转换为列表:data = list(range(numLow, numHigh)),否则会报错。请注意,洗牌是指随机打乱顺序的操作。 - CheshireCat

16

你可以将元素添加到一个set中,直到达到n

setOfNumbers = set()
while len(setOfNumbers) < n:
    setOfNumbers.add(random.randint(numLow, numHigh))

要小心选择比n所需范围更小的范围。这会导致循环无法结束,因为找不到新的数字插入到n


如果您使用random.sample,它会为该情况抛出一个ValueError异常(当然您可以捕获它)。 - Two-Bit Alchemist

9
你可以使用标准库中的random.sample函数从一个总体中选择k个元素:
import random
random.sample(range(low, high), n)

如果可能的数字范围相当大,您可以使用带有无限随机生成器的 itertools.islice

import itertools
import random

def random_gen(low, high):
    while True:
        yield random.randrange(low, high)

gen = random_gen(1, 100)
items = list(itertools.islice(gen, 10))  # Take first 10 random elements

在问题更新后,现在很明显你需要 n 个不同的数字。
import itertools
import random

def random_gen(low, high):
    while True:
        yield random.randrange(low, high)

gen = random_gen(1, 100)

items = set()

# Try to add elem to set until set length is less than 10
for x in itertools.takewhile(lambda x: len(items) < 10, gen):
    items.add(x)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接