人工智能实验二报告

1. prolog的下载安装

点击进入网址prolog下载地址,然后下载你想要的版本,我下的时第一个SWI-Prolog 8.0.3-1 for Microsoft Windows (64 bit)。
下载后一路next即可。然后就会生成快捷方式,打开即可。

2.基本编程练习

1.首先新建一个文本文件,命名为1.pl 。在里面写知识库,用记事本即可。
2.打开swi-prolog 有两种方式打开这个写的pl,一个是点击file下的consult选到刚刚写的1.pl,一个是直接输入consult('文件路径‘). 注意他的路劲要打/不是\。
3.然后开始输入你想问的问题。
我的1.pl如下

:- use_module(library(tabling)).
:- table connection/2.

connection(X, Y) :-
        connection(X, Z),
        connection(Z, Y).
connection(X, Y) :-
        connection(Y, X).

connection('Amsterdam', 'Schiphol').
connection('Amsterdam', 'Haarlem').
connection('Schiphol', 'Leiden').
connection('Haarlem', 'Leiden').

这是仿照其中最后一题来写的一个逻辑题,当然是简化版,一开始我没有加这个table,然后发现程序陷入死循环,进官网查了才知道由于左递归,SLD解析很容易导致无限循环,该目标(间接)调用其自身的变体或输入数据中的循环。因此,如果我们有一系列的connection / 2语句定义了两个城市之间的铁路连接,则我们不能使用最自然的逻辑定义来表示我们可以在两个城市之间旅行,但是加了table之后就可以,运行结果如下:
简单逻辑的运行结果
为了证明这一点 可以看看不加table的效果。发新陷入死循环,栈爆了。
不加table陷入死循环

3. 实际图搜索问题求解

  1. 说明求解的问题
    虽然也写了别的题目,但是八皇后问题是我研究比较久的问题,所以这以八皇后问题为例。
    八皇后问题:八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。

  2. 要进行的逻辑判断

    • 判断输入的棋子的列号是否合法
    1. 列号应该是1-8
    2. 列号应该各不相同
    • 判断是否存在有对角线冲突
    1. 如果在同一条\ 应该列 - 行相等
    2. 如果在同一条/ 应该行 + 列相等
  3. 程序代码及注释

/*库(clpfd):CLP(FD):有限域上的约束逻辑编程 其实就是后面的组合约束 就是all_different要用*/
:- use_module(library(clpfd)).

/*输入是否合法*/
valid_queen(Col) :-member(Col,[1,2,3,4,5,6,7,8]).
valid_board([]).
valid_board([Head|Tail]) :- valid_queen(Head),valid_board(Tail).

/*对角线 怎么判断是不是在对角线上勒 有两种对角线 分别判断 */
diags1([],[]).
diags1([Col|QueensTail], [Diagonal|DiagonalsTail]) :- 
      length(QueensTail,M),
/* M表示的是尾的长度 因为行号为1-8 所以行号相当于8-QueensTail里的元素的个数*/
      Diagonal is Col- 8+M,
% 对角线 \-行
      diags1(QueensTail,DiagonalsTail).
diags2([],[]).
diags2([Col|QueensTail], [Diagonal|DiagonalsTail]) :- 
      length(QueensTail,M),
      Diagonal is Col +8-M,
% 对角线 /+行
      diags2(QueensTail,DiagonalsTail).
%总结 八皇后正确的条件就是 首先要列都在边界内
eight_queens(Board) :-
length(Board,8),
/*判断是否同列*/
valid_board(Board),
/*然后分别获取两种对角线的值(行+列或者列-行)*/
diags1(Board,Diags1),
diags2(Board,Diags2),
/*列号各不相同 对角线的值也各不相同 就ok了*/
all_different(Board),
all_different(Diags1),
all_different(Diags2).
  1. 运行结果
    在这里插入图片描述
  2. 实验收获、反思与改进
    网上关于八皇后其实有很多代码 但是我们可以根据写c语言时的想法来解决这个问题 初于C的的对空间的优化的思想 我没有用一个有2*8元素的表, 因为其实行号和列表中的序号时一样的. 但是原以为我这么写会挺好,结果运行时长反而增加, 仔细思考,虽然我优化了空间复杂度,也省去了取列号的过程.,但是prolog相当于暴力,每次套用一个数据,我就需要调用length16次.而原办法每次套用一个数据只需获取列号8次,获取列号的递归调用也是直接访问就好,而获取length还需要额外访问(不知道表在prolog语言中有没有维护length信息还是遍历的).prolog本来就是一个很傻的方法来获取答案,时间开销很大,所以这种优化空间浪费时间的写法好像并不可取,至少让我觉得用户体验很差,所以建议还是用一个"结构体:来存储list里的元素把.
    "结构体方式存储"如下
:- use_module(library(clpfd)).
/*库(clpfd):CLP(FD):有限域上的约束逻辑编程 其实就是后面的组合约束 就是all_different要用*/

valid_queen((_,Col)) :-member(Col,[1,2,3,4,5,6,7,8]).
/*一个正确的八皇后问题需要首先列都在范围内*/
/*member就是当col在后面这个数组里就为true*/
/*所以下面就是进行这个判断*/
valid_board([]).
valid_board([Head|Tail]) :- valid_queen(Head),valid_board(Tail).
/*判断一个数组是否是符合规则的,头要符合规则queen,剩下的数组也要符合规则
这里相当于一个递归 当元素只有一个的时候就调用valid_queen,开始判断列,当元素不止一个的时候就继续调用自身。当为空的时候就为真 获得一个的办法就是获取头 因为每次数组加|就会分为头和剩下的元素*/
cols([],[]).
cols([(_,Col)|QueenTail],[Col|ColsTail]) :-
    cols(QueenTail,ColsTail).
/*现在开始取出列的数组 遍历每一个坐标 把一个空Queens列表执行rows的结果是一个空Rows列表。
如果Queens列表中第一个元素的Col与cols列表中的第一个元素匹配,
并且如果对Queens的Tail列表执行cols的结果是Cols的Tail列表,
那么cols(Queens,Cols)的结果就是Cols列表。
(这段可以这么理解,row([(1,1),(2,5),(3,6),(4,7),(5,8),(6,6),(7,5),(8,4)],A)将第一个列表中的列号取出来,给A)
*/
diags1([],[]).
diags1([(Row,Col)|QueensTail], [Diagonal|DiagonalsTail]) :- 
      Diagonal is Col - Row,
      diags1(QueensTail,DiagonalsTail).
/*对角线 怎么判断是不是在对角线上类 有两种对角线 分别判断 */
diags2([],[]).
diags2([(Row,Col)|QueensTail], [Diagonal|DiagonalsTail]) :- 
      Diagonal is Col + Row,
      diags2(QueensTail,DiagonalsTail).
/*总结 八皇后正确的条件就是 首先要列都在边界内*/
eight_queens(Board) :-
Board=[(1,_),(2,_),(3,_),(4,_),(5,_),(6,_),(7,_),(8,_)],
valid_board(Board),
/*然后分别获取列号 对角线的值(行+列或者列-行)
cols(Board,Cols),
diags1(Board,Diags1),
diags2(Board,Diags2),
/*当列号各不相同 对角线的值各不相同 就ok了*/
all_different(Cols),
all_different(Diags1),
all_different(Diags2).
  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值