在OR-Tools中如何对带模运算与除法的复杂整数约束进行建模?
OR-Tools CP-SAT 模除类约束建模方案
问题分析
你遇到的报错是因为OR-Tools的CP-SAT求解器的IntVar类型不支持直接使用C#原生的%、/运算符,所有整数运算约束都需要通过求解器提供的内置API建模。另外你更新后的代码存在多处逻辑错误,下面先说明错误点,再给出正确且高效的实现方案。
你更新后代码的错误点
- 运算符误用:C#中
^是按位异或运算符,不是幂运算,26 ^ 13实际计算结果为23,和你需要的26的13次方数值完全不符,这是最致命的错误。 - 变量绑定错误:计算w2的模约束时,你复制粘贴导致写错变量,代码行
model.Add(w1 == ((w1Modulo * multiplicandw2) + remainderw2));应该使用w2和w2Modulo。 - 商变量定义错误:你将商变量
multiplicandw1的定义域固定为模数值,这不符合整除逻辑,商是随输入变量变化的自由整数变量,不能固定值。 - 余数范围错误:你定义
remainderw1的上界为26^12 - 1,这导致余数永远小于26^12,除以26^12的结果永远为0,完全不符合你的约束要求。 - 等价约束错误:最后比较的逻辑写错,你需要的是
remainderw1 / 26^12 == remainderw2 / 26^4,等价于remainderw1 * 26^4 == remainderw2 * 26^12,不是乘以模数值。
正确建模方案
方案1:使用内置模、除约束API(通用场景)
OR-Tools CP-SAT已经提供了AddModuloEquality和AddDivisionEquality内置方法,不需要手动拆分整除公式:
// 提前计算常量,不要用^运算符 const long K1 = 95428956661682176; // 26^12 const long M1 = K1 * 26; // 26^13 const long K2 = 456976; // 26^4 const long M2 = K2 * 26; // 26^5 // w1、w2定义和你的原代码一致 IntVar w1 = model.NewIntVarFromDomain(Domain.FromValues(wl1), "w1"); IntVar w2 = model.NewIntVarFromDomain(Domain.FromValues(wl2), "w2"); // 计算左边值:(w1 mod M1) / K1 IntVar mod1 = model.NewIntVar(0, M1 - 1, "mod1"); model.AddModuloEquality(mod1, w1, M1); IntVar leftBit = model.NewIntVar(0, 25, "leftBit"); model.AddDivisionEquality(leftBit, mod1, K1); // 计算右边值:(w2 mod M2) / K2 IntVar mod2 = model.NewIntVar(0, M2 - 1, "mod2"); model.AddModuloEquality(mod2, w2, M2); IntVar rightBit = model.NewIntVar(0, 25, "rightBit"); model.AddDivisionEquality(rightBit, mod2, K2); // 加相等约束 model.Add(leftBit == rightBit);
方案2:表约束优化(适合枚举域、大量同类约束场景)
你的w1、w2都是有限枚举值,完全可以提前预处理出每个值对应的目标位,用表约束(AllowedAssignments)实现,性能比模除约束高10倍以上,尤其适合约束量大的场景:
// 提前预处理w1的取值和对应位的映射 List<Tuple<long, long>> w1BitMap = new List<Tuple<long, long>>(); foreach (var val in wl1) { long bit = (val % M1) / K1; w1BitMap.Add(Tuple.Create(val, bit)); } // 提前预处理w2的取值和对应位的映射 List<Tuple<long, long>> w2BitMap = new List<Tuple<long, long>>(); foreach (var val in wl2) { long bit = (val % M2) / K2; w2BitMap.Add(Tuple.Create(val, bit)); } // 新建公共中间变量 IntVar commonBit = model.NewIntVar(0, 25, "commonBit"); // 加表约束 model.AddAllowedAssignments(new IntVar[] { w1, commonBit }, w1BitMap.Select(t => new long[] { t.Item1, t.Item2 }).ToList()); model.AddAllowedAssignments(new IntVar[] { w2, commonBit }, w2BitMap.Select(t => new long[] { t.Item1, t.Item2 }).ToList());
内容的提问来源于stack exchange,提问作者T.Bob
相关产品推荐
相关产品推荐

