基礎算法四:編輯距離和文本相似度計算

編輯距離是對兩個字符串差異化的量化,其含義是將一個字符串轉化爲另一個字符串所需的最少操作次數,允許的編輯操作包括將一個字符替換爲另一個字符,插入一個字符,刪除一個字符。編輯距離可用在自然語言處理中,用於計算兩個文本之間的相似度。

算法的基本原理:對於字符串a[1:i]和字符串b[1:j]來說,用edit[i][j]表示它們間的編輯距離。

如果a[i]和b[j]相同,則edit[i][j]=edit[i-1][j-1]。

如果a[i]和b[j]不相同,則有如下情況:

1)  a[1:i]經過多次操作轉化爲b[1:j-1],然後再在結尾插入字符b[j]即可,edit[i][j]=edit[i][j-1]+1;

2)a[1:i-1]經過多次操作轉化爲b[1:j],然後再將字符a[i]刪除即可,edit[i][j]=edit[i-1][j]+1;

3)a[1:i-1]經過多次操作轉化爲b[1:j-1],然後再將字符a[i]替換爲b[j]即可,edit[i][j]=edit[i-1][j-1]+1。

在這三種情況中取最小值即可。其中edit[0][j]表示將空串轉爲b[1:j]的操作次數,爲j;edit[i][0]表示將a[1:i]轉爲空串的操作次數,爲i。

核心思想:看到這裏,應該明白了這是一個動態規劃(dynamic programming)問題。動態規劃是一種通過求子問題的最優解,從而求得原問題的最優解的方法。其中子問題稱爲狀態,由子問題推出原問題的解稱爲狀態轉移方程。這是動態規劃算法的核心。

在本問題中,狀態定義爲求edit[i][j]這樣一個二維數組,而上述的多個求解edit[i][j]的公式則爲狀態轉移方程。

該問題的動態規劃方程可表示如下:

相似度計算:求出兩個字符串a,b間的編輯距離edit(a,b)後,字符串a和字符串b的相似即可這樣計算:

similarity=(max(a,b)-edit(a,b))/max(a,b)

代碼實現:

class LevenshteinDistance(object):

    def distance(self, a, b):
        """
        計算字符串a和b之間的編輯距離
        """
        edit = [[0 for j in range(len(b) + 1)] for i in range(len(a) + 1)]
        for i in range(len(a)):
            edit[i + 1][0] = i + 1
        for j in range(len(b)):
            edit[0][j + 1] = j + 1

        for i in range(len(a) + 1)[1:]:
            for j in range(len(b) + 1)[1:]:
                flag = b[j - 1] == a[i - 1]
                num = 0 if flag else 1
                edit[i][j] = min(edit[i - 1][j] + 1, edit[i][j - 1] + 1, edit[i - 1][j - 1] + num)

        return edit[-1][-1]

    def similarity(self, a, b):
        """
        計算字符串a和b之間的相似度
        """
        m = max(len(a), len(b))
        d = self.distance(a, b)
        return (m - d) / m

測試用例:

if __name__ == '__main__':
    edit = LevenshteinDistance()
    print(edit.distance('kitten', 'sitting'))
    print(edit.similarity('kitten', 'sitting'))


結果爲:
3
0.5714285714285714

 

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