如何使用SAT4J DependencyHelper遍历所有最优解或按最优性迭代全部解
问题原因
你原有方案只能返回部分解,是SAT4J优化求解器的默认行为导致的:
第一次调用hasASolution()得到最优解后,求解器内部会自动添加约束,要求后续解的目标函数值严格小于当前已得到的最优值,用于寻找更优解。你示例中已经得到全局最优值-2,不存在比它更小的合法解,因此添加阻塞子句排除当前解后,第二次调用hasASolution()时求解器直接判定无符合条件的解,不会去查找成本同样为-2的其他最优解。
解决方案1:获取所有最优解
该方案使用你提到的forceObjectiveValueTo()方法实现,全程保留DependencyHelper的解释支持能力,适配你的Eclipse依赖解析场景:
import org.sat4j.core.Vec; import org.sat4j.pb.IPBSolver; import org.sat4j.pb.SolverFactory; import org.sat4j.pb.tools.DependencyHelper; import org.sat4j.pb.tools.StringNegator; import org.sat4j.specs.ContradictionException; import org.sat4j.specs.TimeoutException; import java.math.BigInteger; public class Optimize { public static void main(String[] args) { IPBSolver solver = SolverFactory.newDefaultOptimizer(); DependencyHelper<String, String> helper = new DependencyHelper<>(solver); helper.setNegator(StringNegator.INSTANCE); try { helper.implication("A").implies("B", "C").named("A => B || C"); } catch (ContradictionException e) { e.printStackTrace(); } helper.addToObjectiveFunction("A", -3); helper.addToObjectiveFunction("B", 1); helper.addToObjectiveFunction("C", 1); try { // 第一步:首次求解获取全局最优成本 if (helper.hasASolution()) { BigInteger optimalCost = helper.getSolutionCost(); System.out.println("最优成本:" + optimalCost); // 第二步:固定目标值为最优成本,后续仅查找成本等于该值的解 helper.getProblem().forceObjectiveValueTo(optimalCost); // 第三步:遍历所有最优解 int solutionCount = 0; do { solutionCount++; System.out.printf("第%d个最优解:%s%n", solutionCount, helper.getSolution()); // 阻塞当前解,避免重复返回 helper.discardCurrentSolution(); } while (helper.hasASolution()); System.out.println("共找到" + solutionCount + "个最优解"); } } catch (TimeoutException | ContradictionException e) { e.printStackTrace(); } } }
运行后会输出预期的两个最优解A,C和A,B。
解决方案2:按最优性从高到低遍历全部解
如果需要输出所有合法解,按成本升序排列,只需要在上述方案基础上增加成本迭代逻辑:每次遍历完当前成本的所有解后,添加约束要求后续解成本严格大于当前成本,重复执行直到无合法解即可。
内容的提问来源于stack exchange,提问作者CWalther
相关产品推荐
相关产品推荐

