Minizinc:计算与初始输出数组关联的数组中不同变量的数量

问题描述

我正在尝试计算数组中的出现次数,这些次数受到首先在数组中具有相同值然后通过找到关联数组的不同变量的数量约束。

我从这个模型开始

set of int: OPTS = 1..4;
set of int: ASSIGN = 1..12;

array[ASSIGN] of var OPTS: result;

可以生成结果[ 1,2,1,3,4,4 ]

并希望结合这些陈述

enum CONDS = {A,B,C,D};
array[ASSIGN] of CONDS : const = [A,A,D,D];

array[OPTS] of var 0..max(CONDS): result2;

output ["\(result) \(result2)"]

并产生一个输出,计算每个 CONDS 出现多少个不同的 OPTS

[ 1,4 ] [3,2]

暗示OPTS[1]至少与A、C和D之一相关联,OPTS[2]有As和Ds,OPTS[3]有Bs和Cs,OPTS[4]有Cs和Ds。我已经用这个和更早的问题打乱了我的大脑,无法确定是考虑创建一个不太复杂的约束,还是一个更复杂的约束。我可能在这里错过了一个简单的技巧……或者它可能不那么明显?

我很想知道为什么当 X = j[0,0]X = 1 和 '[0,0]' 当 X = i 和 '[0,0]' }}(这对我来说是东方意义,但显然我不知道这个位置“X”应该如何与其他位置一起工作)。

constraint forall(i in OPTS,j in CONDS)(
   result2[i] = 
     count([ k == j /\ result[k] = i | k in const],X)
);

解决方法

这是一个可以解决您最初问题的模型。构造card({const[i] | i in ASSIGN where result[i]=o} ),使用set推导式{....}计算OPTS的不同值的数量,使用card(集合的基数)进行计数.

include "globals.mzn";

set of int: OPTS = 1..4;
set of int: ASSIGN = 1..12;

array[ASSIGN] of var OPTS: result = [1,2,1,3,4,4];

enum CONDS = {A,B,C,D};
array[ASSIGN] of CONDS : const = [A,A,D,D];

array[OPTS] of var 0..max(ASSIGN): result2;

solve satisfy;

constraint 
   forall(o in OPTS) (
        % count the number of different CONDS for this OPTS value o
        result2[o] = card({const[i]  | i in ASSIGN where result[i]=o} )
   )
;

output [
    "result:\(result)\n","result2:\(result2)\n",];

解决办法是

result:[1,4]
result2:[3,2]

更新:对附加问题的回答

你问为什么这个约束没有给出正确的答案:

constraint forall(i in OPTS,j in CONDS)(
   result2[i] = 
     count([ k == j /\ result[k] = i | k in const],X)
);

以下是这种方法的一些问题。

首先我应该说这种循环通常是一个很好的方法,但问题陈述要求我们计算不同块的数量(每个块包含许多值),而不是所有出现的次数。

  1. k in const 循环遍历所有值 A,...(不是索引),这意味着 result[k] 被解释为 result[A] ... result[D],然后被转换为 { {1}} (A=1,B=2,C=3,D=4) 这不是您想要的。

    相反,result[1] ... result[4] 应该通过 k 循环,而不是普通的 ASSIGN 应该是 k

  2. 另一个问题是 conds[k] 对每个 result2[i] 进行评估,即它进行“重复计算”,这几乎总是意味着麻烦。 (另请注意,约束规划中的值永远不会更新。如果一个循环在 j in CONDS 中获得一个值而在另一个循环中获得另一个值,则模型为 UNSAT。)

    应该使用嵌套循环:

results[i]
  1. 但是,使用 constraint forall(i in OPTS) ( forall(j in CONDS) ( % ... ) ); (或 count)意味着您计算所有 sum 的出现,例如对于值 CONDS 这很重要 两个不正确的 1(来自第一个块)。

要解决这个问题,只需计算 A 的出现次数。在上面的模型中,我使用了集合方法。这是另一种方法,使用 CONDS 只计算每个匹配的 exists 值中的一个。

CONDS