我想对列表进行随机调整,但有一个条件:在shuffle之后元素永远不会处于相同的原始位置。
在python中有一个单行方式来做列表吗?
示例:
list_ex = [1,2,3]
以下混洗列表中的每一个应该在随机播放后具有相同的采样概率:
list_ex_shuffled = [2,3,1]
list_ex_shuffled = [3,1,2]
但不允许排列[1,2,3],[1,3,2],[2,1,3]和[3,2,1],因为它们都重复了一个元素位置
注意:list_ex中的每个元素都是唯一的id。不允许重复相同的元素。
有什么想法吗?谢谢!
答案 0 :(得分:5)
您可以生成所有可能的有效改组:
>>> list_ex = [1,2,3]
>>> import itertools
>>> list(itertools.ifilter(lambda p: not any(i1==i2 for i1,i2 in zip(list_ex, p)),
... itertools.permutations(list_ex, len(list_ex))))
[(2, 3, 1), (3, 1, 2)]
对于其他一些序列:
>>> list_ex = [7,8,9,0]
>>> list(itertools.ifilter(lambda p: not any(i1==i2 for i1,i2 in zip(list_ex, p)),
... itertools.permutations(list_ex, len(list_ex))))
[(8, 7, 0, 9), (8, 9, 0, 7), (8, 0, 7, 9), (9, 7, 0, 8), (9, 0, 7, 8), (9, 0, 8, 7), (0, 7, 8, 9), (0, 9, 7, 8), (0, 9, 8, 7)]
如果你只想要一个结果,你也可以通过短路迭代器来提高效率:
>>> list_ex = [1,2,3]
>>> i = itertools.ifilter(lambda p: not any(i1==i2 for i1,i2 in zip(list_ex, p)),
... itertools.permutations(list_ex, len(list_ex)))
>>> next(i)
(2, 3, 1)
但是,它不会是随机选择。您必须生成所有这些并选择一个作为实际的随机结果:
>>> list_ex = [1,2,3]
>>> i = itertools.ifilter(lambda p: not any(i1==i2 for i1,i2 in zip(list_ex, p)),
... itertools.permutations(list_ex, len(list_ex)))
>>> import random
>>> random.choice(list(i))
(2, 3, 1)
答案 1 :(得分:5)
在循环中随机化并继续拒绝结果,直到满足条件:
import random
def shuffle_list(some_list):
randomized_list = some_list[:]
while True:
random.shuffle(randomized_list)
for a, b in zip(some_list, randomized_list):
if a == b:
break
else:
return randomized_list
答案 2 :(得分:4)
我将这种洗牌描述为“没有固定点的排列”。它们也被称为derangements。
随机排列是一种紊乱的概率约为1 / e(有趣的证明)。这个列表很长。因此,给出随机紊乱的一种明显的算法是正常地对卡进行洗牌,并且一直保持洗牌,直到你出现紊乱。预期的必要洗牌次数约为3次,而且很少需要洗牌十次以上。
(1-1/e)**11 < 1%
假设一个聚会上有n个人,每个人都带了一把雨伞。在聚会结束时,每个人都从篮子里随意拿一把伞。没有人拥有自己的保护伞的概率是多少?
答案 3 :(得分:2)
这是对此的另一种看法。您可以根据需要选择一种或另一种解决方案。这不是一个单行,而是对元素的索引进行洗牌而不是元素本身。因此,原始列表可能具有重复的值或不能比较的类型的值,或者可能比较昂贵。
#! /usr/bin/env python
import random
def shuffled_values(data):
list_length = len(data)
candidate = range(list_length)
while True:
random.shuffle(candidate)
if not any(i==j for i,j in zip(candidate, range(list_length))):
yield [data[i] for i in candidate]
list_ex = [1, 2, 3]
list_gen = shuffled_values(list_ex)
for i in range(0, 10):
print list_gen.next()
这给出了:
[2, 3, 1]
[3, 1, 2]
[3, 1, 2]
[2, 3, 1]
[3, 1, 2]
[3, 1, 2]
[2, 3, 1]
[2, 3, 1]
[3, 1, 2]
[2, 3, 1]
如果list_ex
为[2, 2, 2]
,此方法将一遍又一遍地产生[2, 2, 2]
。其他解决方案将为您提供空列表。在这种情况下,我不确定你想要什么。
答案 4 :(得分:1)
这是另一种算法。随意拿牌。如果您的第i张卡是卡i,请将其放回原处并重试。唯一的问题是,如果当你到达最后一张卡时,那就是你不想要的那张卡。与其他人交换。
我认为这是公平的(统一随机)。
import random
def permutation_without_fixed_points(n):
if n == 1:
raise ArgumentError, "n must be greater than 1"
result = []
remaining = range(n)
i = 0
while remaining:
if remaining == [n-1]:
break
x = i
while x == i:
j = random.randrange(len(remaining))
x = remaining[j]
remaining.pop(j)
result.append(x)
i += 1
if remaining == [n-1]:
j = random.randrange(n-1)
result.append(result[j])
result[j] = n
return result
答案 5 :(得分:1)
使用Knuth-Durstenfeld随机排列列表。只要在改组过程中发现它处于原始位置,就会从头开始新的改组过程,直到返回合格的布置为止。该算法的时间复杂度是最小的常数项:
def _random_derangement(x: list, randint: Callable[[int, int], int]) -> None:
Random derangement list x in place, and return None.
An element can never be in the same original position after the shuffle. provides uniform distribution over permutations.
The formal parameter randint requires a callable object such as rand_int(b, a) that generates a random integer within the specified closed interval.
from collections import namedtuple
sequence_type = namedtuple('sequence_type', ('sequence_number', 'elem'))
x_length = len(x)
if x_length > 1:
for i in range(x_length):
x[i] = sequence_type(sequence_number = i, elem = x[i])
end_label = x_length - 1
while True:
for i in range(end_label, 0, -1):
random_location = randint(i, 0)
if x[random_location].sequence_number != i:
x[i], x[random_location] = x[random_location], x[i]
else:
break
else:
if x[0].sequence_number != 0: break
for i in range(x_length):
x[i] = x[i].elem