计算组合/笛卡尔积的集合(没有重复和没有订单限制)

时间:2013-04-24 15:26:33

标签: algorithm combinations enumeration cartesian-product

我有一个组合问题,可以使用笛卡尔无效率地解决 多套产品。具体来说,我有多个项目和多个元素 满足每个项目。问题在于找到所有可能的元素组合 满足所有项目。例如:

items -> elements
------------------------
1 -> {a,b}     // a and b cover the item 1
2 -> {a,b}     // a and b cover the item 2
3 -> {a,b,c}   // a, b and c cover the item 3
4 -> {a,b,e,f} // a, b, e, f cover the item 4

Alternative representation:
element -> items covered
------------------------
a -> {1,2,3,4}
b -> {1,2,3,4}
c -> {3}
e -> {4}
f -> {4}

目标是找到涵盖项目1,2,3,4的所有组合。 有效的解决方案是:

{a},{a,b},{a,c},{a,e},{a,f},{a,b,c},{a,b,e},{a,b,f},{a,b,c,e},{a,b,c,f}
{b},{b,c},{b,e},{b,f},{b,c,e},{b,c,f}

请注意,订单并不重要,因此{a,b} = {b,a} ({a,b} x {c,d} = {c,d} x {a,b})。 另请注意,{a,a,a,a}, {a,a,a,b}...是多余的组合。

如您所见,此问题类似于宇宙所在的set cover problem 此示例的元素是项U={1,2,3,4},U中的子集集合为S={ab={1,2,3,4},c={3},ef{4}},其中set {1,2,3,4}是元素a和{所涵盖的项集合{1}},b{3}所涵盖的元素集,而c是元素{4}e所涵盖的元素集。但是,这里的目标是找不到 涵盖f中所有元素的S集合的最小组合,但查找涵盖所有项目U的所有元素组合{a,b,c,e,f}

可以通过在两者之间执行笛卡尔积来实现 设置1,2,3和4,然后过滤冗余的组合。然而, 这种方法效率很低。假设我有这种情况:

{1,2,3,4}

六组之间的笛卡尔积将产生1 -> {a,b,c,d,e,f,g,h} 2 -> {a,b,c,d,e,f,g,h} 3 -> {a,b,c,d,e,f,g,h} 4 -> {a,b,c,d,e,f,g,h} 5 -> {a,b,c,d,e,f,g,h} 6 -> {a,b,c,d,e,f,g,h,i} 组合, 当实际上有更少的组合时,它们是:8^5*9=294912

解决此问题的另一种方法是枚举所有元素,跳过 与之前生成的其他组合等效的组合 跳过重复的元素。这有点容易计算并且可以实现 作为一次返回组合的迭代器,但我不知道是否存在 解决这个问题的更好方法,或者之前是否研究过这个问题。

你会如何解决这个问题?

2 个答案:

答案 0 :(得分:2)

首先,要意识到如果一组元素不满足所有项目,那么它的任何子集都不会。

其次,要意识到如果一个集合满足所有项目,那么它的所有超集都是如此。

现在,您所要做的就是:

设S是所有元素的集合。 设R为空集。

定义一个函数find(s,r),它执行:

If r includes s, return r.
If s does not satisfy all items, return r.

Otherwise add s to r.
For every item I in  s,
     let s' be s-I
     let s be f(s', r)

return s.

只需拨打find(S,R)即可获得答案。

此方法执行一些重复测试,但每当识别出它时总是会杀死一个分支。这导致对大量元素进行大量修剪。

查询r是否包含一组特定元素以及检查s是否满足所有项目都可以非常快速地进行,但会牺牲额外的内存。

答案 1 :(得分:1)

如果你这样做会怎么样:

1 -> {a,b}
2 -> {b,c}
3 -> {a,b,c}
4 -> {a,e,f}

=>

a -> [1,3,4]
b -> [1,2,3]
c -> [2,3]
e -> [4]
f -> [4]

然后枚举提供(至少)[1,2,3,4]

的左侧组合
For each item in the set of all-satisfying sets, enumerate combinations 
with other items.

All-Satisfying-Sets: {{a,b},{b,e},{b,f}}
Combinations within All-Satisfiying-Sets: {{a,b,e},{a,b,f},{b,e,f},{a,b,e,f}}
Others: {c}
Combinations with Others: {{a,b,c},{b,e,c},{b,f,c}
                          ,{a,b,e,c},{a,b,f,c},{b,e,f,c},{a,b,e,f,c}}

或者你可以在Haskell中做到这一点:

import Data.List (union, subsequences, sort)

example1 = [(["a"],[1,2,3,4])
           ,(["b"],[1,2,3,4])
           ,(["c"],[3])
           ,(["e"],[4])
           ,(["f"],[4])]

example2 = [(["a"],[1,2,3,4,5,6])
           ,(["b"],[1,2,3,4,5,6])
           ,(["c"],[1,2,3,4,5,6])
           ,(["e"],[1,2,3,4,5,6])
           ,(["f"],[1,2,3,4,5,6])
           ,(["g"],[1,2,3,4,5,6])
           ,(["h"],[1,2,3,4,5,6])
           ,(["i"],[6])]

combs items list = 
  let unify (a,b) (a',b') = (sort (a ++ a'), sort (union b b')) 
  in map fst
     . filter ((==items) . snd) 
     . map (foldr unify ([],[])) 
     . subsequences 
     $ list


OUTPUT:

*Main> combs [1..4] example1
[["a"],["b"],["a","b"],["a","c"],["b","c"],["a","b","c"],["a","e"],["b","e"],
["a","b","e"],["a","c","e"],["b","c","e"],["a","b","c","e"],["a","f"],["b","f"], 
["a","b","f"],["a","c","f"],["b","c","f"],["a","b","c","f"],["a","e","f"],
["b","e","f"],["a","b","e","f"],["a","c","e","f"],["b","c","e","f"],
["a","b","c","e","f"]]