日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 人文社科 > 生活经验 >内容正文

生活经验

Levenshtein distance 编辑距离算法

發(fā)布時(shí)間:2023/11/28 生活经验 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Levenshtein distance 编辑距离算法 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

這幾天再看 virtrual-dom,關(guān)于兩個(gè)列表的對(duì)比,講到了 Levenshtein distance 距離,周末抽空做一下總結(jié)。

Levenshtein Distance 介紹

在信息理論和計(jì)算機(jī)科學(xué)中,Levenshtein 距離是用于測(cè)量?jī)蓚€(gè)序列之間的差異量(即編輯距離)的度量。兩個(gè)字符串之間的 Levenshtein 距離定義為將一個(gè)字符串轉(zhuǎn)換為另一個(gè)字符串所需的最小編輯數(shù),允許的編輯操作是單個(gè)字符的插入,刪除或替換。

例子

‘kitten’和’sitten’之間的 Levenshtein 距離是 3,因?yàn)橐幌氯齻€(gè)編輯將一個(gè)更改為另一個(gè),并且沒(méi)有辦法用少于三個(gè)編輯來(lái)執(zhí)行操作。

  1. k?itten?sitten => 用’s’代替’k’
  2. sitt?e?n sitt?i?=> 用’i’代替’e’
  3. sittin sittin?g?在結(jié)尾插入’g’

Levenshtein Distance (編輯距離) 算法詳解

為了得到編輯距離,我們用 beauty 和 batyu 為例:

圖示如?①?單元位置是兩個(gè)單詞的第一個(gè)字符 [b] 比較得到的值,其的值有它的上方的值 (1)、它左方的值 (1) 和它左上角的值 (0) 來(lái)決定。當(dāng)單元格所在的行和列所對(duì)應(yīng)的字符相等時(shí),單元格的值為左上方的值。

否則,單元格左上角的值與其上方和左方的值進(jìn)行比較,它們之間的最小值 + 1 即是單元格的值。

圖中?①?的值由于單元格行和列相等,所以取左上角值 0。

圖中?②?的值由于單元格行列不相等,(1, 2, 0) 取最小為 0, 結(jié)果 + 1, 所以?②?值為 1。

圖示?③?的值由于單元格行列不相等,(1, 0, 2) 取最小 0, 結(jié)果 + 1, 所以?③?值為 1。

算法證明

這個(gè)算法計(jì)算的是將 s [1…i] 轉(zhuǎn)換為 t [1…j](例如將 beauty 轉(zhuǎn)換為 batyu)所需最少的操作數(shù)(也就是所謂的編輯距離),這個(gè)操作數(shù)被保存在 d [i,j](d 代表的就是上圖所示的二維數(shù)組)中。

  • 在第一行與第一列肯定是正確的,這也很好理解,例如我們將 beauty 轉(zhuǎn)換為空字符串,我們需要進(jìn)行的操作數(shù)為 beauty 的長(zhǎng)度(所進(jìn)行的操作為將 beauty 所有的字符丟棄)。
  • 我們對(duì)字符的可能操作有三種:
  • 將 s [1…n] 轉(zhuǎn)換為 t [1…m] 當(dāng)然需要將所有的 s 轉(zhuǎn)換為所有的 t,所以,d [n,m](表格的右下角)就是我們所需的結(jié)果。
  • 如果我們可以使用 k 個(gè)操作數(shù)把 s [1…i] 轉(zhuǎn)換為 t [1…j-1],我們只需要把 t [j] 加在最后面就能將 s [1…i] 轉(zhuǎn)換為 t [1…j],操作數(shù)為 k+1
  • 如果我們可以使用 k 個(gè)操作數(shù)把 s [1…i-1] 轉(zhuǎn)換為 t [1…j],我們只需要把 s [i] 從最后刪除就可以完成轉(zhuǎn)換,操作數(shù)為 k+1
  • 如果我們可以使用 k 個(gè)操作數(shù)把 s [1…i-1] 轉(zhuǎn)換為 t [1…j-1],我們只需要在需要的情況下(s [i] != t [j])把 s [i] 替換為 t [j],所需的操作數(shù)為 k+cost(cost 代表是否需要轉(zhuǎn)換,如果 s [i]==t [j],則 cost 為 0,否則為 1)。

可能的改進(jìn)

  • 現(xiàn)在的算法復(fù)雜度為 O (m*n),可以將其改進(jìn)為 O (m)。因?yàn)檫@個(gè)算法只需要上一行和當(dāng)前行被存儲(chǔ)下來(lái)就可以了。
  • 如果需要重現(xiàn)轉(zhuǎn)換步驟,我們可以把每一步的位置和所進(jìn)行的操作保存下來(lái),進(jìn)行重現(xiàn)。
  • 如果我們只需要比較轉(zhuǎn)換步驟是否小于一個(gè)特定常數(shù) k,那么只計(jì)算高寬寬為 2k+1 的矩形就可以了,這樣的話,算法復(fù)雜度可簡(jiǎn)化為 O (kl),l 代表參加對(duì)比的最短 string 的長(zhǎng)度。
  • 我們可以對(duì)三種操作(添加,刪除,替換)給予不同的權(quán)值(當(dāng)前算法均假設(shè)為 1,我們可以設(shè)添加為 1,刪除為 0,替換為 2 之類的),來(lái)細(xì)化我們的對(duì)比。
  • 如果我們將第一行的所有 cell 初始化為 0,則此算法可以用作模糊字符查詢。我們可以得到最匹配此字符串的字符串的最后一個(gè)字符的位置(index number),如果我們需要此字符串的起始位置,我們則需要存儲(chǔ)各個(gè)操作的步驟,然后通過(guò)算法計(jì)算出字符串的起始位置。
  • 這個(gè)算法不支持并行計(jì)算,在處理超大字符串的時(shí)候會(huì)無(wú)法利用到并行計(jì)算的好處。但我們也可以并行的計(jì)算 cost values(兩個(gè)相同位置的字符是否相等),然后通過(guò)此算法來(lái)進(jìn)行整體計(jì)算。
  • 如果只檢查對(duì)角線而不是檢查整行,并且使用延遲驗(yàn)證(lazy evaluation),此算法的時(shí)間復(fù)雜度可優(yōu)化為 O (m (1+d))(d 代表結(jié)果)。這在兩個(gè)字符串非常相似的情況下可以使對(duì)比速度速度大為增加。

