Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/349.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
python洗牌,这样位置就不会重复_Python_Random_Shuffle - Fatal编程技术网

python洗牌,这样位置就不会重复

python洗牌,这样位置就不会重复,python,random,shuffle,Python,Random,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],因为它们都重复元素的一个位置 注意:列表中的每个元素都是唯一的id。不允许重复相同的

我想对一个列表进行随机洗牌,但有一个条件:洗牌后一个元素不能处于相同的原始位置

在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],因为它们都重复元素的一个位置

注意:列表中的每个元素都是唯一的id。不允许重复相同的元素


有什么想法吗?谢谢

您可以生成所有可能的有效洗牌:

>>> 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)

在循环中随机化并不断拒绝结果,直到您的条件得到满足:

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

这是另一种看法。您可以根据需要选择一种或另一种解决方案。这不是一行,而是对元素的索引而不是元素本身进行洗牌。因此,原始列表可能有重复的值或无法比较的类型值,或者比较起来可能比较昂贵

#! /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\u ex
[2,2,2]
,此方法将不断地产生
[2,2,2]
。其他解决方案将为您提供空列表。我不知道你在这种情况下想要什么

我将这种洗牌描述为“没有固定点的排列”。它们也被称为

随机排列是一种错乱的概率约为1/e(证明起来很有趣)。不管名单有多长,这都是事实。因此,一个明显的随机混乱算法是正常洗牌,并一直洗牌直到你出现混乱。所需的洗牌次数预计约为3次,而且洗牌次数很少超过10次

(1-1/e)**11 < 1%
(1-1/e)**11<1%

假设一个聚会上有n个人,每个人都带了一把伞。晚会结束时,每个人从篮子里随机拿一把伞。没有人拥有自己的保护伞的可能性有多大


这是另一个算法。随意拿牌。如果您的第i张卡是卡i,请将其放回并重试。唯一的问题是,如果最后一张牌是你不想要的。把它换成另一个

我认为这是公平的(均匀随机的)


使用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

当列表中包含多个彼此相等的项目时,您会期望什么。例如,当列表为
[2,2,2]
时,您希望发生什么?使用A及其
旋转
方法的组合可以产生预期的结果。但是,在某些情况下,如上述评论中所述,您并没有明确定义预期结果。@crayzeewulf说得好!我的数据结构永远不会遇到这种情况,谢谢@肖恩,你介意给我更多的细节吗?听起来是个不错的尝试方法,谢谢!谢谢我认为这是个好主意。对于非常大的列表,内存会使此方法可能存在一些问题。。。想象一下,有一个10毫米元素的列表,以及所有可能的组合…对,对于非常大的列表来说完全不好。有
nn个元素的排列,一个非常大的数字。我得到一个列表长度为12的内存错误。这只适用于非常小的列表。谢谢,这是我在开始时所想的,但不确定在时间方面的效率会有多高……所需的预期洗牌次数是e,小于3@Dnaiel-效率只会是大型列表或多次迭代的问题。在进行优化时,很容易减少随机性。这个解决方案不会让我想得太多。
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