我正在尝试确定两个不同的餐馆名称是否相似,以便能够匹配它们。名称可能拼写错误或标题的部分顺序错误。
在某些情况下,匹配很简单: “愤怒的晚餐”与“愤怒的晚餐餐厅”。 要么 “汉堡王”与“伯格尔国王”
我发现的一个更难的案例是: “Mathias Dahlgren Matbaren”和“Restaurant Mathias Dahlgren”
我已经研究了几种不同的模糊字符串差异算法,但没有找到一个用于此用例的算法。
有谁知道我可以使用的算法和/或库?
答案 0 :(得分:2)
首先:如果您不仅仅是要匹配的名称(例如地址),您将获得更好的结果。然后,您可以使用记录链接引擎来考虑来自所有属性的证据。在大多数情况下,只使用名称会降低精度。
您需要考虑的第一件事是,您是否可能会看到子串的重新排序。也就是说,“餐厅愤怒的晚餐”与“愤怒的晚餐餐厅”。在这种情况下,q-gram,最长公共子串和最长公共子序列都是很好的候选者。对于q-gram,您可以在各种子公式和匹配项之间进行选择。
如果您希望订单重要,那么仿射差距可能对此特别有用。它类似于史密斯 - 沃特曼,但对于删除并没有那么多的惩罚。基本上,第一次删除是昂贵的,但后来删除在同一个地方的成本更低。
正如其他人所建议的那样,在匹配之前删除像“restaurant”,“matbaren”等常用词可能会提高准确性。
有大量的库,但由于你没有指定编程语言,所以很难推荐一个。如果使用PHP,Java有什么用?反之亦然?
但是请仔细注意我上面写的内容:仅凭名称不会很好。即使名称相同,它仍然可能是两个完全不同的餐厅。
答案 1 :(得分:0)
您可以尝试diff算法。它创建所有可能的字符串并找到最长的公共子序列。
Well, as mentioned above the speed is O(N^3), i've done a longest common subsequence way that is O(m.n) where m and n are the length of str1 and str2, the result is a percentage and it seems to be exactly the same as similar_text percentage but with better performance... here's the 3 functions i'm using..
<?php
function LCS_Length($s1, $s2)
{
$m = strlen($s1);
$n = strlen($s2);
//this table will be used to compute the LCS-Length, only 128 chars per string are considered
$LCS_Length_Table = array(array(128),array(128));
//reset the 2 cols in the table
for($i=1; $i < $m; $i++) $LCS_Length_Table[$i][0]=0;
for($j=0; $j < $n; $j++) $LCS_Length_Table[0][$j]=0;
for ($i=1; $i <= $m; $i++) {
for ($j=1; $j <= $n; $j++) {
if ($s1[$i-1]==$s2[$j-1])
$LCS_Length_Table[$i][$j] = $LCS_Length_Table[$i-1][$j-1] + 1;
else if ($LCS_Length_Table[$i-1][$j] >= $LCS_Length_Table[$i][$j-1])
$LCS_Length_Table[$i][$j] = $LCS_Length_Table[$i-1][$j];
else
$LCS_Length_Table[$i][$j] = $LCS_Length_Table[$i][$j-1];
}
}
return $LCS_Length_Table[$m][$n];
}
function str_lcsfix($s)
{
$s = str_replace(" ","",$s);
$s = ereg_replace("[��������]","e", $s);
$s = ereg_replace("[������������]","a", $s);
$s = ereg_replace("[��������]","i", $s);
$s = ereg_replace("[���������]","o", $s);
$s = ereg_replace("[��������]","u", $s);
$s = ereg_replace("[�]","c", $s);
return $s;
}
function get_lcs($s1, $s2)
{
//ok, now replace all spaces with nothing
$s1 = strtolower(str_lcsfix($s1));
$s2 = strtolower(str_lcsfix($s2));
$lcs = LCS_Length($s1,$s2); //longest common sub sequence
$ms = (strlen($s1) + strlen($s2)) / 2;
return (($lcs*100)/$ms);
}
?>
you can skip calling str_lcsfix if you don't worry about accentuated characters and things like that or you can add up to it or modify it for faster performance, i think ereg is not the fastest way?
hope this helps.
Georges
答案 2 :(得分:0)
我认为最佳拟合算法是最佳局部对齐的算法:Smith-Waterman-Algorithm
penalty("Angry Diner","Angry Diner Restaurant") = 0
penalty("Burger King", "Burgor King") = 1
penalty("Mathias Dahlgren Matbaren", "Restaurant Mathias Dahlgren") = 0
它是Levensthein算法的变体,不同之处在于开头/结尾的字符插入/删除不会受到惩罚。