如何随机排列列表以满足条件

时间:2019-11-05 18:36:39

标签: python list random

我想生成列表的n个随机版本,以使每次随机化的顺序都不同于之前的顺序,而且每个元素的位置都必须不同于之前列表中的位置。我已经生成了该列表的所有可能排列的列表,但是我在如何选择符合我的条件的子列表上遇到了麻烦。我在想也许列表理解可以工作,但不确定如何完成。

# constraints: n <= 12

lst = ['John', 'William', 'Michael', 'Victor', 'Tom', 'Charley', 'Patrick', 'David']

permutations = list(itertools.permutations(lst))

randomized_lists = [i for i in permutations if <conditions>]

有什么想法可以做到吗?另外,是否有更好(更有效)的方法来解决该问题?

3 个答案:

答案 0 :(得分:1)

这可以通过修改Fisher–Yates shuffle算法来避免与自身交换一项来完成。也就是说,对于 k 中的每个项目(其中 k 从0开始),而不是选择[0, k][k, n - 1]中的随机项目(包括k),在[0, k)(k, n - 1]中选择一个随机项目(不包括k),然后将 k 处的项目与该随机项目交换。


以下方法实现了这个想法:

import random

def shuffle_diff_pos(list):
  """ Returns a shuffled list in which 
      each item moves to a different position. """
  list=[x for x in list]
  if len(list)>=2:
    i=len(list)-1
    while i>0:
      k=random.randint(0, i-1)
      tmp=list[i];list[i]=list[k];list[k]=tmp
      i-=1
  return list

lst = ['John', 'William', 'Michael', 'Victor', 'Tom', 'Charley', 'Patrick', 'David']
randomized_lists = [shuffle_diff_pos(lst) for _ in range(12)]

答案 1 :(得分:0)

您可以使用此列表理解:

result = [i for i in permutations if any(a != b for a, b in zip(i, lst))]

我们正在any(a == b for a, b in zip(i, lst)测试lst中同一索引上是否有任何匹配项以及该行中的排列。

答案 2 :(得分:0)

只有第二个条件是必要的:如果所有元素均不在其原始位置,则顺序将有所不同。您无需查看所有可能的排列并随机选择一个排列(这比混洗直到满足您的条件效率低得多),而是可以生成符合规格的列表。

您的列表有8个元素,这意味着每个职位可以有7个选项之一(当前所有元素除外)。这等效于将一个矩阵放置在零矩阵中,从而使每个矩阵都不会与另一个矩阵占据相同的行或列(正常改组),但存在元素不能位于主对角线上的限制,这使事情变得复杂。

您可以实施Fisher-Yates-like随机播放,其中包括限制。这将使您的列表随机化,而不必筛选大量的排列。

使用索引来避免放置冲突可能要容易得多,而不是在交换每个元素时查找它们的位置。

n = len(lst)
ind = list(range(n))
for i in range(n - 1):
    # Case 1: Swap out necessary, conflictonly with self
    if ind[i] == i:
        swap_ind = random.randrange(i + 1, n)
    else:
        try:
            # Case 2: swap might cause conflict
            ind_of_i = ind.index(i, i + 1)
            swap_ind = random.randrange(i, n - 1)
            if swap_ind >= ind_of_i:
                swap_ind += 1
        except ValueError:
            # Case 3: no conflict, full range available
            swap_ind = random.randrange(i, n)
    ind[i], ind[swap_ind] = ind[swap_ind], ind[i]

您现在有了一个随机排序的索引列表,这些索引中的任何一个都没有终止于它的开始位置。您可以使用以下方式排列输入列表

result = [lst[i] for i in ind]

请记住,检查ind[swap_ind]的实现在时间上效率很低,并且实际上抵消了对索引进行排序而不是对实际值进行排序的好处。您可以用第二个列表替换它,该列表维护索引的反向查找表的位置,并将其与ind交换。结果将是O(1)查找和两倍的内存利用率。

我不确定结果在所有可用排列的空间上的统一程度如何,但我怀疑(a)它可能足以满足您的需求,并且(b)可以轻松修改为如果必要/可能,则更统一。