湖南大學人工智慧實驗二:Prolog程式設計求解圖搜尋問題

Moyu_42發表於2020-12-29

說在前面:這個實驗我也是抄的 真的有人會寫prolog嘛?

程式碼連結:人工智慧實驗

人工智慧實驗二:Prolog程式設計求解圖搜尋問題

一.實驗目的

  1. 熟悉PROLOG的執行環境,進行PROLOG的基本程式設計練習;

    瞭解PROLOG語言中常量、變數的表示方法。PROLOG的簡單程式結構,掌握分析問題、詢問解釋技巧;進行事實庫、規則庫的編寫,並在此基礎上進行簡單的詢問。

  2. 求解圖搜尋問題。

    任選一個以下實際應用題目:愛因斯坦的超級問題、字謎問題、漢諾塔問題、八數碼問題、八皇后問題、農夫過河問題、傳教士與野人問題

二.實驗的硬體、軟體平臺

__硬體:__計算機

__軟體:__作業系統:WINDOWS

__應用軟體:__GNU Prolog

三.實驗內容及步驟

熟悉prolog語言的使用並實現求解圖搜尋問題

實驗步驟:

  1. 安裝prolog整合開發環境
  2. 採用prolog編寫所選問題的源程式
  3. 編譯程式,輸出查詢問題的結果或資料

四.實驗報告要求

  1. 通過PROLOG的基本程式設計練習,說明實驗的方法和步驟;

  2. 針對圖搜尋要求,說明求解的問題與程式、程式分析、註釋、執行結果等,在討論與結論部分說明實驗收穫、難點重點討論等;

  3. 附上所有實驗原始碼。

五.實驗步驟

通過PROLOG的基本程式設計練習,說明實驗的方法和步驟

原子:小寫字母開頭

變數:大寫字母開頭

符合用來組成事實

規則:且:, 或:. 非:\+

loves(jack,mia).
loves(marsellus,mia).
loves(mike,john).
loves(john,mike).
 
jealous(X,Y):- loves(X,Z), loves(Y,Z).
| ?- jealous(jack, marsellus).

yes
| ?- jealous(mike, john).

no

列表:[A, B, C],可以通過[Head|Tail]解析

| ?- [Head|Tail] = [A, B, C].

Head = A
Tail = [B,C]

yes

匿名變數:_

| ?- [A, B, C, D] = [_,Head|Tail].

Head = B
Tail = [C,D]

yes

內建謂詞

member:檢查某一個值是否在一個列表內

fd_all_different:檢查列表中是否有重複元素

fd_domain:驗證值是否在一個範圍之內

length:獲取列表的長度

針對圖搜尋要求,說明求解的問題與程式、程式分析、註釋、執行結果等,在討論與結論部分說明實驗收穫、難點重點討論等

求解的問題

在8×8格的國際象棋上擺放8個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。

程式
valid((_,Col)):-
    Range = [1,2,3,4,5,6,7,8],
    member(Col,Range).

valid_board([]).
valid_board([Head|Tail]):- valid(Head),valid_board(Tail).

cols([],[]).
cols([(_,Col)|QueensTail],[Col|ColsTail]):-
    cols(QueensTail,ColsTail).
    
main_diag([],[]).
main_diag([(Row,Col)|QueensTail],[Diagonal|DiagonalsTail]):-
    Diagonal is Col - Row,
    main_diag(QueensTail,DiagonalsTail).
    
diag([],[]).
diag([(Row,Col)|QueensTail],[Diagonal|DiagonalsTail]):-
    Diagonal is Col + Row,
    diag(QueensTail,DiagonalsTail).

eight_queens(Board) :-
    Board = [(1, _), (2, _), (3, _), (4, _), (5, _), (6, _), (7, _), (8, _)],
    valid_board(Board),
    
    cols(Board,Cols),
    main_diag(Board,Main_diag),
    diag(Board,Diag),
    
    fd_all_different(Cols),
    fd_all_different(Main_diag),
    fd_all_different(Diag).
程式分析及註釋
  1. 主體部分:
