学习目标:
利用prolog解决八皇后问题
学习内容:
prolog基础
学习产出:
偶然间看到了八皇后问题,和数独类似,前后充满了严密的逻辑性。所以我想到应该可以用prolog语言来解决。查阅资料得知有三种解法。双坐标法、单坐标法、四坐标法。现在先介绍双坐标法。具体八皇后问题和prolog语言是什么。请自行查阅资料。
1.双坐标法。
在这里我们把解的通式写成:[1/Y1,2/Y2,3/Y3,4/Y4,5/Y5,6/Y6,7/Y7,8/Y8].(这里1,2,3,4,5,6,7,8为横坐标,Y为纵坐标,针对的是8*8的问题)。这样具体的解会有很多个。
双坐标法是相对简单的解法。只需满足两个条件:
a.Y在[1,2,3,4,5,6,7,8]中。
b.任意两个皇后不在同一行(列),且不在同一对角线上。不在同一行已经满足了。假如有两个皇后,她们的坐标分别为:(X,Y),(X1,Y1),则不在同一列可表示为Y!=Y1,而不在同一个对角线上可表示为(对角线有上对角线和下对角线):X-X1!=Y-Y1,OR X-X1!=Y1-Y,这里自行画图即可推出(初中知识)。
现在就可以写prolog代码了,代码如下:
member(A,[A|T]).
member(A,[H|B]):-member(A,B).
solution([]).
solution([X/Y|A]):-solution(A),
member(Y,[1,2,3,4,5,6,7,8]),
notattack(X/Y,A).
notattack[_,[]].
notattack(X/Y,[X1/Y1|R]):-not(Y=Y1),
Yd is Y1-Y,Xd1 is X1-X,
not(Yd=xd1),
Xd2 is X-X1,
not(Yd=xd2),
notattack(X/Y,R).
template([1/Y1,2/Y2,3/Y3,4/Y4,5/Y5,6/Y6,7/Y7,8/Y8]).
在swi-prolog解释器中输入:template(Q),solution(Q).即可得到所有的解。
代码解释:
1.
member(A,[A|T]).
member(A,[H|B]):-member(A,B).
这个是用来判断A是否在列表B中的。算是“轮子”,直接拿来用即可。
2
solution([]).
solution([X/Y|A]):-solution(A),member(Y,[1,2,3,4,5,6,7,8]),notattack(X/Y,A).
这里第一句就是边界条件,因为后面用到了递归,所以这是必要的。从最简单的开始,即空列表,这里就认为空列表也算是解。
然后第二句就是对上上面提到的规则的总体概述。个人理解:solution(S),这里的S里面是所有解的一个集合,即S=[[1,2,3,],[4,5,6],[7,8,9],[a,b,c],[d,r,f]]。(这里仅仅是表示格式是这样)。然后再逐个对解进行“约束”。相当于python中的for。但prolog中没有for语句,所以直接用solution([X/Y|Others])来逐一进行匹配。然后solution(A)是递归,没有他不能完成逐一匹配。member就相当于对Y的取值进行限制。notattack即是不能在同一行(列)且不能在同一对角线上。
3.
notattack[_,[]].
notattack(X/Y,[X1/Y1|R]):-not(Y=Y1),
Yd is Y1-Y,Xd1 is X1-X,
not(Yd=xd1),
Xd2 is X-X1,
not(Yd=xd2),
notattack(X/Y,R).
这里就是对上面中的代码的具体解释。依然首先是确定边界条件。第二步中的solution(X/Y|A),递归完后的最终形式肯定是solution(X/Y|[]),相应的notattack为notattack(X/Y,[]).所以notatack的边界条件就确定了。notattack是对两个坐标进行比较,所以第一个位置填X/Y,后面第二个位置自然是解列表中的坐标了,所以用[X1/Y1|R]来表示解列表中的第一个坐标,后面的语句就是对逻辑的解释了。