this repo has no description
1.. _sec-sat: 2 3在MiniZinc中对布尔可满足性问题建模 4============================================ 5 6MiniZinc 可以被用来给布尔可满足性问题建模,这种问题的变量被限制为是布尔型 (:mzn:`bool`)。MiniZinc 可以使用有效率的布尔可满足性求解器来有效地解决求得的模型。 7 8 9整型建模 10------------------ 11 12很多时候,尽管我们想要使用一个布尔可满足性求解器,我们可能也需要给问题的 整数部分建模。 13有三种通用的方式使用布尔型变量对定义域为范围 :math:`0 \dots m` 内的整型变量 :math:`I` 建模,其中 :math:`m = 2^{k}-1` 14 15- 二元: :math:`I` 被表示为 :math:`k` 个二元变量 :math:`i_0, \ldots, i_{k-1}` ,其中 :math:`I = 2^{k-1} i_{k-1} + 2^{k-2} i_{k-2} + \cdots + 2 i_1 + i_0` 。在MiniZinc 中,这可表示为 16 17 .. code-block:: minizinc 18 19 array[0..k-1] of var bool: i; 20 var 0..pow(2,k)-1: I = sum(j in 0..k-1)(bool2int(i[j])*pow(2,j)); 21 22- 一元: :math:`I` 被表示为 :math:`m` 个二元变量 :math:`i_1, \ldots, i_m`:math:`i = \sum_{j=1}^m \mathtt{bool2int}(i_j)` 。由于在一元表 示中有大量的冗余表示,我们通常要求 :math:`i_j \rightarrow i_{j-1}, 1 < j \leq m` 。在MiniZinc中,这可表示为 23 24 .. code-block:: minizinc 25 26 array[1..m] of var bool: i; 27 constraint forall(j in 2..m)(i[j] -> i[j-1]); 28 var 0..m: I = sum(j in 1..m)(bool2int(i[j]); 29 30- 值: 其中 :math:`I` 被表示为 :math:`m+1` 个二元变量 :math:`i_0, \ldots, i_m` ,其中 :math:`i = k \Leftrightarrow i_k` 并且 :math:`i_0, \ldots, i_m` 中最多有一个为真。在MiniZinc中,这可表示为 31 32 .. code-block:: minizinc 33 34 array[0..m] of var bool: i; 35 constraint sum(j in 0..m)(bool2int(i[j]) == 1; 36 var 0..m: I; 37 constraint foall(j in 0..m)(I == j <-> i[j]); 38 39每种表示都有其优点和缺点。这取决于模型中需要对整数做什么样的操作,而这些 操作在哪一种表示上更为方便。 40 41非等式建模 42--------------------- 43 44接下来,让我们考虑如何为一个拉丁方问题建模。一个拉丁方问题是在 :math:`n \times n` 个网 格上放置 :math:`1..n` 之间的数值使得每个数在每行每列都仅出现一次。图 :numref:`ex-latin` 中给出了拉丁方问题的的一个整数模型。 45 46.. literalinclude:: examples/latin.mzn 47 :language: minizinc 48 :name: ex-latin 49 :caption: 拉丁方问题的整数模型 (:download:`latin.mzn <examples/latin.mzn>`). 50 51整型变量直接的唯一的约束实际上是非等式,而它在约束 :mzn:`alldifferent` 中被编码。 数值表示是表达非等式的最佳方式。图 :numref:`ex-latinbool` 给出了一个关于拉丁方问题的只含有布尔型 变量的模型。注意每个整型数组元素 :mzn:`a[i,j]` 被替换为一个布尔型数组。我们使用谓词 :mzn:`exactlyone` 来约束每个数值在每行每列都仅出现一次,也用来约束有且仅有一个布尔型 变量对应于整型数组元素 :mzn:`a[i,j]` 为真。 52 53.. literalinclude:: examples/latinbool.mzn 54 :language: minizinc 55 :name: ex-latinbool 56 :caption: 拉丁方问题的布尔型模型 (:download:`latinbool.mzn <examples/latinbool.mzn>`). 57 58势约束建模 59--------------------- 60 61让我们来看下如何对点灯游戏建模。这个游戏由一个矩形网格组成,每个网格为空 白或者被填充。每个被填充的方格可能包含1到4之间的数字,或者没有数字。我们的 目标是放置灯泡在空白网格使得 62 63- 每个空白的网格是“被照亮的”,也就是说它可以透过一行没有被打断的空白网格 看到光亮。 64- 任何两个灯泡都不能看到彼此。 65- 一个有数值的填充的网格相邻的灯泡个数必须等于这个网格中的数值。 66 67:numref:`fig-lightup` 给出了点灯游戏的一个例子以及 :numref:`fig-lightup-sol` 给出了它的解。 68 69.. _fig-lightup: 70 71.. figure:: figures/lightup.* 72 73 点灯游戏的一个例子展示 74 75.. _fig-lightup-sol: 76 77.. figure:: figures/lightup2.* 78 79 点灯游戏的完整的解 80 81这个问题很自然地可以使用布尔型变量建模。布尔型变量用来决定哪一个网格包含有一个点灯以及哪一个没有。同时我们也有一些作用于填充的网格上的整数算术运算要 考虑。 82 83 84.. literalinclude:: examples/lightup.mzn 85 :language: minizinc 86 :name: ex-lightup 87 :caption: 点灯游戏的SAT模型 (:download:`lightup.mzn <examples/lightup.mzn>`). 88 89:numref:`ex-lightup` 中给出了这个问题的一个模型。图 :numref:`fig-lightup` 中给出的问题的数据文件在图 :numref:`fig-lightupdzn` 中给出。 90 91.. literalinclude:: examples/lightup.dzn 92 :language: minizinc 93 :name: fig-lightupdzn 94 :caption: 点灯游戏的 :numref:`fig-lightup` 中实例的数据文件 95 96模型利用了一个布尔型求和谓词 97 98.. code-block:: minizinc 99 100 predicate bool_sum_eq(array[int] of var bool:x, int:s); 101 102使得一个布尔型数组的和等于一个固定的整数。多种方式都能使用布尔型变量给 *cardinality* 约束建模。 103 104- 加法器网络:我们可以使用包含加法器的一个网络给布尔型总和建立一个二元布 尔型表达式 105- 排序网络:我们可以通过使用一个排序网络去分类布尔型数组来创建一个布尔型 总和的一元表达式 106- 二元决策图:我们可以创建一个二维决策图(BDD)来编码势约束。 107 108 109.. literalinclude:: examples/bboolsum.mzn 110 :language: minizinc 111 :name: ex-bboolsum 112 :caption: 使用二元加法器网络表示势约束 (:download:`bboolsum.mzn <examples/bboolsum.mzn>`). 113 114.. literalinclude:: examples/binarysum.mzn 115 :language: minizinc 116 :name: ex-binarysum 117 :caption: 创建二元求和网络的代码 (:download:`binarysum.mzn <examples/binarysum.mzn>`). 118 119我们可以使用图 :numref:`ex-bboolsum` 给出的二元加法器网络代码实现 :mzn:`bool_sum_eq` 。图 :numref:`ex-binarysum` 中定义的 谓词 :mzn:`binary_sum` 创建了一个 :mzn:`x` 总和的二维表示法。它把列表分为两部分,把每一部分 分别加起来得到它们的一个二元表示,然后用 :mzn:`binary_add` 把这两个二元数值加起来。 如果 :mzn:`x` 列大小是奇数,则最后一位被保存起来作为二元加法时的进位来使用。 120 121.. \pjs{Add a picture of an adding network} 122 123.. literalinclude:: examples/uboolsum.mzn 124 :language: minizinc 125 :name: ex-uboolsum 126 :caption: 使用二元加法器网络表示势约束 (:download:`uboolsum.mzn <examples/uboolsum.mzn>`). 127 128.. literalinclude:: examples/oesort.mzn 129 :language: minizinc 130 :name: ex-oesort 131 :caption: 奇偶归并排序网络 (:download:`oesort.mzn <examples/oesort.mzn>`). 132 133我们可以使用图 :numref:`ex-uboolsum` 中给出的一元排序网络代码来实现 :mzn:`bool_sum_eq` 。势约束通过扩展输入 :mzn:`x` 长度为2 的次幂,然后使用奇偶归并排序网络给得到的位排序来实现。奇偶归 并排序工作方式在图 :numref:`ex-oesort` 中给出,它递归地把输入列表拆为两部分,给每一部分排序,然 后再把有序的两部分归并起来。 134 135.. \pjs{Add much more stuff on sorting networks} 136 137 138 139.. \pjs{Add a picture of an adding network} 140 141.. literalinclude:: examples/bddsum.mzn 142 :language: minizinc 143 :name: ex-bddsum 144 :caption: 使用二元加法器网络表示势约束 (:download:`bddsum.mzn <examples/bddsum.mzn>`). 145 146我们可以使用图 :numref:`ex-bddsum` 中给出的二元决策图代码来实现 :mzn:`bool_sum_eq` 。势约束被分为两种情况:或者第一个元素 :mzn:`x[1]`:mzn:`true` 并且剩下位的总和是 :mzn:`s-1` ,或者 :mzn:`x[1]`:mzn:`false` 并 且剩下位的总和是 :mzn:`s` 。它的效率的提高依赖于去除共同子表达式来避免产生太多的相同的约束。 147 148 149.. \pjs{Add a picture of a bdd network network} 150 151