约束规划建模技巧总结---中间变量和全局约束

一直以来都在关注CP(Constraint Programming),感觉它是一个特别实用的优化工具,虽然在最优解方面不是那么令人满意,但是大规模问题下的短时间内得到质量较好的可行解才是我们将OR落到企业中的的关键点。结合2年来的CP建模经验和最近在看的or-tools感受,总结下CP 建模的一些tricks,我将采用or-tools 中的案例来阐释:

1、尽量引入中间变量保存expression的值,且保持1对1的关系,这样有助于约束传播。code1 为最初版的代码,code2 为改进后的代码。

code1:

IntVar* diff1;
IntVar* diff2;
int k, l, next_k, next_l;
for (int i = 1; i < n - 1; ++i) {
   for (int j = i + 1; j <= n; ++j) {
        k = i;     
        l = j;   
        diff1 = s.MakeDifference(X[j], X[i])->Var();
        diff1->SetMin(1);
        while (next_interval(n, k, l, &next_k, &next_l)) {
        diff2 = s.MakeDifference(X[next_l], X[next_k])->Var();
        diff2->SetMin(1);
        s.AddConstraint(s.MakeNonEquality(diff1, diff2));
        k = next_k;
        l = next_l;
        }
    }
}
改进后的code2:

for (int i = 1; i < n; ++i) {
    for (int j = i + 1; j <= n; ++j) {
         Y[i][j] = s.MakeDifference(X[j], X[i])->Var();
         Y[i][j]->SetMin(1);
    }
}
int k, l, next_k, next_l;
for (int i = 1; i < n - 1; ++i) {
    for (int j = i + 1; j <= n; ++j) {
        k = i; l = j;
        while (next_interval(n, k, l, &next_k, &next_l)) {
        s.AddConstraint(s.MakeNonEquality(Y[i][j],Y[next_k][next_l]));
        k = next_k;
        l = next_l;
        }
     }
}
(如此采用 Y[i][j]能大幅度提高求解性能)

2、建模时尽量使用全局约束(global constraint)。全局约束有更有助于约束传递和域收敛。我们对code1再次改进为code3.code1 中的s.MakeNonEquality为两两的局部比较,code3中s.MakeAllDifferent(Y)则为全局的比较。

code3:

std::vector Y;
for (int i = 1; i <= n; ++i) {
    for (int j = i + 1; j <= n; ++j) {
          IntVar* const diff = s.MakeDifference(X[j], X[i])->Var();
          Y.push_back(diff);
          diff->SetMin(1);
     }
}
s.AddConstraint(s.MakeAllDifferent(Y));

总结:

1、中间变量记录expression的值的用法能大幅度提高模型求解效率,这种技巧不仅仅在CP 中有体现,笔者在采用LocalSolver建模时也深有体会。

2、ILOG-CP 中Scheduler包也是非常推崇全局约束的使用,例如noOverlap()等。

你可能感兴趣的:(or-tools,ILOG,OR相关)