与this question相关,我想知道算法(以及java / c / c ++ / python /等中的实际代码,如果你有!)来生成r
元素的所有组合总共包含m
个元素的列表。其中一些m
元素可能会重复出现。
谢谢!
答案 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
。在这种情况下,aba
和aab
是重复的组合,abb
和bab
也是如此,并且使用哈希表不会为您删除这些重复项。但是,您可以为每个组合创建一个位掩码,并将哈希表构思与位掩码一起使用。这会稍微复杂一些,但并非如此。
如果您首先对初始数组进行排序,以便重复的项目相邻,那么问题就会消失,您可以使用哈希表的想法。
毫无疑问,有一种方法可以修改代码以防止它生成重复项。我可以看到一种可能的方法,但它会变得杂乱而昂贵。如果您只使用哈希表的想法,它可能会使算法变慢。我会采取的方法:
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
,则返回该组合,否则返回空集。 (+c
是Plus[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}}。