【问题标题】:Loop through different sets of unique permutations循环遍历不同的独特排列集
【发布时间】:2010-12-31 05:50:43
【问题描述】:

我很难开始为这个问题设计代码。

我有固定数量的随机数,在本例中是 8 个数字。 R[] = { 1, 2, 3, 4, 5, 6, 7, 8 };

这将被放置在 3 组数字中,唯一的限制是每组至少包含一个值,并且每个值只能使用一次。 编辑:所有 8 个数字都应使用

例如:

R1[] = { 1, 4 }

R2[] = { 2, 8, 5, 6 }

R3[] = { 7, 3 }

我需要遍历一组 R1、R2、R3 的所有可能组合。顺序并不重要,所以如果上面的例子发生了,我就不需要了

R1[] = { 4, 1 }

R2[] = { 2, 8, 5, 6 }

R3[] = { 7, 3 }

正常

R1[] = { 2, 8, 5, 6 }

R2[] = { 7, 3 }

R3[] = { 1, 4 }

什么是好方法?

【问题讨论】:

  • 在您给出的示例中,使用了所有数字。这是意外还是您一直希望如此?
  • @Vincent @aaron 我在我的回答中都假设是的。

标签: algorithm loops permutation


【解决方案1】:

我面前有 Knuth 第 4 卷,第 3 卷,生成所有组合和分区,第 7.2.1.5 节 生成所有设置分区(分册中的第 61 页) .

首先他详细介绍了 算法 H,按字典顺序限制增长字符串,这要归功于 George Hutchinson。它看起来很简单,但我现在不打算深入研究它。

在下一页的详细说明下设置分区的灰色代码他思考:

然而,假设我们对所有的分区不感兴趣;我们可能只想要那些有 m 块的。我们可以通过较小的受限增长字符串集合来运行它,仍然一次更改一个数字吗?

然后他详细说明了 Frank Ruskey 的解决方案。

简单的解决方案(并且肯定是正确的)是在m==3 且所有分区都不是空集的分区上编写算法 H 过滤(根据您声明的约束)。我怀疑算法 H 运行得非常快,所以过滤成本不会很大。

如果您在 8051 上实现此功能,您可以从 Ruskey 算法开始,然后仅过滤包含空集的分区。

如果您在小于 8051 且毫秒数很重要的情况下实现此功能,您可以为三个分区中的每一个添加一个唯一元素(一个简单的三层嵌套循环),然后通过对其余五个分区进行扩充m==3 的元素使用 Ruskey 算法。您不必过滤任何内容,但您必须跟踪剩余的五个元素进行分区。

从通用算法中过滤的好处是,您不必验证自己的聪明程度,而且您稍后会改变对约束条件的想法,而无需修改您的聪明程度。

我什至可能会在以后找到解决方案,但目前仅此而已。

附:对于 Java 孔雀鱼:我发现在“George Hutchison 限制增长字符串”中搜索某个包 ca.ubc.cs.kisynski.bell 以及实现 Hutchison 算法的方法 growthStrings() 的文档。

似乎可以通过http://www.cs.ubc.ca/~kisynski/code/bell/获得

