geo*_*org 12 python random permutation
如何随机洗牌以使所有元素都不在其原始位置?
换句话说,给定一个A包含不同元素的列表,我想生成B它的排列
n,a[n] != b[n]例如
a = [1,2,3,4]
b = [4,1,2,3] # good
b = [4,2,1,3] # good
a = [1,2,3,4]
x = [2,4,3,1] # bad
Run Code Online (Sandbox Code Playgroud)
我不知道这种排列的正确术语(它是"总"吗?)因此很难用谷歌搜索.正确的术语似乎是"紊乱".
经过一些研究后,我能够实现"早期拒绝"算法,如本文所述.它是这样的:
import random
def random_derangement(n):
while True:
v = range(n)
for j in range(n - 1, -1, -1):
p = random.randint(0, j)
if v[p] == j:
break
else:
v[j], v[p] = v[p], v[j]
else:
if v[0] != 0:
return tuple(v)
Run Code Online (Sandbox Code Playgroud)
我们的想法是:我们不断改组数组,一旦我们发现我们正在处理的排列无效(v[i]==i),我们就会从头开始破解.
快速测试表明,该算法统一生成所有紊乱:
N = 4
# enumerate all derangements for testing
import itertools
counter = {}
for p in itertools.permutations(range(N)):
if all(p[i] != i for i in p):
counter[p] = 0
# make M probes for each derangement
M = 5000
for _ in range(M*len(counter)):
# generate a random derangement
p = random_derangement(N)
# is it really?
assert p in counter
# ok, record it
counter[p] += 1
# the distribution looks uniform
for p, c in sorted(counter.items()):
print p, c
Run Code Online (Sandbox Code Playgroud)
结果:
(1, 0, 3, 2) 4934
(1, 2, 3, 0) 4952
(1, 3, 0, 2) 4980
(2, 0, 3, 1) 5054
(2, 3, 0, 1) 5032
(2, 3, 1, 0) 5053
(3, 0, 1, 2) 4951
(3, 2, 0, 1) 5048
(3, 2, 1, 0) 4996
Run Code Online (Sandbox Code Playgroud)
为简单起见,我选择此算法,此简要介绍了其他想法.
感谢大家 ))