修改 Levenshtein-Distance 以忽略顺序
Modify Levenshtein-Distance to ignore order
我想计算最多包含 6 个值的序列之间的编辑距离。 这些值的顺序不应影响距离。
我如何将其实现到迭代或递归算法中?
示例:
# Currently
>>> LDistance('dog', 'god')
2
# Sorted
>>> LDistance('dgo', 'dgo')
0
# Proposed
>>> newLDistance('dog', 'god')
0
'dog' 和 'god' 具有完全相同的字母,预先对字符串进行排序将 return 得到所需的结果。然而,这并不总是有效:
# Currently
>>> LDistance('doge', 'gold')
3
# Sorted
>>> LDistance('dego', 'dglo')
2
# Proposed
>>> newLDistance('doge', 'gold')
1
'doge' 和 'gold' 有 3/4 个匹配的字母,因此 return 的距离应该为 1。
这是我当前的递归代码:
def mLD(s, t):
memo = {}
def ld(s, t):
if not s: return len(t)
if not t: return len(s)
if s[0] == t[0]: return ld(s[1:], t[1:])
if (s, t) not in memo:
l1 = ld(s, t[1:])
l2 = ld(s[1:], t)
l3 = ld(s[1:], t[1:])
memo[(s,t)] = 1 + min(l1, l2, l3)
return memo[(s,t)]
return ld(s, t)
编辑:后续问题:
你不需要 Levenshtein 机器。
import collections
def distance(s1, s2):
cnt = collections.Counter()
for c in s1:
cnt[c] += 1
for c in s2:
cnt[c] -= 1
return sum(abs(diff) for diff in cnt.values()) // 2 + \
(abs(sum(cnt.values())) + 1) // 2 # can be omitted if len(s1) == len(s2)
为什么不只计算有多少个字母是相同的,然后从中找出答案呢?对于每个字符计算它的频率,然后对于每个字符串根据频率计算它有多少 "extra" 个字符,并取这些 "extra".
中的最大值
伪代码:
for c in s1:
cnt1[c]++
for c in s2:
cnt2[c]++
extra1 = 0
extra2 = 0
for c in all_chars:
if cnt1[c]>cnt2[c]
extra1 += cnt1[c]-cnt2[c]
else
extra2 += cnt2[c]-cnt1[c]
return max(extra1, extra2)
这可能会晚一些,但我认为它可以帮助某些人,而且我也在寻求改进。
我遇到的挑战是:
match_function('kigali rwanda','rwanda kigali') 可能的匹配百分比应为 100%
match_function('kigali','ligaki') 可能的匹配百分比应为 +50%
...
我使用交叉连接和 Levenstein 在 T-SQL 中写了一个有趣的函数,它在某些时候有所帮助,但我仍然需要改进:
Create FUNCTION [dbo].[GetPercentageMatch](@left VARCHAR(100),@right VARCHAR(100))
RETURNS DECIMAL
AS
BEGIN
DECLARE @returnvalue DECIMAL(5, 2);
DECLARE @list1 TABLE(value VARCHAR(50));
declare @count1 int, @count2 int, @matchPerc int;
INSERT INTO @list1 (value) select value from STRING_SPLIT(@left, ' ');
DECLARE @list2 TABLE(value VARCHAR(50));
INSERT INTO @list2 (value) select * from STRING_SPLIT(@right, ' ');
select @count1 = count(*) from @list1
select @count2 = count(*) from @list2
select @matchPerc = (r3.percSum/case when @count1 > @count2 then @count1 else @count2 end) from (
select count(r2.l1) rCount, sum(r2.perc) percSum from(
select r.t1, r.t2, r.distance, (100-((r.distance*100)/(case when len(r.t1) > len(r.t2) then len(r.t1) else len(r.t2) end))) perc, len(r.t1) l1,len(r.t2)l2 from
(select
isnull(t1.value,'') t1,
isnull(t2.value,'') t2,
[dbo].[LEVENSHTEIN](isnull(t1.value,''),isnull(t2.value,'')) distance
from @list1 t1 cross join @list2 t2 ) as r
) r2
) r3
return case when @matchPerc > 100 then 100 else @matchPerc end
END;
我想计算最多包含 6 个值的序列之间的编辑距离。 这些值的顺序不应影响距离。
我如何将其实现到迭代或递归算法中?
示例:
# Currently
>>> LDistance('dog', 'god')
2
# Sorted
>>> LDistance('dgo', 'dgo')
0
# Proposed
>>> newLDistance('dog', 'god')
0
'dog' 和 'god' 具有完全相同的字母,预先对字符串进行排序将 return 得到所需的结果。然而,这并不总是有效:
# Currently
>>> LDistance('doge', 'gold')
3
# Sorted
>>> LDistance('dego', 'dglo')
2
# Proposed
>>> newLDistance('doge', 'gold')
1
'doge' 和 'gold' 有 3/4 个匹配的字母,因此 return 的距离应该为 1。 这是我当前的递归代码:
def mLD(s, t):
memo = {}
def ld(s, t):
if not s: return len(t)
if not t: return len(s)
if s[0] == t[0]: return ld(s[1:], t[1:])
if (s, t) not in memo:
l1 = ld(s, t[1:])
l2 = ld(s[1:], t)
l3 = ld(s[1:], t[1:])
memo[(s,t)] = 1 + min(l1, l2, l3)
return memo[(s,t)]
return ld(s, t)
编辑:后续问题:
你不需要 Levenshtein 机器。
import collections
def distance(s1, s2):
cnt = collections.Counter()
for c in s1:
cnt[c] += 1
for c in s2:
cnt[c] -= 1
return sum(abs(diff) for diff in cnt.values()) // 2 + \
(abs(sum(cnt.values())) + 1) // 2 # can be omitted if len(s1) == len(s2)
为什么不只计算有多少个字母是相同的,然后从中找出答案呢?对于每个字符计算它的频率,然后对于每个字符串根据频率计算它有多少 "extra" 个字符,并取这些 "extra".
中的最大值伪代码:
for c in s1:
cnt1[c]++
for c in s2:
cnt2[c]++
extra1 = 0
extra2 = 0
for c in all_chars:
if cnt1[c]>cnt2[c]
extra1 += cnt1[c]-cnt2[c]
else
extra2 += cnt2[c]-cnt1[c]
return max(extra1, extra2)
这可能会晚一些,但我认为它可以帮助某些人,而且我也在寻求改进。 我遇到的挑战是:
match_function('kigali rwanda','rwanda kigali') 可能的匹配百分比应为 100%
match_function('kigali','ligaki') 可能的匹配百分比应为 +50% ... 我使用交叉连接和 Levenstein 在 T-SQL 中写了一个有趣的函数,它在某些时候有所帮助,但我仍然需要改进:
Create FUNCTION [dbo].[GetPercentageMatch](@left VARCHAR(100),@right VARCHAR(100)) RETURNS DECIMAL AS BEGIN DECLARE @returnvalue DECIMAL(5, 2); DECLARE @list1 TABLE(value VARCHAR(50)); declare @count1 int, @count2 int, @matchPerc int; INSERT INTO @list1 (value) select value from STRING_SPLIT(@left, ' '); DECLARE @list2 TABLE(value VARCHAR(50)); INSERT INTO @list2 (value) select * from STRING_SPLIT(@right, ' '); select @count1 = count(*) from @list1 select @count2 = count(*) from @list2 select @matchPerc = (r3.percSum/case when @count1 > @count2 then @count1 else @count2 end) from ( select count(r2.l1) rCount, sum(r2.perc) percSum from( select r.t1, r.t2, r.distance, (100-((r.distance*100)/(case when len(r.t1) > len(r.t2) then len(r.t1) else len(r.t2) end))) perc, len(r.t1) l1,len(r.t2)l2 from (select isnull(t1.value,'') t1, isnull(t2.value,'') t2, [dbo].[LEVENSHTEIN](isnull(t1.value,''),isnull(t2.value,'')) distance from @list1 t1 cross join @list2 t2 ) as r ) r2 ) r3 return case when @matchPerc > 100 then 100 else @matchPerc end END;