我在一个数组中有 5000 个,有时甚至更多的街道地址字符串。我想将它们与 levenshtein 进行比较以找到相似的匹配项。如果不遍历所有 5000 并将它们与其他所有 4999 直接比较,我怎么能做到这一点?
编辑:如果有人有建议,我也对替代方法感兴趣。总体目标是根据用户提交的街道地址找到相似的条目(并消除重复项)。
我在一个数组中有 5000 个,有时甚至更多的街道地址字符串。我想将它们与 levenshtein 进行比较以找到相似的匹配项。如果不遍历所有 5000 并将它们与其他所有 4999 直接比较,我怎么能做到这一点?
编辑:如果有人有建议,我也对替代方法感兴趣。总体目标是根据用户提交的街道地址找到相似的条目(并消除重复项)。
我认为对类似地址进行分组的更好方法是:
创建一个包含两个表的数据库 - 一个用于地址(和一个 id),一个用于地址中单词或文字数字的音译(使用地址表的外键)
大写地址,将 [AZ] 或 [0-9] 以外的任何内容替换为空格
按空格分割地址,计算每个“单词”的 soundex,只保留数字的任何内容,并将其与您开始使用的地址的外键一起存储在 soundexes 表中
为每个地址(id $target)找到最相似的地址:
SELECT similar.id, similar.address, count(*)
FROM adress similar, word cmp, word src
WHERE src.address_id=$target
AND src.soundex=cmp.soundex
AND cmp.address_id=similar.id
ORDER BY count(*)
LIMIT $some_value;
计算您的源地址与查询返回的前几个值之间的 levenstein 差异。
(在数据库中对大型数组进行任何类型的操作通常更快)
我认为你不能避免遍历数组,因为 levenstein() 函数只接受字符串而不是数组作为输入。
您可以执行以下操作:
for($i=0;$i<count($array)-1;$i++)
{
for($j=$i+1;$j<count($array);$j++)
{
$lev = levenshtein($array[$i],$array[$j]);
if($lev == 0)
{
// exact match
}
else if($lev <= THRESHOLD)
{
// similar
}
}
}
您可以使用bk-tree来加速搜索/比较。
http://blog.notdot.net/2007/4/Damn-Cool-Algorithms-Part-1-BK-Trees说:
现在我们可以对 Levenshtein 距离进行一个特别有用的观察:它形成了一个度量空间。
[...]
假设我们有两个参数,查询,我们在搜索中使用的字符串,以及字符串可以与查询的最大距离并且仍然被返回。假设我们采用任意字符串,对其进行测试并将其与查询进行比较。调用合成距离 d。因为我们知道三角不等式成立,所以我们所有的结果必须最多有 d+n 距离,至少距离 test 有 dn。
[...]
测试表明,搜索距离为 1 的查询不超过树的 5-8%,搜索两个错误的查询不超过树的 17-25% - 与检查每个节点相比有了很大的改进!
编辑:但这对您的(“12 Bird Road,Apt 6”和“12 Bird Rd.#6”)问题没有帮助。仅通过您的蛮力 m*n 比较。
由于 Levenshtein 算法的性质(特别是它是两个字符串之间的比较这一事实),我看不出这是怎么可能的。
您当然可以通过首先执行一些基本匹配要求来减少比较次数,但这超出了您所要求的范围。
作为一个(很可能不相关的)选项,您总是可以使用类似的东西soundex
,它可以让您预先计算字符串值。(我相信你也可以直接在 MySQL 中使用它。)
您可以根据 soundexes 对它们进行分组,然后将比较限制为最接近的 N 个案例...
$mashed=array();
foreach ($address as $key=>$val) {
$mashed[$key]=soundex($val);
}
sort($mashed);
然后遍历 $mashed 的键。
C。
鉴于您的问题,如果您想使用Lehvenstein distance ,除了将每个地址与每个其他地址进行比较之外,我别无他法。
首先,您应该规范化地址,摆脱缩写等。
对于类似的地址,您可以有一些固定的最大 Lehvenstein 距离 ( N )。
如果是这样,当您确定当前地址对的编辑距离大于 N 时,您可以中止 Lehvenstein 算法。为此,您需要编写 Lehvenstein 算法的自定义版本。这将使算法更快一点。
还有一些相关的琐碎优化。例如:如果地址 A 的长度为 10 个字符,地址 B 的长度为 20 个字符,并且您认为 Lehvenstein 距离小于 8 的地址是相似的。您可以查看地址的长度并立即确定它们不相似。
如果要查找所有相似的值,则必须将所有项目与所有其他项目进行比较。但是选择正确的数组函数会显着加快速度。这是一个简单的示例(结果数组可能会更好):
$results = array();
$count = count($entries);
while ($count != 0) {
# The entry to process
$entry = array_shift($entries);
# Get levenshtein distances to all others
$result = array_map(
'levenshtein',
# array_map() needs two arrays, this one is an array consisting of
# multiple entries of the value that we are processing
array_fill($entry, 0, $count),
$toCompare
);
$results[] = array($entry => $result);
$count--;
}
$stringA = "this is php programming language";
$stringB = "this is complete programming script in which java php and all other minor languages include";
echo "string 1---->".$stringA."<br />";
echo "string 2---->".$stringB."<br />";
// changing string to arrays
$array1 = explode(' ', $stringA);
$array2 = explode(' ', $stringB);
// getting same element from two strings
$c = array_intersect($array1, $array2);
// changing array to the string
$d=implode(' ',$c);
echo "string same elements---> ".$d."<br />";
// getting difrent element from two arrays
$result = array_diff($array2, $array1);
// changing array to the string
$zem = implode(' ',$result);
if (!empty($zem)) {
echo "string diffrence---> ".$zem."<br />";
} else {
echo "string diffrence--->both strings are same <br />";
}
similar_text($stringA, $d, $p);
echo " similarity between the string is ".$p."% <br />";