Python 快速随机配对置换

Python 快速随机配对置换,python,arrays,algorithm,random,permutation,Python,Arrays,Algorithm,Random,Permutation,我需要为整数(索引)的有序数组生成一个随机配对排列。例如,对于N=10,输入数组如下所示: [0 1 2 3 4 5 6 7 8 9] 有效的随机配对排列为: [7 8 6 5 9 3 2 0 1 4] 请注意,索引是随机配对的,即:如果元素0(输入数组)与7(排列数组中的0th位置)配对,则元素7(输入数组)与0(排列数组中的7th位置)配对,以此类推。 输入数组中的任何元素都不能与其自身配对,即这不是有效的排列: [7 1 6 5 9 3 2 0 8 4] 因为元素1和8处于其“索引”

我需要为整数(索引)的有序数组生成一个随机配对排列。例如,对于
N=10
,输入数组如下所示:

[0 1 2 3 4 5 6 7 8 9]
有效的随机配对排列为:

[7 8 6 5 9 3 2 0 1 4]
请注意,索引是随机配对的,即:如果元素
0
(输入数组)与
7
(排列数组中的
0
th位置)配对,则元素
7
(输入数组)与
0
(排列数组中的
7
th位置)配对,以此类推。 输入数组中的任何元素都不能与其自身配对,即这不是有效的排列:

[7 1 6 5 9 3 2 0 8 4]
因为元素
1
8
处于其“索引”位置。这与SO中已多次涉及的主题有关:

我不确定如何(如果)使用上面列出的任何答案对我有利。 下面的代码满足我的需要,但对于较大的
N
值,它会变得非常慢。我需要对
N~1500
执行数千次此操作,这意味着此方法没有用处

我肯定有更聪明的方法来做这件事

N = 10
idxs = np.arange(N)

# Generate a shuffled array
input_idxs = idxs.copy()
np.random.shuffle(input_idxs)

# Empty array to store the final randomly paired indexes
shuffled_idxs = np.zeros(N, dtype=int)

used_idxs = []
for i in idxs:
    if i not in used_idxs:
        for j in input_idxs:
            if j not in used_idxs:
                if j != i:
                    shuffled_idxs[i] = j
                    shuffled_idxs[j] = i
                    used_idxs.append(i)
                    used_idxs.append(j)
                    break

互换配对意味着N始终是偶数。如果是这种情况,那么您可以抽取一半索引的样本,并将它们与剩余索引的无序列表配对:

import random
def randomPairs(N):
    result = [None] * N
    A  = random.sample(range(N),N//2)
    B  = random.sample(list(set(range(N))-set(A)),N//2)
    for a,b in zip(A,B):
        result[a] = b
        result[b] = a
    return result

for _ in range(10):
    print(randomPairs(6))

[4, 3, 5, 1, 0, 2]
[5, 4, 3, 2, 1, 0]
[5, 3, 4, 1, 2, 0]
[2, 3, 0, 1, 5, 4]
[3, 2, 1, 0, 5, 4]
[2, 3, 0, 1, 5, 4]
[4, 2, 1, 5, 0, 3]
[3, 4, 5, 0, 1, 2]
[2, 3, 0, 1, 5, 4]
[1, 0, 3, 2, 5, 4]
性能

# 1000 shuffles of 1500 elements in pairs
def test1():
    for _ in range(1000):
        randomPairs(1500)
        
from timeit import timeit
count = 1

t = timeit(test1,number=count)
print(t) # 1.12 seconds
[编辑]一个稍微快一点的解决方案是洗牌所有索引,并在洗牌列表中每隔一个位置与下一个位置配对:

import random
def randomPairs(N):
    result = [None] * N
    A  = random.sample(range(N),N)
    for a,b in zip(A[::2],A[1::2]):
        result[a] = b
        result[b] = a
    return result

为了确保我理解正确:您的目标是随机交换列表中的元素,要求每个数字只交换一次,对吗?我想您可以这样想。主要的一点是,随机交换必须成对进行。