我有一个组合问题,可以使用笛卡尔无效率地解决 多套产品。具体来说,我有多个项目和多个元素 满足每个项目。问题在于找到所有可能的元素组合 满足所有项目。例如:
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
。
解决此问题的另一种方法是枚举所有元素,跳过 与之前生成的其他组合等效的组合 跳过重复的元素。这有点容易计算并且可以实现 作为一次返回组合的迭代器,但我不知道是否存在 解决这个问题的更好方法,或者之前是否研究过这个问题。
你会如何解决这个问题?
答案 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"]]