【讨论】:

    【解决方案2】:

    可能不是最好的方法,但应该可以。

    求三个数之和为 8 的组合数:

    1,1,6
    1,2,5
    1,3,4
    2,2,4
    2,3,3
    

    要找到上面我开始的:

    6,1,1 then subtracted 1 from six and added it to the next column...
    5,2,1 then subtracted 1 from second column and added to next column...
    5,1,2 then started again at first column...
    4,2,2 carry again from second to third
    4,1,3 again from first...
    3,2,3 second -> third
    3,1,4 
    

    知道不到一半是 2 必须找到所有组合...但是由于列表不长,我们不妨走到最后。

    现在将 3 个列表从大到小排序(反之亦然) 现在对每个 3 列表相对于彼此进行排序。 将每个唯一列表复制到唯一列表列表中。 我们现在有了所有加到 8 的组合(我认为是五个列表)。

    现在考虑上述集合中的一个列表

    6,1,1 所有可能的组合都通过以下方式找到:

    8 选 6,(因为我们选了 6,所以只剩下 2 个可供选择)2 选 1,1 选 1 计算结果为 28*2*1 = 56,值得知道有多少种可能性,以便您进行测试。

    n 选择 r(从 n 个选项中选择 r 个元素)

    n C r = n! / [(n-r)! r!]

    所以现在你有了列表中每个组件的迭代总数,第一个是 28...

    那么从 8 中选择 6 项与创建一个 8 减 2 元素的列表相同,但是哪两个元素呢?

    如果我们删除 1,2,剩下 3,4,5,6,7,8。让我们考虑所有 2 组...从 1,2 开始接下来是 1,3...所以下面是逐列阅读的。

    12
    13 23 
    14 24 34
    15 25 35 45
    16 26 36 46 56
    17 27 37 47 57 67
    18 28 38 48 58 68 78
    

    将上述每一列相加得到 28。(所以这只涵盖了列表中的第一个数字 (6,1,1) 对第二个数字 (a one) 重复该过程,即“2 选择 1”所以从上面列表中剩下的两位数中,我们选择两位中的一位,然后对于最后一位,我们选择剩余的一位。

    我知道这不是一个详细的算法,但我希望你能够开始。

    【讨论】:

      【解决方案3】:

      把问题放在头上,你会找到一个直截了当的解决方案。您有 8 个号码,每个号码都需要分配给一组; “解决方案”只有在每个组至少分配一个数字时才是一个解决方案。

      简单的实现将涉及 8 个 for 循环和一些 IF(伪代码):

      for num1 in [1,2,3]
        for num2 in [1,2,3]
          for num3 in [1,2,3]
            ...
              if ((num1==1) or (num2==1) or (num3 == 1) ... (num8 == 1)) and ((num1 == 2) or ... or (num8 == 2)) and ((num1 == 3) or ... or (num8 == 3))
                Print Solution!
      

      它也可以递归实现,使用两个数组和几个函数。更好更容易调试/跟踪(伪代码):

      numbers = [1, 2, 3, 4, 5, 6, 7, 8]
      positions = [0, 0, 0, 0, 0, 0, 0, 0]
      
      function HandleNumber(i) {
        for position in [1,2,3] {
          positions[i] = position;
          if (i == LastPosition) {
              // Check if valid solution (it's valid if we got numbers in all groups)
              // and print solution!
            }
          else HandleNumber(i+1)
        }      
      }
      

      第三种实现将不使用递归和一点点回溯。伪代码,再次:

      numbers = [1,2,3,4,5,6,7,8]
      groups = [0,0,0,0,0,0,0,0]
      
      c_pos = 0 // Current position in Numbers array; We're done when we reach -1
      while (cpos != -1) {
        if (groups[c_pos] == 3) {
            // Back-track
            groups[c_pos]=0;
            c_pos=c_pos-1
          }
        else {
           // Try the next group
           groups[c_pos] = groups[c_pos] + 1
           // Advance to next position OR print solution
           if (c_pos == LastPostion) {
               // Check for valid solution (all groups are used) and print solution!
             }
           else
             c_pos = c_pos + 1
          }
      }
      

      【讨论】:

        【解决方案4】:

        以经典方式递归生成所有子集组合。当您达到剩余元素的数量等于空子集的数量时,将自己限制为仅空子集。

        这是一个 Python 实现:

        def combinations(source, n):
          def combinations_helper(source, subsets, p=0, nonempty=0):
            if p == len(source):
              yield subsets[:]
            elif len(source) - p == len(subsets) - nonempty:
              empty = [subset for subset in subsets if not subset]
              for subset in empty:
                subset.append(source[p])
                for combination in combinations_helper(source, subsets, p+1, nonempty+1):
                  yield combination
                subset.pop()
            else:
              for subset in subsets:
                newfilled = not subset
                subset.append(source[p])
                for combination in combinations_helper(source, subsets, p+1, nonempty+newfilled):
                  yield combination
                subset.pop()
        
          assert len(source) >= n, "Not enough items"
          subsets = [[] for _ in xrange(n)]
          for combination in combinations_helper(source, subsets):
            yield combination
        

        还有一个测试:

        >>> for combination in combinations(range(1, 5), 2):
        ...   print ', '.join(map(str, combination))
        ... 
        [1, 2, 3], [4]
        [1, 2, 4], [3]
        [1, 2], [3, 4]
        [1, 3, 4], [2]
        [1, 3], [2, 4]
        [1, 4], [2, 3]
        [1], [2, 3, 4]
        [2, 3, 4], [1]
        [2, 3], [1, 4]
        [2, 4], [1, 3]
        [2], [1, 3, 4]
        [3, 4], [1, 2]
        [3], [1, 2, 4]
        [4], [1, 2, 3]
        >>> len(list(combinations(range(1, 9), 3)))
        5796
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-11-19
          • 1970-01-01
          • 1970-01-01
          • 2016-12-22
          • 2021-04-21
          • 2020-07-18
          相关资源
          最近更新 更多