在本章中,我們將重點介紹邏輯編程以及它在人工智能中的作用。
我們已經知道邏輯是對正確推理原則的研究,或者簡單地說就是研究什么是什么。 例如,如果兩個陳述是真的,那么我們可以從中推斷出任何第三個陳述。
概念
邏輯編程是兩個字,邏輯和編程的組合。 邏輯編程是一種編程模式,其中問題通過程序語句表達為事實和規則,但在形式邏輯系統中。 就像面向對象,函數式,聲明式和程序式等其他編程模式一樣,它也是編程方法的一種特殊方式。
如何用邏輯編程解決問題
邏輯編程使用事實和規則來解決問題。 這就是為什么他們被稱為邏輯編程的基石。 在邏輯編程中需要為每個程序指定一個目標。要理解在邏輯編程中如何解決問題,我們需要了解構建塊 - 事實和規則 -
事實
實際上,每個邏輯程序都需要事實來處理,以達到既定目標。 事實上基本上是關於計划和數據的真實陳述。 例如,北京是中國的首都。
規則
實際上,規則是允許我們對問題域做出結論的約束條件。 規則基本上寫成邏輯條款來表達各種事實。 例如,如果構建游戲,那么必須定義所有規則。
規則對於解決邏輯編程中的任何問題都非常重要。 規則基本上是可以表達事實的合乎邏輯的結論。 以下是規則的語法 -
1
|
A∶− B1,B2,...,Bn.
|
在這里,A是頭部,B1,B2,… Bn是主體。
例如 - ancestor(X,Y): - father(X,Y)
。
ancestor(X,Z): - father(X,Y),ancestor(Y,Z)
。
對於每一個X
和Y
,如果X
是Y
的父親,Y
是Z
的祖先,那么X
是Z
的祖先。對於每個X
和Y
,X
是Z
的祖先,如果X
是 Y
和Y
的父親是Z
的祖先。
安裝必需的包
為了在Python中開始邏輯編程,需要安裝以下兩個包 -
Kanren
它為我們提供了一種簡化業務邏輯編寫代碼的方式。 它讓我們用規則和事實來表達邏輯。 以下命令來安裝kanren
-
1
|
pip install kanren
|
SymPy
SymPy是符號數學的Python庫。 它旨在成為一個全功能的計算機代數系統(CAS),同時保持代碼盡可能簡單,以便易於理解和擴展。 以下命令是用來安裝SymPy -
1
|
pip install sympy
|
邏輯編程的例子
以下是一些可以通過邏輯編程解決的例子 -
匹配數學表達式
實際上,我們可以通過使用邏輯編程以非常有效的方式找到未知值。 以下Python代碼用於匹配數學表達式 -
考慮先導入下列軟件包 -
1
2
3
|
from kanren import run, var, fact
from kanren.assoccomm import eq_assoccomm
as
eq
from kanren.assoccomm import commutative, associative
|
需要定義要使用的數學運算 -
1
2
|
add =
'add'
mul =
'mul'
|
加法和乘法都是交互進程。 因此,我們需要指定它,這可以按照以下方式完成 -
1
2
3
4
|
fact(commutative, mul)
fact(commutative, add)
fact(associative, mul)
fact(associative, add)
|
定義變量是強制性的; 這可以如下完成 -
1
|
a, b = var(
'a'
), var(
'b'
)
|
需要將表達式與原始模式相匹配。有以下原始模式,基礎是(5 + a)* b
-
1
|
Original_pattern = (mul, (add, 5, a), b)
|
有以下兩個表達式來匹配原始模式 -
1
2
|
exp1 = (mul, 2, (add, 3, 1))
exp2 = (add,5,(mul,8,1))
|
輸出可以使用以下命令打印 -
1
2
|
print(run(0, (a,b), eq(original_pattern, exp1)))
print(run(0, (a,b), eq(original_pattern, exp2)))
|
運行此代碼后,將得到以下輸出 -
1
2
|
((3,2))
()
|
第一個輸出表示a
和b
的值。 第一個表達式匹配原始模式並返回a
和b
的值,但第二個表達式與原始模式不匹配,因此沒有返回任何內容。
查找素數
在邏輯編程的幫助下,可以從數字列表中出素數,也可以生成素數。 下面給出的Python代碼將從數字列表中找到素數,並且還會生成前10
個素數。
首先導入以下軟件包 -
1
2
3
4
|
from kanren import isvar, run, membero
from kanren.core import success, fail, goaleval, condeseq, eq, var
from sympy.ntheory.generate import prime, isprime
import itertools
as
it
|
現在,我們將定義一個名為prime_check
的函數,它將根據給定的數字檢查素數作為數據。
1
2
3
4
5
|
def prime_check(x):
if
isvar(x):
return
condeseq([(eq,x,p)]
for
p
in
map(prime, it.count(1)))
else
:
return
success
if
isprime(x)
else
fail
|
現在,聲明一個變量 -
1
2
3
4
|
x = var()
print((
set
(run(0,x,(membero,x,(12,14,15,19,20,21,22,23,29,30,41,44,52,62,65,85)),
(prime_check,x)))))
print((run(10,x,prime_check(x))))
|
上述代碼的輸出如下 -
1
2
|
{19, 23, 29, 41}
(2, 3, 5, 7, 11, 13, 17, 19, 23, 29)
|
解決難題
邏輯編程可用於解決許多問題,如8拼圖,斑馬拼圖,數獨,N皇后等。在這里,舉例說明斑馬拼圖的變體如下 -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
有五間房子。
英國人住在紅房子里。
瑞典人有一只狗。
丹麥人喝茶。
綠房子在白房子的左邊。
他們在綠房子里喝咖啡。
吸Pall Mall的人有鳥。
吸Dunhill在的人黃色房子里。
在中間的房子里,他們喝牛奶。
挪威人住在第一宮。
那個抽Blend的男人住在貓屋旁邊的房子里。
在他們有一匹馬的房子旁邊的房子里,他們吸Dunhill煙。
抽Blue Master的人喝啤酒。
德國人吸Prince煙。
挪威人住在藍房子旁邊。
他們在房子旁邊的房子里喝水,在那里吸Blend煙。
|
在Python的幫助下解決誰有斑馬的問題。
導入必要的軟件包 -
1
2
3
|
from kanren import *
from kanren.core import lall
import time
|
現在,我們需要定義兩個函數 - left()
和next()
來查找哪個房屋左邊或接近誰的房子 -
1
2
3
4
|
def left(q, p, list):
return
membero((q,p), zip(list, list[1:]))
def next(q, p, list):
return
conde([left(q, p, list)], [left(p, q, list)])
|
現在,聲明一個變量:houses
,如下 -
1
|
houses = var()
|
需要在lall
包的幫助下定義規則如下。
有5
間房子 -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
rules_zebraproblem = lall(
(eq, (var(), var(), var(), var(), var()), houses),
(membero,(
'Englishman'
, var(), var(), var(),
'red'
), houses),
(membero,(
'Swede'
, var(), var(),
'dog'
, var()), houses),
(membero,(
'Dane'
, var(),
'tea'
, var(), var()), houses),
(left,(var(), var(), var(), var(),
'green'
),
(var(), var(), var(), var(),
'white'
), houses),
(membero,(var(), var(),
'coffee'
, var(),
'green'
), houses),
(membero,(var(),
'Pall Mall'
, var(),
'birds'
, var()), houses),
(membero,(var(),
'Dunhill'
, var(), var(),
'yellow'
), houses),
(eq,(var(), var(), (var(), var(),
'milk'
, var(), var()), var(), var()), houses),
(eq,((
'Norwegian'
, var(), var(), var(), var()), var(), var(), var(), var()), houses),
(next,(var(),
'Blend'
, var(), var(), var()),
(var(), var(), var(),
'cats'
, var()), houses),
(next,(var(),
'Dunhill'
, var(), var(), var()),
(var(), var(), var(),
'horse'
, var()), houses),
(membero,(var(),
'Blue Master'
,
'beer'
, var(), var()), houses),
(membero,(
'German'
,
'Prince'
, var(), var(), var()), houses),
(next,(
'Norwegian'
, var(), var(), var(), var()),
(var(), var(), var(), var(),
'blue'
), houses),
(next,(var(),
'Blend'
, var(), var(), var()),
(var(), var(),
'water'
, var(), var()), houses),
(membero,(var(), var(), var(),
'zebra'
, var()), houses)
)
|
現在,用前面的約束運行解算器 -
1
|
solutions = run(0, houses, rules_zebraproblem)
|
借助以下代碼,可以提取解算器的輸出 -
1
|
output_zebra = [house
for
house
in
solutions[0]
if
'zebra'
in
house][0][0]
|
以下代碼將打印解決方案 -
1
|
print (
'\n'
+ output_zebra +
'owns zebra.'
)
|
上述代碼的輸出如下 -
1
|
German owns zebra.
|