CLPFD能否实现物品均匀分组?负载均衡求解问询
CLPFD 重量均衡分组解决方案
问题分析
你的代码出现非预期结果的核心原因有两点:
labeling/2调用方式错误:将派生变量TableDiff放入待标记变量列表,CLPFD 的min(Expr)选项需要直接针对影响目标的决策变量(即台面分配列表Tables)生效,而非派生的差异值。balance/3实现冗余:递归遍历的写法既低效又容易引入约束传播问题,完全可以用 CLPFD 内置的求和约束替代。
优化后的实现方案
以下是修正后的代码,同时优化了约束逻辑,确保优先找到负载差异最小的分组:
% 定义重量数据 weight(10). weight(10). weight(10). weight(10). go(Pairs) :- % 提取重量数值列表,而非封装后的weight(Kg)结构 findall(Kg, weight(Kg), KgList), length(KgList, N), % 生成对应每个重量的台面分配变量(1或2) length(Tables, N), Tables ins 1..2, % 计算总重量 sum(KgList, #=, Total), % 计算台面1的总负载:遍历每个重量,若分配到1则累加对应Kg T1 #= sum( [ Kg * (T #= 1) || Kg, T <- zip(KgList, Tables) ] ), % 台面2的总负载 = 总重 - 台面1负载 T2 #= Total - T1, % 定义负载差异目标 TableDiff #= abs(T1 - T2), % 核心:优先最小化TableDiff,再标记分配变量 labeling([min(TableDiff)], Tables), % 将重量与分配结果组合成预期的Pair格式 pairs_keys_values(Pairs, [weight(Kg) || Kg <- KgList], Tables). % 辅助谓词:将两个列表按位置配对 zip([], [], []). zip([X|Xs], [Y|Ys], [X-Y|Zs]) :- zip(Xs, Ys, Zs).
关键改进点说明
- 简化负载计算:用
sum/3和逻辑表达式(T #= 1)直接计算台面1的总负载,替代递归的balance/3,约束传播更高效。 - 修正标记逻辑:
labeling/2仅针对决策变量Tables,通过min(TableDiff)选项告知CLPFD优先探索使差异最小的分配方案。 - 数据结构优化:先提取纯数值的重量列表,减少不必要的结构封装,提升代码可读性和运行效率。
测试验证
对于4个10Kg的测试用例,运行 go(P) 会返回预期结果:
P = [weight(10)-1, weight(10)-1, weight(10)-2, weight(10)-2]
对于无法完全均衡的场景(如10,9,8,7),代码会优先返回负载差异为0的分组(10+7=17,9+8=17),符合需求。
额外优化建议
如果重量数据量较大,可以在 labeling/2 中添加 ff(首失败)策略,进一步提升搜索效率:
labeling([min(TableDiff), ff], Tables)
内容的提问来源于stack exchange,提问作者TessellatingHeckler
相关产品推荐
相关产品推荐

