为重复项目列表生成所有组合

时间:2011-11-16 01:58:46

标签: algorithm combinations

this question相关,我想知道算法(以及java / c / c ++ / python /等中的实际代码,如果你有!)来生成r元素的所有组合总共包含m个元素的列表。其中一些m元素可能会重复出现。

谢谢!

3 个答案:

答案 0 :(得分:2)

递归每种元素类型

int recurseMe(list<list<item>> items, int r, list<item> container)
{
  if (r == container.length)
  {
    //print out your collection;
    return 1;
  }
  else if (container.length > score)
  {
    return 0;
  }
  list<item> firstType = items[0];
  int score = 0;
  for(int i = 0; i < firstType.length; i++)
  {
    score += recurseMe(items without items[0], r, container + i items from firstType);
  }
  return score;
}

这将输入包含项目列表的列表,假设每个内部列表代表唯一类型的项目。您可能必须构建一个排序函数作为输入。

//start with a list<item> original;
list<list<item>> grouped = new list<list<item>>();
list<item> sorted = original.sort();//use whichever method for this
list<item> temp = null;
item current = null;
for(int x = 0; x < original.length; x++)
  if (sorted[x] == current)
  {
    temp.add(current);
  }
  else
  {
    if (temp != null && temp.isNotEmpty)
      grouped.add(temp);
    temp = new list<item>();
    temp.add(sorted[x]);
  }
}
if (temp != null && temp.isNotEmpty)
  grouped.add(temp);
//grouped is the result

对列表进行排序,然后创建包含相同元素的子列表,将它们插入列表列表grouped

答案 1 :(得分:1)

我将把这个作为答案,而不是一堆评论。

我原来的评论是:

  

CombinationGenerator Java类系统地生成所有   n个元素的组合,一次取r。算法是   由Kenneth H. Rosen描述,离散数学及其   Applications,2nd edition(NY:McGraw-Hill,1991),pp.284-286。“见   merriampark.com/comb.htm。它有一个源代码链接。

正如您在评论中指出的那样,您需要独特的组合。因此,给定数组["a", "a", "b", "b"],您希望它生成aab, abb。我链接的代码会生成aab, aab, baa, baa

使用该数组,删除重复项很容易。根据您实现它的方式,您可以让它生成重复项,然后在事后过滤它们(即从数组中选择唯一元素),或者修改代码以包含哈希表,以便在生成组合时,在将项放入输出数组之前检查哈希表。

在哈希表中查找内容是O(1)操作,因此其中任何一个都是高效的。事后这样做会有点贵,因为你必须复制物品。不过,您正在谈论O(n),其中n是生成的组合数。

有一个复杂因素:秩序无关紧要。也就是说,给定数组["a", "b", "a", "b"],代码将生成aba, abb, aab, bab。在这种情况下,abaaab是重复的组合,abbbab也是如此,并且使用哈希表不会为您删除这些重复项。但是,您可以为每个组合创建一个位掩码,并将哈希表构思与位掩码一起使用。这会稍微复杂一些,但并非如此。

如果您首先对初始数组进行排序,以便重复的项目相邻,那么问题就会消失,您可以使用哈希表的想法。

毫无疑问,有一种方法可以修改代码以防止它生成重复项。我可以看到一种可能的方法,但它会变得杂乱而昂贵。如果您只使用哈希表的想法,它可能会使算法变慢。我会采取的方法:

Sort the input array
Use the linked code to generate the combinations
Use a hash table or some other code to select unique items.

虽然......我想到了一个想法。

如果您对输入数组进行排序,那么任何生成的重复数据都是相邻的吗?也就是说,给定输入数组["a", "a", "b", "b"],生成的输出将按此顺序为aab, aab, abb, abb。当然,这将取决于实现。但是,如果在您的实现中确实如此,那么修改算法以删除重复项是一个简单的问题,即检查当前组合是否等于前一个组合。

答案 2 :(得分:1)

这是一个递归,我认为它与Jean-Bernard Pellerin的算法密切相关,在 Mathematica 中。

这将输入作为每种元素的数量。输出的形式类似。例如:

{a,a,b,b,c,d,d,d,d} -> {2,2,1,4}

功能:

f[k_, {}, c__] := If[+c == k, {{c}}, {}]

f[k_, {x_, r___}, c___] := Join @@ (f[k, {r}, c, #] & /@ 0~Range~Min[x, k - +c])

使用:

f[4, {2, 2, 1, 4}]
{{0, 0, 0, 4}, {0, 0, 1, 3}, {0, 1, 0, 3}, {0, 1, 1, 2}, {0, 2, 0, 2},
 {0, 2, 1, 1}, {1, 0, 0, 3}, {1, 0, 1, 2}, {1, 1, 0, 2}, {1, 1, 1, 1},
 {1, 2, 0, 1}, {1, 2, 1, 0}, {2, 0, 0, 2}, {2, 0, 1, 1}, {2, 1, 0, 1},
 {2, 1, 1, 0}, {2, 2, 0, 0}}

要求解释此代码。它是一个递归函数,它接受可变数量的参数。第一个参数是k,子集的长度。第二个是可供选择的每种类型的计数列表。第三个参数及其后的参数在函数内部用于在构造时保存子集(组合)。

因此,当选择集中没有其他项目时,将使用此定义:

f[k_, {}, c__] := If[+c == k, {{c}}, {}]

如果组合的总值(其长度)等于k,则返回该组合,否则返回空集。 (+cPlus[c]

的简写

否则:

f[k_, {x_, r___}, c___] := Join @@ (f[k, {r}, c, #] & /@ 0~Range~Min[x, k - +c])

从左到右阅读:

  • Join用于展平嵌套列表的级别,因此结果不是越来越深的张量。

  • f[k, {r}, c, #] &调用该函数,删除选择集的第一个位置(x),并向组合中添加新元素(#)。

  • /@ 0 ~Range~ Min[x, k - +c])表示选择集中第一个元素的零和较小值之间的每个整数,k组合总数减去k,这是可以在不超出组合的情况下选择的最大值大小{{1}}。