给出一个集合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 讨论了解决它的方法没有重新排列。
我打算尝试模拟退火。所以我想知道,如果有更好的方法吗?
提前致谢。
答案 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到底意味着什么。当然它是---你要么将一个项目从一个集合移动到另一个集合,要么交换来自两个不同集合的项目,这是一个简单的邻域。我会在随机搜索中使用这两种操作(实际上可能是禁忌搜索或模拟退火。)