循环遍历不同的唯一排列集

时间:2010-12-31 05:50:44

标签: algorithm loops permutation

我很难开始为这个问题布局代码。

我有固定数量的随机数,在这种情况下是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}

NOR

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

R2 [] = {7,3}

R3 [] = {1,4}

什么是好方法?

4 个答案:

答案 0 :(得分:3)

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

首先,由于George Hutchinson,他详细列出了算法H,字典顺序中的限制增长字符串 。它看起来很简单,但我现在不打算深入研究它。

在下一页的详细说明设置分区的格雷码下,他思考:

  

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

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

简单的解决方案(并且肯定是正确的)是对分区上的算法H过滤进行编码,其中m==3并且没有任何分区是空集(根据您声明的约束)。我怀疑算法H运行速度非常快,因此过滤成本不会很高。

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

如果你在小于8051和毫秒的事情上实现这一点,你可以使用一个独特的元素(三个级别的简单嵌套循环)为三个分区中的每个分区播种,然后通过对剩余的五个分区进行分区来扩充使用Ruskey算法的m==3元素。您不必过滤任何东西,但您必须跟踪要分区的五个元素。

从一般算法中滤除的好处是你不必验证自己的任何聪明,并且稍后你会改变主意你的约束,而不必修改你的聪明。

我甚至可能会在以后解决问题,但现在一切都是这样。

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

似乎可以在http://www.cs.ubc.ca/~kisynski/code/bell/

上找到

答案 1 :(得分:1)

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

确定总和为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,6,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)重复第二个数字(一个)的过程,即“2选择1”所以在上面列表中左边两位数的位置,我们选择两个中的一个,然后在最后我们选择剩下的一个。

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

答案 2 :(得分:1)

将问题解决了,您将找到一个直截了当的解决方案。你有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)
  }      
}

第三个实现将不使用递归和一点回溯。 Pseudocode,再次:

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

答案 3 :(得分:0)

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

这是一个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