合一算法的Python實現--人工智能

  • 考察 合一算法
  • 求點贊,給作者一點分享的鼓勵
  • 代碼沒寫GUI,因爲不喜歡這玩意,直接在終端中進行人機交互
  • 代碼寫的較爲冗餘,主要還是我沒動力了,不想優化了,因爲調試代碼已經把我榨乾了

合一算法是什麼?

由來

如何解決 謂詞演算歸結 的一個問題,即決定那兩個子句是否爲親本子句。例如,下面兩個文字
是否能夠成爲親本子句呢?

L(f(x)) ∨ L(A)

~L(B)

合一算法正是爲此而來

合一算法

在謂詞邏輯中,一個表達式的 常量符號、變量符號或函數式

  • 表達式的 例示(instance) 是指在表達式中用項來置換變量而得到特定的表達式,用來
    置換的項稱爲置換項。
  • 在歸結過程中,尋找項之間合適的變量置換使表達式一致的過程,稱爲合一過程,簡稱合一(Unify)。

定義1:若存在一個代換s,使得二個文字L1與L2進行代換後,有 L1s = L2s,即 L1與~L2爲互補文字,則L1與L2爲可合一的。這個代換s稱爲合一元(unifier)

代換的使用:

  1. 只能用項(常量,變量或函數符號)t 去代換變量 x在公式中的一切出現,代換記爲s = t/x,對一個公式F的作s代換記爲Fs。顯然,f(x)、A、B之間不存在代換。
  2. 任一被代換的變量不能出現在用作代換的表達式中。{g(x)/x}不是代換。
  3. 代換並非唯一。

看一個例子,對於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}

再看一個例子,

思路及代碼

代碼實現之前的準備

  1. 爲實現方便,可規定將每個文字和函數符表達成一個表,表中第一元素爲謂詞名,其餘元素爲變元。
    • P(x, y) 表示成 (P x y )f(x) 表示成 (f x )
    • 若變元爲函數,則該變元爲一個子表,子表中第一元素爲函數名。如:P(f(x), y) 表示成 (P ( f x ) y ),這樣謂詞和函數都統一表示成表。
  2. 判斷兩個文字能否合一,則要判斷它們所對應的表的各個項是否能夠匹配,判斷兩個表項匹配的規則爲:
    • 變量可與常量、函數或變量匹配;
    • 常量與常量,函數與函數,謂詞與謂詞相等纔可以匹配。不同的常量,函數,謂詞之間不能匹配;
  3. 判斷兩個文字能否合一,還必須判斷表示文字的表的長度必須相等,並且謂詞是否相同。
    例如:P( x , y ) 化成表形式爲 (P x y ),其長度爲3;
    Q( x , y , g(z) ) 化成表形式爲 (Q x y (g z)),其長度爲4。

合一算法Unify(L1, L2)

  1. 若L1或L2爲一原子,則執行
    • 若L1和L2恆等,則返回NIL
    • 若L1爲一變量,則執行:
      • 若L1出現在L2中,則返回F;否則返回(L2/ L1
    • 若L2爲一變量,則執行:
      • 若L2中出現在L1中,則返回F;否則返回(L1/ L2
    • 否則返回F
  2. 若length(L1)不等於length(L2),則返回F。
  3. 置SUBST(替代)爲NIL,在結束本過程時,SUBST將包含用來合一L1和L2的所有代換。
  4. 對於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']

推薦文章

命題邏輯歸結推理 + 合一算法 = 謂詞邏輯歸結推理 就這樣了吧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章