我试图在一组字段中找到几乎重复的值,以便管理员清理它们。
我符合两个条件
- 一个字符串完全包含在另一个字符串中,并且至少是其长度的 1/4
- 字符串的编辑距离小于两个字符串总长度的 5%
伪 PHP 代码:
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
}
我试图尽可能减少对相对昂贵的函数的调用stripos
,levenshtein
这大大减少了执行时间。然而,作为一个 O(n^2) 操作,这并不能扩展到更大的值集,而且似乎大量的处理时间都花在了简单地遍历数组上。
正在操作的几组值的一些属性
总计 | 弦乐 | # 每个字符串的匹配数 | | 弦乐 | 搭配火柴 | 平均 | 中位数 | 最大 | 时间(秒)| --------+-------------+---------+--------+------+ ----------+ 第844章 413 | 1.8 | 1 | 58 | 140 | 第593章 156 | 1.2 | 1 | 5 | 62 | 272 | 168 | 3.2 | 2 | 26 | 10 | 157 | 47 | 1.5 | 1 | 4 | 3.2 | 106 | 48 | 1.8 | 1 | 8 | 1.3 | 62 | 47 | 2.9 | 2 | 16 | 0.4 |
我可以做些什么来减少检查标准的时间,更重要的是,我有什么方法可以减少所需的标准检查次数(例如,通过预处理输入值),因为有这样的选择性低?
编辑:实施的解决方案
// $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
}