这些值取决于搜索策略,有时您无法避免叶节点,因为它没有被修剪,也就是说,在它告诉求解器该节点将出现故障之前,什么都没有,将其建模为不同的方式可以防止一些失败,也可以在优化问题的情况下防止次优的解决方案。
这些是在 minizinc 的默认搜索策略的搜索树上评估的前三个节点,我按照评估的顺序将它们标记在 image of the Search Tree 中,4 和 5 表示到达可行的解决方案。
蓝点是仍然存在不确定性的节点,红色方块是失败,白点是未评估的节点,大三角形是整个分支,搜索只导致失败,绿色菱形表示可行的解决方案,橙色菱形表示非最佳但可行的解决方案(仅在优化问题中)。
每个标记节点的解释是
0:根节点:所有变量都未赋值
什么都没发生,这些都是决策变量及其完整域
queens = array1d(1..8, [[1..8], [1..8], [1..8], [1..8], [1..8], [1..8], [1..8], [1..8]]);
1:第一个决定
然后它在最后一个变量的域中选择最小值并进行第一次拆分,求解器认为queens[8] = 1(根的左孩子)或queens[8] = [2..8](根的右孩子),它会首先评估queens[8] = 1 并让第一个节点存在,
queens = array1d(1..8, [[2..7], {2..6,8}, {2..5,7..8}, {2..4,6..8}, {2..3,5..8}, {2,4..8}, [3..8], 1]);
其中决定queens[8] = 1 已经传播到其他变量并从其域中删除了值。
2:搜索继续
然后它再次在queens[7] 处分裂,这是queens[7] = 3 所在的左子节点,该变量的域中的最小值,以及该决策对其他变量的传播。
queens = array1d(1..8, [{2,4..7}, {2,4..6}, {2,4..5,8}, {2,4,7..8}, {2,6..8}, [5..8], 3, 1]);
事后看来(更像是通过查看image of the Search Tree来作弊)我们知道搜索的整个分支都会导致失败,但是我们无法知道在搜索时,因为某些变量仍然存在不确定性,要知道我们必须评估所有可能性,这些可能性可能可行,可能发生或不发生,希望我们能在此之前找到令人满意的解决方案,但在继续搜索之前,请注意已经一些修剪以不存在的节点的形式完成,例如queens[4]此时只能取值2,4,7,8,我们还没有对此做出任何决定,它只是求解器消除值从它知道的变量肯定会导致失败,如果我们在哪里进行蛮力搜索,这个变量将与根节点[1..8]具有相同的域,因为我们还没有做出决定,所以我们是通过传播约束进行更智能的搜索。
3:第一次失败:但我们继续
继续使用相同的策略,它对queens[6]进行拆分,这次是最小值queens[6] = 5,当传播到未决定的变量时,但没有满足所有约束的解决方案(这里它给出了值8 到两个皇后),所以这是一个死胡同,必须回溯。
queens = array1d(1..8, [7, 2, 4, 8, 8, 5, 3, 1]); ---> 失败
所以搜索的前三个节点导致失败。
搜索就这样继续下去,因为queens[6] = 5 的选择导致失败,它会转到下一个值queens[6] = [6..8],该搜索还会导致image of the Search Tree 中用红色圈起来的失败。
您现在可能已经猜到,搜索策略类似于 go in the order of the variables 和 split the domain of the variables by picking the smallest value available and put the rest of the domain in another node,在 minizinc 搜索注释中称为 input_order 和 indomain_min。
现在我们将搜索快进到标记为 4 的节点。
4:解决方案的前奏:我们到了吗?
在这里你可以看到queens[8] = 1(保持不变),queens[7] = 5 而在节点 2 中是queens[7] = 3,这意味着queens[8] = 1 和@ 的所有可能性987654354@ 被评估或修剪,但都导致失败。
queens = array1d(1..8, [{2,4,6..7}, {2..3,6}, {2..4,7}, {3..4,7}, {2,6}, 8, 5, 1]);
然后这个节点进入queens[6] = 2(左孩子)导致更多失败和queens[6] = 6(右孩子)
5:我们找到了黄金:一个可行的解决方案!
queens[2] = 6 传播,结果满足所有约束,所以我们有一个解决方案,我们停止搜索。
queens = array1d(1..8, [4, 2, 7, 3, 6, 8, 5, 1]);
修剪
到达解决方案只需要巨大Whole Search Tree的47个节点,蓝线内的区域是搜索树是the Search Tree,其中节点标记为0,1,2,3,4,5 是,对于这个具有全局约束的 8 个基数为 8 的决策变量的相对较小的实例,它甚至是巨大的,这肯定会大大减少搜索树的跨度,因为它可以在彼此之间大量传达变量的域比求解器的约束存储更有效。整个搜索树总共只有 723 个节点(节点和叶子),其中只有 362 个是叶子,而暴力搜索可以直接生成所有可能的 8^8 个叶子节点(同样,它可能不会,但它可以),就是这样16.777.216 种可能性的搜索空间(它就像 8 个八进制数字,因为它的 8 个变量具有域 8 的基数),当你比较它时,这是一个很大的节省,在 16.777.216 和求解器中只有 362 有意义,并且92 在可行的情况下,它不到您将面临的整个搜索空间组合的 0.0001%,例如,通过在 [1..8] 范围内生成 8 个随机数字并随后评估其可行性来随机生成解决方案,大海捞针。
修剪基本上意味着减少搜索空间,任何比评估所有组合更好的东西,即使通过删除一个单一的可能性也被认为是修剪的搜索空间。由于这是一个满意度问题而不是优化问题,因此修剪只是从变量域中删除不可行的值。
在优化问题中有两种类型的剪枝,像以前一样的满意剪枝,消除不可能的解决方案,以及目标函数边界的剪枝,当目标函数的边界可以在所有变量达到一个值之前确定并且被确定为“最差” 比目前发现的当前“最佳” 值(即在最小化优化中,目标可以在分支中采用的最小值大于迄今为止在可行解决方案中找到的最小值)您可以修剪该分支,该分支肯定包含可行(但不是那么好)的解决方案以及不可行的解决方案,并节省一些工作,您仍然需要修剪或评估如果您想找到最优解并证明它是最优的,则所有的树。
要探索像图像中的搜索树,您可以在 minizinc IDE 中使用 gecode-gist 求解器运行代码,或在命令行中使用 minizinc --Solver gecode-gist <modelFile> <dataFile>,双击您将看到的节点之一决策变量的状态,就像这篇文章中的一样。
甚至进一步使用solve :: int_search( pos, varChoise, valChoise, complete) satisfy; 来测试这种不同的搜索策略
% variable selections:
ann : varChoise
% = input_order
% = first_fail
% = smallest
% = largest
;
% value selections:
ann : valChoise
% = indomain_min
% = indomain_max
% = indomain_median
% = indomain_random
% = indomain_split
% = indomain_reverse_split
;
只需将其粘贴到您的模型中并取消注释一个 varChoise 注释和一个 valChoise 以测试变量选择和值选择的组合,并查看一种策略是否找到具有更少故障、更少节点或更少传播的解决方案。您可以在 minizinc 文档中阅读更多关于它们的信息。