eight_queens(Board) :-
    Board = [(1, _), (2, _), (3, _), (4, _), (5, _), (6, _), (7, _), (8, _)],
    valid_board(Board),
    
    cols(Board,Cols),
    main_diag(Board,Main_diag),
    diag(Board,Diag),
    
    fd_all_different(Cols),
    fd_all_different(Main_diag),
    fd_all_different(Diag).
  1. 既然是8皇后問題,自然要定義一個8 * 8的棋盤,根據約束條件:不能位於同一行,所以按照prolog的語法棋盤可表示為:
Board = [(1, _), (2, _), (3, _), (4, _), (5, _), (6, _), (7, _), (8, _)]

棋盤生成之後就需要檢查是否符合範圍要求,即是不是在棋盤上:

valid_board(Board).

由於Board是以列表的形式呈現,所以可以用之前提到的[Head|Tail]進行判斷,檢查Head指向的當前第一個元素是不是符合要求,再將剩餘的Tail作為新的列表繼續。檢查是不是符合時,只需要判斷列號是不是在1-8的範圍內,這個可以用到內建謂詞member判斷是不是在範圍內。具體實現為

valid((_,Col)):-
    Range = [1,2,3,4,5,6,7,8],
    member(Col,Range).

valid_board([]).
valid_board([Head|Tail]):- valid(Head),valid_board(Tail).
  1. 接下來就是生成列號以及得到兩個對角線的數值:
    cols(Board,Cols),
    main_diag(Board,Main_diag),
    diag(Board,Diag),

對於Col列號,只需要從棋盤中取出即可

cols([],[]).
cols([(_,Col)|QueensTail],[Col|ColsTail]):-
    cols(QueensTail,ColsTail).

而對於兩個對角線,則需要通過計算:主對角線為列 - 行,副對角線為列 + 行

main_diag([],[]).
main_diag([(Row,Col)|QueensTail],[Diagonal|DiagonalsTail]):-
    Diagonal is Col - Row,
    main_diag(QueensTail,DiagonalsTail).
    
diag([],[]).
diag([(Row,Col)|QueensTail],[Diagonal|DiagonalsTail]):-
    Diagonal is Col + Row,
    diag(QueensTail,DiagonalsTail).
  1. 這樣在最後,只需要保證列號,兩條對角線均不相同,則可完成任務:
    fd_all_different(Cols),
    fd_all_different(Main_diag),
    fd_all_different(Diag).
執行結果

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-QsnSdAVv-1609253228360)(C:\Users\a2783\AppData\Roaming\Typora\typora-user-images\image-20201121165519777.png)]

均符合要求

實驗收穫

這次實驗最大的應該就是一定程度上掌握了prolog語言吧,瞭解到了還有一門這樣的可以進行邏輯推理,程式設計也比較方便的語言。主要的難點部分的話,應該就是prolog語法的學習,因為在之前一直沒有接觸過,而且有的地方的不熟練也在一定程度上限制了程式設計,所以這次實驗主要的時間都是耗費在了prolog的學習之上。

實驗原始碼

valid((_,Col)):-
    Range = [1,2,3,4,5,6,7,8],
    member(Col,Range).

valid_board([]).
valid_board([Head|Tail]):- valid(Head),valid_board(Tail).

cols([],[]).
cols([(_,Col)|QueensTail],[Col|ColsTail]):-
    cols(QueensTail,ColsTail).
    
main_diag([],[]).
main_diag([(Row,Col)|QueensTail],[Diagonal|DiagonalsTail]):-
    Diagonal is Col - Row,
    main_diag(QueensTail,DiagonalsTail).
    
diag([],[]).
diag([(Row,Col)|QueensTail],[Diagonal|DiagonalsTail]):-
    Diagonal is Col + Row,
    diag(QueensTail,DiagonalsTail).

eight_queens(Board) :-
    Board = [(1, _), (2, _), (3, _), (4, _), (5, _), (6, _), (7, _), (8, _)],
    valid_board(Board),
    
    cols(Board,Cols),
    main_diag(Board,Main_diag),
    diag(Board,Diag),
    
    fd_all_different(Cols),
    fd_all_different(Main_diag),
    fd_all_different(Diag).

相關文章