【问题标题】:produce a random subset of all permutations产生所有排列的随机子集
【发布时间】:2018-07-22 13:10:40
【问题描述】:

我正在寻找一种方法来随机采样所有排列的固定长度子集。

import itertools
from random import shuffle

mylist = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T']

方法 A

下面的方法 A 存在排列过于相似的问题。

a_pre = itertools.permutations(mylist,20)
a = itertools.islice(a_pre,3)

list(a)

['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L '、'M'、'N'、'O'、'P'、'Q'、'R'、'S'、'T']

['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L '、'M'、'N'、'O'、'P'、'Q'、'R'、'T'、'S']

['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L '、'M'、'N'、'O'、'P'、'Q'、'S'、'R'、'T']

方法 B

方法 B 让我更接近我想要的结果,但这里总是存在在列表之间产生相同排序的风险,因此这种方法不可行。

#repeat n=3 times

shuffle(mylist)
print(mylist)

['J', 'B', 'M', 'A', 'O', 'C', 'K', 'S', 'H', 'Q', 'N', 'T '、'R'、'D'、'G'、'P'、'I'、'E'、'F'、'L']

['R', 'O', 'C', 'I', 'G', 'E', 'Q', 'L', 'P', 'J', 'F', 'N '、'A'、'B'、'H'、'T'、'D'、'K'、'M'、'S']

['L', 'O', 'I', 'G', 'B', 'E', 'R', 'A', 'D', 'N', 'J', 'S '、'H'、'F'、'K'、'M'、'Q'、'T'、'C'、'P']

【问题讨论】:

  • 您要生成多少个排列?您可以将已经拥有的那些存储在一个集合中(例如,在将列表连接到字符串之后,或者将其转换为元组之后)并避免使用已经使用的那些。
  • 对于给定的列表,有 20 个! = 2432902008176640000 种不同的排列,所以碰撞真的非常不可能。

标签: python permutation itertools


【解决方案1】:

我认为您的问题是one I had 的一个特例,因为 k=N 基于此,应适用该处所述的两种解决方案。第一个有点慢:)

因此,随机抽样(您也暗示了您的问题,只是丢弃重复项...)似乎是目前唯一的答案。

看看这个问题是否有生成解决方案或更通用的解决方案会非常有趣...... 这是基于该答案的代码:

import itertools
from random import shuffle

mylist = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T']
n=len(mylist)
k = n
m = 5
samples = set()
tries = 0
while len(samples) < m:
    samples.add(tuple(random.sample(mylist,k)))
    print (len(samples))

print(samples)
print(tries)

【讨论】:

    【解决方案2】:

    考虑一下 itertools random_permutation recipe:

    来自文档:

    def random_permutation(iterable, r=None):
        "Random selection from itertools.permutations(iterable, r)"
        pool = tuple(iterable)
        r = len(pool) if r is None else r
        return tuple(random.sample(pool, r))
    

    代码

    import string
    
    import more_itertools as mit
    
    
    iterable = string.ascii_uppercase[:-6]
    [random_permutation(iterable) for _ in range(3)]
    

    输出

    [('M', 'K', 'Q', 'A', 'I', 'J', 'H', 'T', 'C', 'E', 'P', 'L', 'B', 'N', 'G', 'F', 'S', 'D', 'O', 'R'), 
     ('A', 'G', 'I', 'S', 'E', 'T', 'B', 'Q', 'D', 'M', 'C', 'O', 'J', 'H', 'N', 'F', 'K', 'P', 'R', 'L'), 
     ('C', 'S', 'O', 'H', 'I', 'K', 'A', 'G', 'D', 'B', 'R', 'E', 'L', 'T', 'M', 'N', 'F', 'P', 'Q', 'J')]
    

    more_itertools 是第三方库,implements this recipe 为您服务。

    【讨论】:

      【解决方案3】:

      您可以使用它来生成N 元素的number-th 字典排列:

      def permutation_from_int(N, number):
          '''
          get the number-th lexicographic permutation of length N.
      
          N: the length of the permutation
          0 <= number <= factorial(N) -1: the number of the desired
          permutation
          '''
      
          # assert 0 <= number < factorial(N)
      
          ret = [None] * N
          select = list(range(N))
      
          for i in range(N - 1, -1, -1):
              index, number = divmod(number, factorial(i))
              element = select[index]
              ret[N - 1 - i] = element
              select.remove(element)
          return ret
      

      那么您只需要生成(并保留set of - 如果您想避免重复)代表排列的随机整数:

      N_TESTS = 17
      strg = 'ABCD'
      N = len(strg)
      N_MAX = factorial(N)
      seen = set()
      
      for _ in range(N_TESTS):
          number = randrange(N_MAX)
          while number in seen:
              number = randrange(N_MAX)
          seen.add(number)
          perm = permutation_from_int(N, number)
          print(''.join(strg[i] for i in perm))
      

      请注意,如果测试的数量大于所有排列的空间,这可能会永远循环......

      哪个打印(例如):

      DACB
      DBCA
      BADC
      BDCA
      DCAB
      DABC
      CADB
      DBAC
      DCBA
      ...
      

      但正如其他答案中所述:如果您有 20 个元素的排列,则重复排列的机会非常小!

      【讨论】:

        【解决方案4】:

        但是这里总是存在在列表之间产生相同排序的风险,所以这种方法是不可行的。

        您可以使用元组(因为列表不可散列)和集合(因此没有重复/相同的列表)来解决这个问题:

        from random import shuffle
        
        mylist = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T']
        myset = set()
        while len(myset) < 5: #change 5 to however many you want
             shuffle(mylist)
             myset.add(tuple(mylist))
        print([list(x) for x in myset])
        

        编辑:正如@tobias_k 指出的那样:

        对于给定的列表,有 20 个! = 2432902008176640000 种不同的排列,所以碰撞真的非常不可能。

        【讨论】:

        • 您可能会添加一些关于遇到重复的机会(对于给定的列表来说非常非常低)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-07-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-22
        • 2016-07-15
        相关资源
        最近更新 更多