在没有clpfd的prolog中的数独求解器

时间:2017-01-30 14:13:13

标签: prolog sudoku

我有一个 AI 项目,我应该在 Prolog 中创建一个 sudoku 解算器但是而不使用{{1} } 包。我应该如何编写代码,是否有任何方法可以在变量获取值时进行打印?

1 个答案:

答案 0 :(得分:3)

如果没有clpfd,您必须编写经典的生成和测试搜索。关于clpfd的好处是约束限制了搜索空间。您可以在不使用clpfd的情况下模拟此操作。让我们简化一下这个任务,这样我就可以说明了。如何找到解决这两个方程的X和Y值:X + Y = 10,2 * X + Y - 1 = 15?

首先,编码你的两个方程:

eq1(X,Y) :- 10 is X + Y.
eq2(X,Y) :- 15 is 2*X + Y - 1.

现在在求解器谓词中创建搜索空间:

solution(X, Y) :- 
  between(1, 10, X), between(1, 10, Y),
  eq1(X,Y),
  eq2(X,Y).

现在你可以运行它并看到:

?- solution(X,Y).
X = 6,
Y = 4 ;
false.

这比使用clpfd的效率低,因为clpfd会注意到有助于它约束搜索空间的方程式:

?- [library(clpfd)].
true.

?- X + Y #= 10, 2*X + Y - 1 #= 15.
2*X+Y#=16,
X+Y#=10.

?- X + Y #= 10, 2*X + Y - 1 #= 15, X in 1..10.
X = 6,
Y = 4.

看,它已经发现只有一个解决方案,我根本没有约束Y.这真是令人印象深刻!但你仍然可以使用没有clpfd的Prolog,它更糟糕。 :)在这个例子中,我们可能尝试了所有10x10 = 100种可能的组合来找到这个解决方案。效率较低。但并非不可能。

那么,你需要为数独计算出什么限制?可能是这样的:

unique(Row) :- sort(Row, Sorted), length(Row, Length), length(Sorted, Length).
all_digits(Row) :- forall(between(1,9,X), memberchk(X, Row)).

等等。

修改:组合复杂性估算

假设我们做了一个完全无原则的搜索:也就是说,我们甚至会尝试明显错误的情况,例如所有9个网格。我们有9x9 = 81个单元格和9个可能的值(1-9)。这产生9 ^ 81 =一个非常大的数字,不可能在你的一生中检查。而且大多数这些网格都将是毫无结果的排列。

假设您约束搜索,以便每行都是1-9的排列。有9个!排列1-9;有九个,你可以将结果乘以九,所以应该有9!^ 9。这仍然很糟糕! clpfd可能通过结合3x3网格约束来进一步减少这种情况;我不确定如何手动执行此操作,除了以半程序方式,选择3x3网格排列,然后将其中的每个3x1行传递到下一个3x3网格选择。

值得注意的是,经典Prolog程序中的大部分优化都归结为使生成步骤生成更合格的候选者或使测试步骤更便宜。更明显的实现对于检查更复杂的实现仍然有用。

感谢@mat检查我的数学并推荐this excellent article on the combinatorial problem of Sudoku