Python 无替换的多个随机数序列

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,

在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,), 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具有相似的平均情况复杂性(最坏情况是无限的)。