Python 如何创建一个包含随机项的numpy数组,每个索引中排除一个元素?

Python 如何创建一个包含随机项的numpy数组,每个索引中排除一个元素?,python,numpy,random,numpy-ndarray,Python,Numpy,Random,Numpy Ndarray,我有一个可能值的数组val(例如val=[0,1,2,3,4,5])和一个选择值的数组A(可能很长的列表)(例如A=[2,3,1,0,2,1,…,2,3,1,0,4]) 现在我想创建一个与A长度相同的数组B,这样A[I]对于每个I都不同于B[I],并且B中的条目是随机选择的。如何使用numpy高效地执行此操作?这有点浪费,因为它会为a中的每个项目创建一个临时列表,但在其他方面会满足您的要求: from random import choice val = [0, 1, 2, 3, 4, 5]

我有一个可能值的数组
val
(例如
val=[0,1,2,3,4,5]
)和一个选择值的数组
A
(可能很长的列表)(例如
A=[2,3,1,0,2,1,…,2,3,1,0,4]


现在我想创建一个与
A
长度相同的数组
B
,这样
A[I]
对于每个
I
都不同于
B[I]
,并且
B
中的条目是随机选择的。如何使用numpy高效地执行此操作?

这有点浪费,因为它会为
a
中的每个项目创建一个临时列表,但在其他方面会满足您的要求:

from random import choice


val = [0, 1, 2, 3, 4, 5]
A = [2, 3, 1, 0, 2, 1, 2, 3, 1, 0, 4]

val = set(val)
B = [choice(list(val - {x})) for x in A]
print(B) # -> [4, 2, 3, 2, 5, 4, 1, 5, 5, 4, 1]
简言之:

发生的情况是
val
被转换为
set
,其中
a
中的当前项被删除。因此,从这个结果子集中随机选择一个项目,并将其添加到
B


您还可以使用以下工具进行测试:

print(all(x!=y for x, y in zip(A, B)))
这当然会返回
True



最后,请注意,上述方法仅适用于可散列项。因此,如果您可能有类似于
val=[[1,2],[2,3],…]
的内容,例如,您将遇到问题。

这里有一种矢量化方法-

def randnum_excludeone(A, val):
    n = val[-1]
    idx = np.random.randint(0,n,len(A))
    idx[idx>=A] += 1
    return idx
我们的想法是为
A
中的每个条目生成随机整数,覆盖
val
减去
1
的整个长度。然后,如果生成的当前随机数等于或大于当前
A
元素,则添加
1
,否则保留它。因此,对于生成的任何小于当前
A
数的随机数,我们保留它。否则,添加
1
后,我们将从当前
A
编号中进行偏移。这是我们的最终输出-
idx

让我们验证随机性,并确保它在非A元素中是一致的-

In [42]: A
Out[42]: array([2, 3, 1, 0, 2, 1, 2, 3, 1, 0, 4])

In [43]: val
Out[43]: array([0, 1, 2, 3, 4, 5])

In [44]: c = np.array([randnum_excludeone(A, val) for _ in range(10000)])

In [45]: [np.bincount(i) for i in c.T]
Out[45]: 
[array([2013, 2018,    0, 2056, 1933, 1980]),
 array([2018, 1985, 2066,    0, 1922, 2009]),
 array([2032,    0, 1966, 1975, 2040, 1987]),
 array([   0, 2076, 1986, 1931, 2013, 1994]),
 array([2029, 1943,    0, 1960, 2100, 1968]),
 array([2028,    0, 2048, 2031, 1929, 1964]),
 array([2046, 2065,    0, 1990, 1940, 1959]),
 array([2040, 2003, 1935,    0, 2045, 1977]),
 array([2008,    0, 2011, 2030, 1937, 2014]),
 array([   0, 2000, 2015, 1983, 2023, 1979]),
 array([2075, 1995, 1987, 1948,    0, 1995])]
大型阵列的基准测试

其他矢量化方法:

计时结果-

In [66]: np.random.seed(0)
    ...: A = np.random.randint(0,6,100000)

In [67]: %timeit pp(A,val)
100 loops, best of 3: 3.11 ms per loop

In [68]: %timeit randnum_excludeone(A, val)
100 loops, best of 3: 2.53 ms per loop

In [69]: np.random.seed(0)
    ...: A = np.random.randint(0,6,1000000)

In [70]: %timeit pp(A,val)
10 loops, best of 3: 39.9 ms per loop

In [71]: %timeit randnum_excludeone(A, val)
10 loops, best of 3: 25.9 ms per loop
val
的范围扩展到
10
-

In [60]: np.random.seed(0)
    ...: A = np.random.randint(0,10,1000000)

In [61]: %timeit pp(A,val)
10 loops, best of 3: 31.2 ms per loop

In [62]: %timeit randnum_excludeone(A, val)
10 loops, best of 3: 23.6 ms per loop

这是另一种方法
B
首先获得
a
的随机洗牌。然后,
A
B
重叠的所有值都被洗牌。在所有重叠元素具有相同值的特殊情况下,它们会被随机良好值交换

这种方法的有趣之处在于,当
A
只包含一组非常有限的不同值时,它也可以工作。与其他方法不同,
B
A
的精确混合,因此当
A
的分布不均匀时,它也可以工作。另外,
B
是一种完全随机的洗牌,除了在相同的索引上不同的要求

随机导入
N=10000
A=[random.randrange(0,6)表示范围(N)]
B=a.复制()
随机。洗牌(b)
印刷品(A)
印刷品(B)
尽管如此:
equal_vals={i代表zip(A,B)中的i,j,如果i==j}
打印(长度(相等值),相等值)
如果len(equal_vals)=0:#完成,则相同位置上没有相等的值
打破
其他:
equal_ind=[k表示枚举中的k,(i,j)(zip(A,B)),如果i==j]
#创建a和B相等的索引列表
随机。shuffle(equal_ind)#当列表排序时,将其随机排列以获得随机顺序
如果len(equal_vals)==1:#特殊情况,则所有相等的索引都具有相同的值
特殊值=相等值.pop()
#找到可以毫无问题地放置特殊值的所有索引
good_ind=[k代表k,(i,j)在枚举(zip(A,B))中,如果i!=special_val和j!=special_val]
如果len(良好)小于len(相等):
打印(“问题:列表A中的相等值太多”)
其他:
#用一个随机的好索引替换每个坏索引
所选=随机样本(良好样本,长度(相等))
对于拉链中的k1、k2(相等标识,选择标识):
b[k1],b[k2]=b[k2],b[k1]#swap
打破
elif len(相等值)>=2:
#通过指数相等的lis排列B;
#由于至少有两个不同的值,因此至少有两个索引将获得所需的值
上一个=等于[0]
old_first=B[prev]
对于k等于[1:]:
B[prev]=B[k]
prev=k
B[prev]=旧的
印刷品(A)
印刷品(B)

又快又脏,可以进行改进,但现在开始。 您的要求可以通过以下方式实现:

val = [0, 1, 2, 3, 4, 5]
A = [2, 3, 1, 0, 2, 1,4,4, 2, 3, 1, 0, 4]
val_shifted = np.roll(val,1) 
dic_val = {i:val_shifted[i] for i in range(len(val_shifted))}
B = [dic_val[i] for i in A]
这样可以得到满足您要求的结果

A = [2, 3, 1, 0, 2, 1, 4, 4, 2, 3, 1, 0, 4]
B = [1, 2, 0, 5, 1, 0, 3, 3, 1, 2, 0, 5, 3]

一个简单的方法是画出模n的A和B之间的差,其中n是可能结果的数量。A[我]!=B[i]表示该差值不是零,因此我们从1,…,n-1得出:

n,N = 10,100
A = np.random.randint(0,n,N)

D = np.random.randint(1,n,N)
B = (A-D)%n
更新:虽然可以说这个解决方案很优雅,但它并不是最快的。我们可以通过替换(慢)模运算符来节省一些时间,只需测试负值并向其添加n

在这种形式下,这个解决方案开始看起来非常类似于@Divakar:两个可能的值块,一个需要移位

但是我们可以做得更好:我们不需要平均移动一半的值,而只需要在A[i]==B[i]的情况下,我们就可以交换掉它们。由于除非允许值列表非常短,否则这种情况预计很少发生,因此代码运行速度更快:

B = np.random.randint(1,n,N)
B[B==A] = 0

一种方法是先做
p=np.random.permutation(len(A))
,然后再做
B=A[p]
。这并不能保证
A[i]!=B[i]
虽然每个
i
A[i]=B[i]
在我的情况下是必不可少的
val
是否始终是按顺序排列的数字
0到n
和顺序编号?@Divakar事实上,它们也可以是数组,但是有数字的解决方案就足够了。更具体地说,我的问题是,这些数字是否总是按顺序排列的,或者你是否有一些数字
B = np.random.randint(1,n,N)
B[B==A] = 0