- 考察 合一算法
- 求點贊,給作者一點分享的鼓勵
- 代碼沒寫GUI,因爲不喜歡這玩意,直接在終端中進行人機交互
- 代碼寫的較爲冗餘,主要還是我沒動力了,不想優化了,因爲調試代碼已經把我榨乾了
合一算法是什麼?
由來
如何解決 謂詞演算歸結 的一個問題,即決定那兩個子句是否爲親本子句。例如,下面兩個文字
是否能夠成爲親本子句呢?
L(f(x)) ∨ L(A)
~L(B)
合一算法正是爲此而來
合一算法
在謂詞邏輯中,一個表達式的 項 是 常量符號、變量符號或函數式。
- 表達式的 例示(instance) 是指在表達式中用項來置換變量而得到特定的表達式,用來
置換的項稱爲置換項。 - 在歸結過程中,尋找項之間合適的變量置換使表達式一致的過程,稱爲合一過程,簡稱合一(Unify)。
定義1:若存在一個代換s,使得二個文字L1與L2進行代換後,有 L1s = L2s,即 L1與~L2爲互補文字,則L1與L2爲可合一的。這個代換s稱爲合一元(unifier)。
代換的使用:
- 只能用項(常量,變量或函數符號)t 去代換變量 x在公式中的一切出現,代換記爲s = t/x,對一個公式F的作s代換記爲Fs。顯然,f(x)、A、B之間不存在代換。
- 任一被代換的變量不能出現在用作代換的表達式中。{g(x)/x}不是代換。
- 代換並非唯一。
看一個例子,對於F= P(x, f(y), B),存在四種代換:
s1 = {z/x, w/y},則Fs1 = P(z, f(w), B) (較一般的代換)
s2 = {A/y},則Fs2 = P(x, f(A), B)
s3 = {g(z)/x, A/y},則Fs3 = P(g(z), f(A), B)
s4 = {C/x, A/y},則Fs4 = P(C, f(A), B) (限制最嚴的代換)
s4稱爲基例示,因爲作替換後不再含有變量。
定義2:表達式集合 {E1, … , Er} 的合一元σ稱爲是最一般合一元(most general unifier 簡寫爲 mgu),當且僅當對集合的每一個合一θ都存在代換λ使得θ=σ·λ,即任何代換都可以由σ再次代換而來。
看一個例子,表達式集合{P(x), P(f(y))}
是可合一的,其最一般合一元爲{f(y)/x}
。因爲對此集合的合一元θ = {f(a)/x, a/y}
,有代換 λ = {a/y}
,使θ = σ·λ = {f(y)/x}·{a/y}
再看一個例子,
思路及代碼
代碼實現之前的準備
- 爲實現方便,可規定將每個文字和函數符表達成一個表,表中第一元素爲謂詞名,其餘元素爲變元。
- P(x, y) 表示成 (P x y ),f(x) 表示成 (f x )。
- 若變元爲函數,則該變元爲一個子表,子表中第一元素爲函數名。如:P(f(x), y) 表示成 (P ( f x ) y ),這樣謂詞和函數都統一表示成表。
- 判斷兩個文字能否合一,則要判斷它們所對應的表的各個項是否能夠匹配,判斷兩個表項匹配的規則爲:
- 變量可與常量、函數或變量匹配;
- 常量與常量,函數與函數,謂詞與謂詞相等纔可以匹配。不同的常量,函數,謂詞之間不能匹配;
- 判斷兩個文字能否合一,還必須判斷表示文字的表的長度必須相等,並且謂詞是否相同。
例如:P( x , y ) 化成表形式爲 (P x y ),其長度爲3;
Q( x , y , g(z) ) 化成表形式爲 (Q x y (g z)),其長度爲4。
合一算法Unify(L1, L2)
- 若L1或L2爲一原子,則執行
- 若L1和L2恆等,則返回NIL
- 若L1爲一變量,則執行:
- 若L1出現在L2中,則返回F;否則返回(L2/ L1)
- 若L2爲一變量,則執行:
- 若L2中出現在L1中,則返回F;否則返回(L1/ L2)
- 否則返回F
- 若length(L1)不等於length(L2),則返回F。
- 置SUBST(替代)爲NIL,在結束本過程時,SUBST將包含用來合一L1和L2的所有代換。
- 對於i:=1 到L1的元素數|L1|,執行
- 對合一L1的第 i 個元素和L2的第 i 個元素調用UNIFY,並將結果放在S中。
- 若S = F,則返回F 。
- 若S不等於NIL,則執行:把S應用到L1和L2的剩餘部分;SUBST:=APPEND(S, SUBST)。返回SUBST。
合一過程Unify(L1, L2)
-
輸入:
L1, L2 爲表示成爲表的兩個謂詞
例如:P(x, y) 輸入爲 (P x y ),
f(x) 輸入爲 (f x );
P(f(x), y) 輸入爲 (P ( f x ) y ) -
輸出:
如果找到代換,一張代換表作爲其返回的值;
如果返回空表NIL,表示可以匹配,但無需任何代換;
如果返回由F值組成的表,則說明合一過程失敗。
代碼:
import re
"""
處理從鍵盤中讀取的文字,返回列表
- 中文字符切換爲英文
- 括號轉化爲列表
- 空格轉爲逗號
- 函數名一律轉爲大寫字母
"""
def handleWord(word):
# 中文字符切換爲英文; 空格轉爲逗號
word = word.replace('(', '[').replace(')', ']').replace('(', '[').replace(')', ']').replace(' ', ',').strip()
# 對謂詞進行操作
if len(word) > 3:
# 函數名一律轉爲大寫字母
def func(x):
return '[' + x.string[x.start()+1].upper()
word = re.sub(r'\[(\w)', lambda x:func(x), word)
# 將word中字母字符轉化爲ASCII碼,爲了使用eval直接將其轉化爲元組
def func2(x):
return str(ord(x.string[x.start()]))
word = re.sub(r'[a-zA-Z]', lambda x:func2(x), word)
# 將其轉化爲列表,內部數據爲int類型
word = list(eval(str(word)))
# print('---handleWord()---')
# print(word)
# print(type(word))
# print('---handleWord()---')
print()
return word
"""
判斷是變量、常量還是函數
- 是變量,返回1
- 是常量,返回2
- 是函數,返回3
"""
def judge(x):
if isinstance(x, list):
return 3
elif x >= 97 and x <= 122:
return 1
elif x >= 65 and x <= 90:
return 2
"""
合一算法
"""
def unifyAtom(atom1, atom2):
if judge(atom1) == 3 and judge(atom2) == 3:
return unifyAllPre(atom1, atom2)
if atom1 == atom2:
return 'NIL'
if judge(atom1) == 1:
if str(atom1) in str(atom2):
# print('--2--')
return False
else:
return str(atom2) + '/' + str(atom1) + '/' + 'one'
elif judge(atom2) == 1:
if str(atom2) in str(atom1):
# print('--3--')
return False
else:
return str(atom1) + '/' + str(atom2) + '/' + 'two'
else:
# print('--4--')
return False
# testNumber = 0
results = []
"""
合一算法(對謂詞公式中的參數逐一判斷)
"""
def unify(L1, L2):
global results
for index in range(len(L1)):
# global testNumber
# testNumber += 1
# print(testNumber)
if(L1 == L2):
return results
atom1 = L1[index]
atom2 = L2[index]
S = unifyAtom(atom1, atom2)
if not S or isinstance(S, list):
# print('--5--')
return S
elif S != 'NIL':
t = S.split('/')
result = chr(int(t[0])) + '/' + chr(int(t[1]))
if t[2] == 'one':
t1 = str(L1).replace(t[1], t[0])
L1 = list(eval(t1))
if t[2] == 'two':
t2 = str(L2).replace(t[1], t[0])
L2 = list(eval(t2))
results.append(result)
# print(result)
# print(L1,L2)
return results
"""
合一算法(兩個文字都是謂詞公式時)
"""
def unifyAllPre(L1, L2):
if len(L1) != len(L2) or L1[0] != L2[0]:
# print('--1--')
return False
if L1 == L2:
return 'NIL'
del L1[0]
del L2[0]
return unify(L1, L2)
def ui():
print('----')
print('--------合一算法--------')
print('約定:')
print('1.變量輸入時爲小寫字母')
print('2.常量輸入時爲大寫字母')
print('3.函數名輸入時大寫或小寫字母,並且程序運行函數名不區分大小寫,比如函數名f與函數名F等價')
print('----')
print('輸入方式:')
print('比如:P(x, y, g(z)) 輸入形式爲:(P x y (g z))')
print('字符之間用一個空格分隔')
print('----')
print('輸出結果:')
print('有代換,即輸出代換s')
print('完全相等,輸出NIL')
print('合一失敗,輸出False')
print('----')
word1 = input("輸入第一個文字:")
word2 = input("輸入第二個文字:")
word1 = handleWord(word1)
word2 = handleWord(word2)
print(unifyAllPre(word1, word2))
def main():
ui()
if __name__ == '__main__':
main()
- 代碼註釋處,之所以保留,是爲了運行出問題時快速定位錯誤
人機交互示例:
----
--------合一算法--------
約定:
1.變量輸入時爲小寫字母
2.常量輸入時爲大寫字母
3.函數名輸入時大寫或小寫字母,並且程序運行函數名不區分大小寫,比如函數名f與函數名F等價
----
輸入方式:
比如:P(x, y, g(z)) 輸入形式爲:(P x y (g z))
字符之間用一個空格分隔
----
輸出結果:
有代換,即輸出代換s
完全相等,輸出NIL
合一失敗,輸出False
----
輸入第一個文字:(P x y W (q r (T y)))
輸入第二個文字:(P E D t (Q A (t D)))
['E/x', 'D/y', 'W/t', 'A/r']
推薦文章
命題邏輯歸結推理 + 合一算法 = 謂詞邏輯歸結推理 就這樣了吧