MiniZinc 中的基数约束

Axe*_*per 5 constraint-programming minizinc gecode

MiniZinc约束求解器允许表达基数约束很容易使用内置的sum()功能:

%  This predicate is true, iff 2 of the array
%  elements are true
predicate exactly_two_sum(array[int] of var bool: x) =
    (sum(x) == 2);
Run Code Online (Sandbox Code Playgroud)

满足基数约束,当且仅当布尔变量数组中的真实元素的数量符合指定。布尔值自动映射到整数值01计算总和。

我将自己的基数约束谓词实现为一组计数器切片:

%  This predicate is true, iff 2 of the array
%  elements are true
predicate exactly_two_serial(array[int] of var bool: x) =
    let 
    {
      int: lb = min(index_set(x));
      int: ub = max(index_set(x));
      int: len = length(x);
    }
    in
    if len < 2 then
      false
    else if len == 2 then
      x[lb] /\ x[ub]
    else
      (
        let 
        {
          %  1-of-3 counter is modelled as a set of slices
          %  with 3 outputs each
          array[lb+1..ub-1] of var bool: t0;
          array[lb+1..ub-1] of var bool: t1;
          array[lb+1..ub-1] of var bool: t2;
        }
        in
        %  first two slices are hard-coded
        (t0[lb+1] == not(x[lb] \/ x[lb+1])) /\
        (t1[lb+1] == (x[lb] != x[lb+1])) /\
        (t2[lb+1] == (x[lb] /\ x[lb+1])) /\
        %   remaining slices are regular
        forall(i in lb+2..ub-1)
        (
          (t0[i] == t0[i-1] /\ not x[i]) /\
          (t1[i] == (t0[i-1] /\ x[i]) \/ (t1[i-1] /\ not x[i])) /\
          (t2[i] == (t1[i-1] /\ x[i]) \/ (t2[i-1] /\ not x[i])) 
        ) /\
        %  output 2 of final slice must be true to fulfil predicate
        ((t1[ub-1] /\ x[ub]) \/ (t2[ub-1] /\ not x[ub]))
      )
    endif endif;
Run Code Online (Sandbox Code Playgroud)

此实现使用并行编码,切片之间的行/变量较少:

%  This predicate is true, iff 2 of the array
%  elements are true
predicate exactly_two_parallel(array[int] of var bool: x) =
    let 
    {
      int: lb = min(index_set(x));
      int: ub = max(index_set(x));
      int: len = length(x);
    }
    in
    if len < 2 then
      false
    else if len == 2 then
      x[lb] /\ x[ub]
    else
      (
        let 
        {
          %  counter is modelled as a set of slices
          %  with 2 outputs each
          %  Encoding:
          %  0 0 : 0 x true
          %  0 1 : 1 x true
          %  1 0 : 2 x true
          %  1 1 : more than 2 x true
          array[lb+1..ub] of var bool: t0;
          array[lb+1..ub] of var bool: t1;
        }
        in
        %  first two slices are hard-coded
        (t1[lb+1] == (x[lb] /\ x[lb+1])) /\
        (t0[lb+1] == not t1[lb+1]) /\
        %   remaining slices are regular
        forall(i in lb+2..ub)
        (
          (t0[i] == (t0[i-1] != x[i]) \/ (t0[i-1] /\ t1[i-1])) /\
          (t1[i] == t1[i-1] \/ (t0[i-1] /\ x[i])) 
        ) /\
        %  output of final slice must be  1 0 to fulfil predicate
        (t1[ub] /\ not t0[ub])
      )
    endif endif;
Run Code Online (Sandbox Code Playgroud)

题:

使用自有基数谓词有意义吗?还是 MiniZinc 的实现sum()在求解速度方面无可置疑?

更新:
我使用Gecode作为求解器后端。

Zay*_*enz 5

线性和通常是在约束求解器中很好地实现的最重要的约束之一,因此对于您的情况,使用简单和的初始版本要好得多。特别是,Gecode 中实现布尔和的传播器进行了大量优化,以尽可能提高效率。

作为一般规则,使用可用的约束通常是一个好主意。特别是,如果一个人正在做的事情很好地映射到全局约束,这通常是一个好主意。一个相关的例子是,如果您想计算整数数组中几个不同数字的出现次数,在这种情况下,全局基数约束非常有用。

为了完整性:当使用惰性子句生成求解器(例如 Chuffed)时,(新颖的)分解有时可能会非常有用。但这是一个更高级的话题。

  • @AxelKemper MiniZinc 使用自动强制。这意味着如果谓词或函数需要,布尔值可用作整数,整数可用作浮点数。在您的情况下,当谓词仅包含布尔参数时,我们通常相信编译器会将约束简化回布尔逻辑形式。请注意,在 Gecode 中,由于专门的传播器,全局约束可能会表现得更好,而构造的谓词将由(大量)传播器组成。 (3认同)