将集合划分为k个不相交子集

时间:2011-03-28 11:38:12

标签: algorithm set sum subset data-partitioning

给出一个集合S,将集合划分为k个不相交的子集,使其总和的差异最小。

说,S = {1,2,3,4,5}k = 2,所以{ {3,4}, {1,2,5} },因为他们的总和{7,8}差异很小。对于S = {1,2,3}, k = 2,它将为{{1,2},{3}},因为总和的差异为0

问题类似于算法设计手册中的分区问题。除了 Steven Skiena 讨论了解决它的方法没有重新排列。

我打算尝试模拟退火。所以我想知道,如果有更好的方法吗?

提前致谢。

2 个答案:

答案 0 :(得分:3)

背包的伪多边形算法可用于k=2。我们能做的最好的是sum(S)/ 2。运行背包算法

for s in S:
    for i in 0 to sum(S):
        if arr[i] then arr[i+s] = true;

然后查看sum(S)/ 2,然后是sum(S)/ 2 +/- 1等。

对于'k> = 3'我相信这是NP完全的,就像3分区问题一样。

为k> = 3执行此操作的最简单方法就是强制它,这是一种方式,不确定它是最快还是最干净。

import copy
arr = [1,2,3,4]

def t(k,accum,index):
    print accum,k
    if index == len(arr):
        if(k==0):
            return copy.deepcopy(accum);
        else:
            return [];

    element = arr[index];
    result = []

    for set_i in range(len(accum)):
        if k>0:
            clone_new = copy.deepcopy(accum);
            clone_new[set_i].append([element]);
            result.extend( t(k-1,clone_new,index+1) );

        for elem_i in range(len(accum[set_i])):
            clone_new = copy.deepcopy(accum);
            clone_new[set_i][elem_i].append(element)
            result.extend( t(k,clone_new,index+1) );

    return result

print t(3,[[]],0);

模拟退火可能是好的,但由于特定解决方案的“邻居”并不是很清楚,遗传算法可能更适合这一点。你可以通过随机选择一组子集开始,然后通过在子集之间移动数字来“变异”。

答案 1 :(得分:0)

如果集很大,我肯定会去随机搜索。在写“邻域没有明确定义”时,不知道spin_plate到底意味着什么。当然它是---你要么将一个项目从一个集合移动到另一个集合,要么交换来自两个不同集合的项目,这是一个简单的邻域。我会在随机搜索中使用这两种操作(实际上可能是禁忌搜索或模拟退火。)