在numpy中生成不重复的随机数

136

如何在numpy中生成不重复的随机数?

list = np.random.random_integers(20,size=(10))

“非重复性”是什么意思?是指随机数的序列永远不会重复吗?这是不可能的,因为随机数生成器的状态需要适应计算机的有限内存。还是说你的意思是没有一个数字会出现两次? - Sven Marnach
9
非重复意味着您的列表中没有重复项。 - Polynomial
2
也许你需要一个随机排列?http://docs.scipy.org/doc/numpy/reference/generated/numpy.random.permutation.html - cyborg
6个回答

178

numpy.random.Generator.choice 函数提供了一个 replace 参数,可以实现无重复采样:

from numpy.random import default_rng

rng = default_rng()
numbers = rng.choice(20, size=10, replace=False)
如果您使用的是1.17版本之前且没有Generator API的NumPy,则可以使用标准库中的random.sample()函数:random.sample()
print(random.sample(range(20), 10))

您还可以使用numpy.random.shuffle()和切片,但这样会更低效:

a = numpy.arange(20)
numpy.random.shuffle(a)
print a[:10]

在遗留的numpy.random.choice函数中也有一个replace参数,但由于随机数流的稳定性保证,该参数实现效率低下且不建议使用(它基本上是在内部执行洗牌和切片操作)。

一些时间:

import timeit
print("when output size/k is large, np.random.default_rng().choice() is far far quicker, even when including time taken to create np.random.default_rng()")
print(1, timeit.timeit("rng.choice(a=10**5, size=10**4, replace=False, shuffle=False)", setup="import numpy as np; rng=np.random.default_rng()", number=10**3)) #0.16003450006246567
print(2, timeit.timeit("np.random.default_rng().choice(a=10**5, size=10**4, replace=False, shuffle=False)", setup="import numpy as np", number=10**3)) #0.19915290002245456

print(3, timeit.timeit("random.sample( population=range(10**5), k=10**4)", setup="import random", number=10**3))   #5.115292700007558

print("when output size/k is very small, random.sample() is quicker")
print(4, timeit.timeit("rng.choice(a=10**5, size=10**1, replace=False, shuffle=False)", setup="import numpy as np; rng=np.random.default_rng()", number=10**3))  #0.01609779999125749
print(5, timeit.timeit("random.sample( population=range(10**5), k=10**1)", setup="import random", number=10**3))  #0.008387799956835806

因此,numpy.random.Generator.choice通常是最理想的选择,除非输出大小/k非常小。


1
如果我的n不是20,而是像1000000这样的数字,但我只需要其中的10个唯一数字,是否有更节省内存的方法? - mrgloom
2
在Python 3中,即使对于非常大的n,“random.sample(range(n), 10))”也是高效的,因为“range”对象只是一个存储起始、停止和步长值的小包装器,但不会创建整个整数列表。在Python 2中,您可以将“range”替换为“xrange”以获得类似的行为。 - Sven Marnach

145

我认为目前numpy.random.sample没有正常工作。这是我的方法:

import numpy as np
np.random.choice(range(20), 10, replace=False)

32
choice 函数的第一个参数中,可以用 n 代替 range(n)arange(n),例如 choice(20, 10, replace=False)。这样做是等价的。请注意,不要改变原意,并尽量使翻译更通俗易懂。 - Josh Bode
2
请注意,对于大型的 anp.random.choice(a, size, replace=False) 的速度非常慢 - 在我的计算机上,a=1M 时大约需要30毫秒。 - Matthew Rahtz
5
为避免处理非常大的n时出现时间和内存问题,请使用numpy.random.Generator.choice(从numpy v1.17开始可用)。 - benbo
1
我看到的主要缺点是np.random.choice没有轴参数 -> 它仅适用于1d数组。 - ssp
1
@ssp 然后只需获取足够的随机数并进行重塑... - user654123

8

几年后,在选择10000的平方中的40000个数字时(使用Numpy 1.8.1,iMac 2.7 GHz):

import random
import numpy as np

n = 10000
k = 4
np.random.seed( 0 )

%timeit np.random.choice( n**2, k * n, replace=True )  # 536 µs ± 1.58 µs
%timeit np.random.choice( n**2, k * n, replace=False ) # 6.1 s ± 9.91 ms

# https://docs.scipy.org/doc/numpy/reference/random/index.html
randomstate = np.random.default_rng( 0 )
%timeit randomstate.choice( n**2, k * n, replace=False, shuffle=False )  # 766 µs ± 2.18 µs
%timeit randomstate.choice( n**2, k * n, replace=False, shuffle=True )   # 1.05 ms ± 1.41 µs

%timeit random.sample( range( n**2 ), k * n )          # 47.3 ms ± 134 µs

为什么要选择从10000^2中选出40000个数字? 为了生成大型的 scipy.sparse.random 矩阵。在scipy 1.4.1中,使用了np.random.choice( replace=False ),速度非常慢。 向numpy.random的开发者们致以敬意。

1
简而言之:在这种情况下,使用np.random.Generator.choice而不是np.random.choice,可以获得6000倍的速度提升。 - undefined

3
你也可以通过排序来获得这个结果:
random_numbers = np.random.random([num_samples, max_int])
samples = np.argsort(random_numbers, axis=1)

0

可以使用Python的集合-列表转换。可以获取0到20之间的10个随机且不重复的数字:

import random
numbers=set()
while(len(numbers)<10):
    numbers.add(random.randint(0,20))

numbers=list(numbers)
random.shuffle(numbers)
print(numbers)

-4

只需生成一个包含所需数字范围的数组,然后通过不断地将随机数与数组中的第0个元素交换来对它们进行洗牌。这样就可以产生一个不包含重复值的随机序列。


2
生成的随机序列的另一个特点是,它并不特别随机。 - Sven Marnach
@SvenMarnach - 对于大多数情况来说,这已经足够随机了。如果他想要更随机的话,他可以使用双重随机方法。 - Polynomial
1
这是毫无意义的。问题提出者可以使用库调用来正确实现。它们比自定义版本更易于使用、更快速、更易读。我想不出任何理由在这里使用错误的算法,仅仅因为它可能足够“随机”,而使用正确的算法完全没有任何缺点。 - Sven Marnach
@SvenMarnach - 好的。我不了解numpy,所以我只是提供一个潜在的解决方案。 - Polynomial

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