字符串比較代碼

這一部分的代碼,參考了?https://rosettacode.org/wiki/Levenshtein_distance#ES5

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const ld = (a, b) => {let t = [], u, i, j, m = a.length, n = b.length;if (!m) return b;if (!n) return a;for (j = 0; j <= m; j++) {t[j] = j;}console.log(t);for (i = 1; i <= n; i++) {for (u = [i], j = 1; j <= m; j++) {u[j] = a[j - 1] === b[i - 1] ? t[j - 1] : Math.min(t[j - 1], t[j], u[j - 1]) + 1  // Levenshtein Distance 算法核心比較部分。}t = u;console.log(t);}return u[m];
}[['beauty', 'batyu', 3],
].forEach(function (v) {var a = v[0], b = v[1], t = v[2], d = ld(a, b);if (d !== t) {console.log('levenstein("' + a + '","' + b + '") was ' + d + ' should be ' + t);}
});// 打印出來(lái)
[ 0, 1, 2, 3, 4, 5, 6 ]
[ 1, 0, 1, 2, 3, 4, 5 ]
[ 2, 1, 1, 1, 2, 3, 4 ]
[ 3, 2, 2, 2, 2, 2, 3 ]
[ 4, 3, 3, 3, 3, 3, 2 ]
[ 5, 4, 4, 4, 3, 4, 3 ]

還原字符串

上面總結(jié)了傳統(tǒng)的計(jì)算字符串之間的差距,那么當(dāng)我們?cè)趺茨茉谟?jì)算的過(guò)程中,記錄需要轉(zhuǎn)換的步驟,并且進(jìn)行還原呢。

這里我們需要對(duì)比較的每一位的步驟有一個(gè)了解。

為了得到編輯距離,我們用 beauty 和 batyu 為例:
從上面一節(jié)的圖中可以看到,'beauty'?轉(zhuǎn)換為?''?,對(duì)一個(gè)的第一行的?[1,2,3,4,5,6],每一個(gè)步驟都相對(duì)與上一個(gè)元素新建一個(gè)元素,同理?''?轉(zhuǎn)換為?'batyu',每一個(gè)值都是相對(duì)一上一個(gè)元素的刪除步驟。

那么對(duì)角線也顯而易見(jiàn)就是先相對(duì)于替換操作。那么我們現(xiàn)在需要做的就是,記錄下相對(duì)應(yīng)的索引和元素以及需要進(jìn)行的操作,并將其保存為一個(gè)對(duì)象,每次新增的對(duì)象用數(shù)組來(lái)保存就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const actionType = {TYPE_REPLACE: 'TYPE_REPLACE',TYPE_NEW: 'TYPE_NEW',TYPE_DELETE: 'TYPE_DELETE'
}// 生成對(duì)象的方法
const patchObj = (index, type, item = '') => {return {index,type,item}
}

下面是 compare 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*** * @param { array } r 替換操作 replace* @param { array } n 新建操作 new* @param { array } d 刪除操作 delete* @param { number } i 需要轉(zhuǎn)換元素的 index* @param { number } j 需要?jiǎng)h除元素的 index* @param { string } b 比較字符串*/
const compare = (r, n, d, i, j, b) => {const min = Math.min(r.length, n.length, d.length);switch (min) {case r.length:return [...r, patchObj(i, actionType.TYPE_REPLACE, b[i])];case n.length:return [...n, patchObj(i, actionType.TYPE_NEW, b[i])];case d.length:return [...d, patchObj(j, actionType.TYPE_DELETE)];}
}

上面需要注意的是,我們一組保存了多個(gè)數(shù)組對(duì)象,不要對(duì)原數(shù)組進(jìn)行操作,每一次操作我們都需要拷貝一個(gè)新的數(shù)組對(duì)象。

具體的的 diff 代碼參考?diff 代碼

得到了,patches 對(duì)象,剩下的我們就需要 patch 了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const patch = (a, diffs) => {let aList = a.split('');let delCount = 0; // 刪除之后,后續(xù)的index計(jì)算需要加上之前刪除的數(shù)量diffs.forEach((diff) => {switch (diff.type) {case actionType.TYPE_DELETE:aList.splice(diff.index - delCount, 1);delCount++break;case actionType.TYPE_NEW:aList.splice(diff.index, 0, diff.item);break;case actionType.TYPE_REPLACE:aList.splice(diff.index, 1, diff.item);break;default:break;}})// console.log(aList.join(''))return aList.join('')
}

具體代碼參考代碼地址

參考資料

  • http://www.cnblogs.com/zhoug2020/p/4224866.html
  • https://rosettacode.org/wiki/Levenshtein_distance#ES5

總結(jié)

以上是生活随笔為你收集整理的Levenshtein distance 编辑距离算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。