Php 优化近似重复值搜索
我试图在一组字段中找到几乎重复的值,以便管理员能够清理它们 我有两个标准可以匹配Php 优化近似重复值搜索,php,optimization,string-matching,Php,Optimization,String Matching,我试图在一组字段中找到几乎重复的值,以便管理员能够清理它们 我有两个标准可以匹配 一个字符串完全包含在另一个字符串中,且至少为其长度的1/4 这些字符串的编辑距离小于两个字符串总长度的5% 伪PHP代码: foreach($values as $value){ $matches = array(); foreach($values as $match){ if( ( $value['length'] < $match['length'] &&am
foreach($values as $value){
$matches = array();
foreach($values as $match){
if(
(
$value['length'] < $match['length']
&&
$value['length'] * 4 > $match['length']
&&
stripos($match['value'], $value['value']) !== false
)
||
(
$match['length'] < $value['length']
&&
$match['length'] * 4 > $value['length']
&&
stripos($value['value'], $match['value']) !== false
)
||
(
abs($value['length'] - $match['length']) * 20 < ($value['length'] + $match['length'])
&&
0 < ($match['changes'] = levenshtein($value['value'], $match['value']))
&&
$match['changes'] * 20 <= ($value['length'] + $match['length'])
)
){
$matches[] = &$match;
}
}
// output matches for current outer loop value
}
我是否可以做其他事情来减少检查标准的时间,更重要的是,是否有任何方法可以减少所需标准检查的数量(例如,通过预处理输入值),因为存在如此低的选择性
编辑:实现的解决方案
// $values is ordered from shortest to longest string length
$values_count = count($values); // saves a ton of time, especially on linux
for($vid = 0; $vid < $values_count; $vid++){
for($mid = $vid+1; $mid < $values_count; $mid++){ // only check against longer strings
if(
(
$value['length'] * 4 > $match['length']
&&
stripos($match['value'], $value['value']) !== false
)
||
(
($match['length'] - $value['length']) * 20 < ($value['length'] + $match['length'])
&&
0 < ($changes = levenshtein($value['value'], $match['value']))
&&
$changes * 20 <= ($value['length'] + $match['length'])
)
){
// store match in both directions
$matches[$vid][$mid] = true;
$matches[$mid][$vid] = true;
}
}
}
// Sort outer array of matches alphabetically with uksort()
foreach($matches as $vid => $mids){
// sort inner array of matches by usage count with uksort()
// output matches
}
/$值按从最短到最长的字符串长度排序
$values_count=count($values);//节省大量时间,特别是在linux上
对于($vid=0;$vid<$values\u count;$vid++){
对于($mid=$vid+1;$mid<$values\u count;$mid++){//仅检查较长的字符串
如果(
(
$value['length']*4>$match['length']
&&
stripos($match['value'],$value['value'])!==false
)
||
(
($match['length']-$value['length'])*20<($value['length']+$match['length']))
&&
0<($changes=levenshtein($value['value'],$match['value']))
&&
$changes*20$mids){
//使用uksort()按使用计数对内部匹配数组进行排序
//输出匹配
}
您可以先按长度(O(N))对字符串进行排序,然后只检查较小的字符串是否为子字符串或较大的字符串,并且只检查差异不太大的字符串对中的levenshtein
您已经执行了这些检查,但现在对所有N x N对都执行了检查,而按长度预选“第一”将帮助您减少要首先检查的对。避免N x N循环,即使它只包含将失败的测试
对于子字符串匹配,您可以通过为所有较小的项创建索引来进一步改进,并在解析较大的项时相应地更新索引。索引应该可以形成一个在字母上分支的树结构,其中每个单词(字符串)形成从根到叶的路径。通过这种方式,您可以找到索引中的任何单词是否与要匹配的字符串进行比较。对于匹配字符串中的每个字符,请尝试继续树索引中的任何指针,并在索引处创建新指针。如果指针无法继续指向索引中的后续字符,则将其删除。如果有任何指针到达叶子注释时,您已找到子字符串匹配。
我认为,实现这一点并不困难,但也不是微不足道的。通过收紧内部循环,您可以立即获得100%的改进。您的结果中是否存在重复匹配
对于预处理步骤,我会经过并计算字符频率(假设您的字符集很小,如a-z0-9,鉴于您使用的是stripos,我认为这是可能的)。然后,与比较序列(昂贵)相比,比较频率(便宜)。这会给您带来误报,您可以接受,也可以插入当前必须删除的测试。值数组中的所有项都是唯一的。还有一个比较可以跳过将项与自身进行比较。我曾考虑按长度排序,但担心输出排序的额外开销,如它需要使用
usort()
。但是,由于结果集很小,与减少循环迭代所节省的时间相比,这是微不足道的。按长度排序在我的windows开发机器上几乎将时间缩短了一半,这是可以预期的。奇怪的是,在生产Linux系统上,时间最初实际上增加了2到3倍。从一个foreach
循环到for
,我每次都计算值数组的长度;计算一次并将其存储在一个变量中,而在windows上则将时间减少了一半,在linux上则减少到原来foreach
循环时间的5%。嗯,这很奇怪。在windows和linux上运行完全相同的代码?
// $values is ordered from shortest to longest string length
$values_count = count($values); // saves a ton of time, especially on linux
for($vid = 0; $vid < $values_count; $vid++){
for($mid = $vid+1; $mid < $values_count; $mid++){ // only check against longer strings
if(
(
$value['length'] * 4 > $match['length']
&&
stripos($match['value'], $value['value']) !== false
)
||
(
($match['length'] - $value['length']) * 20 < ($value['length'] + $match['length'])
&&
0 < ($changes = levenshtein($value['value'], $match['value']))
&&
$changes * 20 <= ($value['length'] + $match['length'])
)
){
// store match in both directions
$matches[$vid][$mid] = true;
$matches[$mid][$vid] = true;
}
}
}
// Sort outer array of matches alphabetically with uksort()
foreach($matches as $vid => $mids){
// sort inner array of matches by usage count with uksort()
// output matches
}