假设您有一个整数范围S1,...,Sn
的子集R={1,2,...,N}
和一个整数k
的列表。是否有一种有效的方法来查找C
大小为R
的子集k
,以便C
是Si
的最大数量的子集?< / p>
例如,请R={1,2,3,4}
和k=2
S1={1,2,3}
S2={1,2,3}
S3={1,2,4}
S4={1,3,4}
然后我想要返回C={1,2}
或C={1,3}
(并不重要)。
答案 0 :(得分:2)
我认为你的问题是NP-Hard。考虑二分图,左边的节点是你的集合,右边的节点是整数{1, ..., N}
,如果集合包含整数,则在两个节点之间有一条边。然后,找到大小为k
的公共子集,它是Si
的最大数量的子集,相当于找到具有最大边数{{}的完整二分子图K(i, k)
1}}。如果您可以在多项式时间内执行此操作,则可以通过尝试每个固定的i*k
找到在多项式时间内具有最大边数K(i, j)
的完整二分子图i*j
。但NP-Complete中的这个问题(Complete bipartite graph)。
因此,除非P = NP,否则您的问题没有多项式时间算法。
答案 1 :(得分:1)
假设我理解你的问题,我相信对于相当小的集合来说这是直截了当的。
我将使用 Mathematica 代码进行说明,但这个概念是通用的。
我从{1 .. 8}集中生成长度为10
的{{1}}个随机子集:
4
ss = Subsets[Range@8, {4}] ~RandomSample~ 10
我将这些转换为每个子集中每个数字存在的二进制数组:
{{1, 3, 4, 6}, {2, 6, 7, 8}, {3, 5, 6, 7}, {2, 4, 6, 7}, {1, 4, 5, 8},
{2, 4, 6, 8}, {1, 2, 3, 8}, {1, 6, 7, 8}, {1, 2, 4, 7}, {1, 2, 5, 7}}
这是10个子集的十列,以及元素{1 .. 8}的8行。
现在生成所有可能的目标子集(大小a = Normal@SparseArray[Join @@ MapIndexed[Tuples[{##}] &, ss] -> 1];
Grid[a]
):
3
获取“密钥”并从数组中提取这些行并执行BitAnd操作(如果所有列等于keys = Subsets[Union @@ ss, {3}];
,则返回1
),然后计算1的数量。例如,对于密钥1
,我们有:
{1, 6, 8}
BitAnd之后:
为每个键执行此操作:
a[[{1, 6, 8}]]
然后找到该列表的最大元素的位置,并提取counts = Tr[BitAnd @@ a[[#]]] & /@ keys;
的相应部分:
keys
keys ~Extract~ Position[counts, Max@counts]
如果有足够的内存,此过程可以快速处理更大的内存。从50,000个随机选择的长度{{1, 2, 7}, {2, 4, 6}, {2, 4, 7}, {2, 6, 7}, {2, 6, 8}, {6, 7, 8}}
的子集开始,来自{1 .. 30}:
7
长度ss = Subsets[Range@30, {7}] ~RandomSample~ 50000;
的最大子集在大约9秒内计算:
4
AbsoluteTiming[ a = Normal@SparseArray[Join @@ MapIndexed[Tuples[{##}] &, ss] -> 1]; keys = Subsets[Union @@ ss, {4}]; counts = Tr[BitAnd @@ a[[#]]] & /@ keys; keys~Extract~Position[counts, Max@counts] ]
我应该补充一点, Mathematica 是一种高级语言,这些操作都在通用对象上,因此如果真的在二进制级别完成,这应该更快,内存效率更高。 / p>
答案 2 :(得分:1)
我希望我不会误解这个问题......这是SWI-Prolog的解决方案
:- module(subsets, [solve/0]).
:- [library(pairs),
library(aggregate)].
solve :-
problem(R, K, Subsets),
once(subset_of_maximal_number(R, K, Subsets, Subset)),
writeln(Subset).
problem(4, 2,
[[1,2,3], [1,2,3], [1,2,4], [1,3,4]]).
problem(8, 3,
[[1, 3, 4, 6], [2, 6, 7, 8], [3, 5, 6, 7], [2, 4, 6, 7], [1, 4, 5, 8],
[2, 4, 6, 8], [1, 2, 3, 8], [1, 6, 7, 8], [1, 2, 4, 7], [1, 2, 5, 7]]).
subset_of_maximal_number(R, K, Subsets, Subset) :-
flatten(Subsets, Numbers),
findall(Num-Count,
( between(1, R, Num),
aggregate_all(count, member(Num, Numbers), Count)
), NumToCount),
transpose_pairs(NumToCount, CountToNumSortedR),
reverse(CountToNumSortedR, CountToNumSorted),
length(Subset, K), % list of free vars
prefix(SolutionsK, CountToNumSorted),
pairs_values(SolutionsK, Subset).
测试输出:
?- solve.
[1,3]
true ;
[7,6,2]
true.
编辑我认为上述解决方案是错误的,因为返回的内容不能是任何输入的子集:这里(注释)解决方案没有这个问题:
:- module(subsets, [solve/0]).
:- [library(pairs),
library(aggregate),
library(ordsets)].
solve :-
problem(R, K, Subsets),
once(subset_of_maximal_number(R, K, Subsets, Subset)),
writeln(Subset).
problem(4, 2,
[[1,2,3], [1,2,3], [1,2,4], [1,3,4]]).
problem(8, 3,
[[1, 3, 4, 6], [2, 6, 7, 8], [3, 5, 6, 7], [2, 4, 6, 7], [1, 4, 5, 8],
[2, 4, 6, 8], [1, 2, 3, 8], [1, 6, 7, 8], [1, 2, 4, 7], [1, 2, 5, 7]]).
subset_of_maximal_number(R, K, Subsets, Subset) :-
flatten(Subsets, Numbers),
findall(Num-Count,
( between(1, R, Num),
aggregate_all(count, member(Num, Numbers), Count)
), NumToCount),
% actually sort by ascending # of occurrences
transpose_pairs(NumToCount, CountToNumSorted),
pairs_values(CountToNumSorted, PreferredRev),
% we need higher values first
reverse(PreferredRev, Preferred),
% empty slots to fill, preferred first
length(SubsetP, K),
select_k(Preferred, SubsetP),
% verify our selection it's an actual subset of any of subsets
sort(SubsetP, Subset),
once((member(S, Subsets), ord_subtract(Subset, S, []))).
select_k(_Subset, []).
select_k(Subset, [E|R]) :-
select(E, Subset, WithoutE),
select_k(WithoutE, R).
试验:
?- solve.
[1,3]
true ;
[2,6,7]
true.