替代CS IntVar []。Element(IntExpr index)的ORTools SAT,以进行相互分配

时间:2019-05-23 02:55:06

标签: c# or-tools sat

如果IntVar []中存在“互惠”分配,我正在尝试分配成本。这是我在较旧的ConstraintSolver中完成此操作的方式...

IntVar[] assignments = solver.MakeIntVarArray(size, 0, size-1, "assignments");
var cost = Enumerable.Range(0, size)
    .Select(i => 999 * (assignments.Element(assignments[i]) == i))
    .ToArray()
    .ToSum()
    .Var();
var objective = cost.Minimize(1);

现在,我尝试在CpSolver扩展名不存在的Google.OrTools.Sat中使用较新的.Element(我认为是有充分理由的)。我已经设法使用四个IntVars []使它“工作”,但我怀疑这只是我的一次重大建模失败。

foreach (var i in Enumerable.Range(0, size))
{
    model.AddElement(assignments[i], assignments, reciprocals[i]);
    model.Add(reciprocals[i] == i).OnlyEnforceIf(reciprocalBools[i]);
    model.Add(reciprocals[i] != i).OnlyEnforceIf(reciprocalBools[i].Not());
    model.Add(costs[i] == 999).OnlyEnforceIf(reciprocalBools[i]);
    model.Add(costs[i] == 0).OnlyEnforceIf(reciprocalBools[i].Not());
}
model.Minimize(costs.Sum());

根据我的测试,上述内容在功能上似乎是正确的,但是随着size的变大,我的测试应用程序的SAT版本比CS版本差几个数量级。 任何建议将不胜感激。

1 个答案:

答案 0 :(得分:1)

这是一个更好的版本

foreach (var i in Enumerable.Range(0, size))
{
    model.AddElement(assignments[i], assignments, reciprocals[i]);
    model.Add(reciprocals[i] == i).OnlyEnforceIf(reciprocalBools[i]);
    model.Add(reciprocals[i] != i).OnlyEnforceIf(reciprocalBools[i].Not());
}
model.Minimize(999 * LinearExpr.Sum(reciprocalBools));

我们还有一个逆约束(model.AddInverseConstraint(x_array, y_array)) 强制

x_array[i] == j <=> y_array[j] == i

我还是想知道您是否需要所有这些东西。

如果xi = {xi_1, .., xi_n}(映射到布尔变量数组) reciprocalBools[i] is true,如果exists j, such that (xi_j && xj_i) is true

因此,您只需要计算成对的(xi_j && xj_i)对。

这并不简单。

给定i和j,i!= j

Literal implied = model.newBoolVar("");
model.addBoolOr(new Literal[] {xi_j.not(), xj_i.not(), implied});
model.addImplication(implied, xi_j);
model.addImplication(implied, xj_i);

现在,您有implied <=> xi_j && xj_i。您可以计算这些implied变量。

如果i == j,则不要创建implied变量,不要添加3布尔约束,并直接使用xi_i。