Python 无替换的多个随机数序列
在numpy中,我可以使用代码Python 无替换的多个随机数序列,python,numpy,random,Python,Numpy,Random,在numpy中,我可以使用代码 from numpy.random import default_rng rng = default_rng() M, N, n = 10000, 1000, 3 rng.choice(np.arange(0, N), size=n, replace=False) 在不替换的情况下,从0到9获得三个随机样本 我想得到数千个这样的随机序列。正确的方法是什么? 我知道我能行 np.array([rng.choice(np.arange(0, N), size=(n,
from numpy.random import default_rng
rng = default_rng()
M, N, n = 10000, 1000, 3
rng.choice(np.arange(0, N), size=n, replace=False)
在不替换的情况下,从0到9获得三个随机样本
我想得到数千个这样的随机序列。正确的方法是什么?
我知道我能行
np.array([rng.choice(np.arange(0, N), size=(n,), replace=False) for i in range(0, M)])
但我想知道是否有更有效的方法使用numpy
来实现这一点
在这种情况下,建议采用以下方法
np.argsort(rng.random((M,N)),axis=1)[:, :n]
这是超快速和优雅的。然而,成本规模像nxm
,而不是我希望实现的nxm
还有其他方法吗?方法#1
对于N
>N
,我们可以使用带屏蔽的迭代方法,以便在每次迭代中,我们每行拾取一个以前未拾取的元素。实现看起来像这样-
R = np.arange(M)
mask = np.ones((M,N), dtype=bool)
idx = np.random.randint(0,N,(M))
mask[R,idx] = 0
for i in range(1,n):
lim = N-i
m2 = np.ones((M,lim), dtype=bool)
idx2 = np.random.randint(0,lim,(M))
m2[R,idx2] = 0
mask[mask] = m2.ravel()
out = np.nonzero(~mask)[1].reshape(-1,n)
# https://stackoverflow.com/a/51915131/ @Divakar
def random_num_per_grp(L):
# For each element in L pick a random number within range specified by it
r1 = np.random.rand(np.sum(L)) + np.repeat(np.arange(len(L)),L)
offset = np.r_[0,np.cumsum(L[:-1])]
return r1.argsort()[offset] - offset
R = np.arange(M)
mask = np.ones((M,N), dtype=bool)
idx = np.random.randint(0,N,(M,n))
mask[R[:,None],idx] = 0
rows_notdone = mask.sum(1)!=N-n
while np.any(rows_notdone):
idx0 = random_num_per_grp(mask[rows_notdone].sum(1))
steps = np.r_[0,mask.sum(1).cumsum()[:-1]]
flat_idx0 = steps[rows_notdone] + idx0
m2 = np.ones(mask.sum(), dtype=bool)
m2[flat_idx0] = 0
mask[mask] = m2
rows_notdone = mask.sum(1)!=N-n
out = np.nonzero(~mask)[1].reshape(-1,n)
如果您需要随机化每行的数字,请使用问题帖子中链接的兰德技巧:
out = np.take_along_axis(out, np.random.rand(M,n).argsort(1), axis=1)
如果使用m2
创建常量数组让您感到困扰,请在循环前初始化后重新使用,同时保持其余代码不变-
m2 = np.ones((M,N-1), dtype=bool)
for i in range(1,n):
lim = N-i
idx2 = np.random.randint(0,lim,(M))
m2[R,idx2] = 0
mask[mask] = m2.ravel()
m2[R,idx2] = 1
m2 = m2[:,:-1]
方法#2类似于方法#1,但初始化部分的大部分工作是设置每行的unqiue随机数。另一个while
迭代部分负责处理无法分配唯一行的行。使用N
>N
,我们几乎不需要迭代。实现看起来像这样-
R = np.arange(M)
mask = np.ones((M,N), dtype=bool)
idx = np.random.randint(0,N,(M))
mask[R,idx] = 0
for i in range(1,n):
lim = N-i
m2 = np.ones((M,lim), dtype=bool)
idx2 = np.random.randint(0,lim,(M))
m2[R,idx2] = 0
mask[mask] = m2.ravel()
out = np.nonzero(~mask)[1].reshape(-1,n)
# https://stackoverflow.com/a/51915131/ @Divakar
def random_num_per_grp(L):
# For each element in L pick a random number within range specified by it
r1 = np.random.rand(np.sum(L)) + np.repeat(np.arange(len(L)),L)
offset = np.r_[0,np.cumsum(L[:-1])]
return r1.argsort()[offset] - offset
R = np.arange(M)
mask = np.ones((M,N), dtype=bool)
idx = np.random.randint(0,N,(M,n))
mask[R[:,None],idx] = 0
rows_notdone = mask.sum(1)!=N-n
while np.any(rows_notdone):
idx0 = random_num_per_grp(mask[rows_notdone].sum(1))
steps = np.r_[0,mask.sum(1).cumsum()[:-1]]
flat_idx0 = steps[rows_notdone] + idx0
m2 = np.ones(mask.sum(), dtype=bool)
m2[flat_idx0] = 0
mask[mask] = m2
rows_notdone = mask.sum(1)!=N-n
out = np.nonzero(~mask)[1].reshape(-1,n)
说到成本,你担心的是内存还是性能?我担心的主要是计算性能(我可以通过批量生成随机变量来减少内存)。正如建议的那样,你可以使用
np.random.rand(M,N).argpartition(N)[:,:N]
。使用argpartition
可以获得性能效率,从而取代argsort
。那对你有用吗?嗯。。这仍然可以像nxm
那样缩放。有没有办法让某样东西在基准面上的比例不那么差呢?谢谢。我将此标记为答案,因为它显著降低了成本。然而,成本缩放仍然是“MxN”,因为掩码数组具有该大小,并且生成了大约“mx(N-N)^2”个随机变量,以便在“N”接近“N”时更好地使用它。@Tohiko对于掩码,它实际上是MxN/8
,因为它是布尔数组,所以内存占用要小得多。你是怎么想到使用“mx(N-N)^2”随机变量的?正如我所说,对于方法2,您几乎不需要在循环时进入内部。因此,基本上除了掩码之外,还有np.random.randint(0,N,(M,N))
和np.nonzero
部分,它们再次生成Mxn
数组。所以,总共是mask+Mx2*n
。就这些,主要是内存占用。啊,对不起。我指的是方法1。就我所见,方法#2具有相似的平均情况复杂性(最坏情况是无限的)。