PHP – 优化 – 具有优先级的Levenshtein距离
|
我试图用一个小插件来实现 levenshtein algorithm.我想优先考虑具有连续匹配字母的值.我已经尝试使用下面的代码实现我自己的形式: function levenshtein_rating($string1,$string2) {
$GLOBALS['lvn_memo'] = array();
return lev($string1,strlen($string1),$string2,strlen($string2));
}
function lev($s1,$s1x,$s1l,$s2,$s2x,$s2l,$cons = 0) {
$key = $s1x . "," . $s1l . "," . $s2x . "," . $s2l;
if (isset($GLOBALS['lvn_memo'][$key])) return $GLOBALS['lvn_memo'][$key];
if ($s1l == 0) return $s2l;
if ($s2l == 0) return $s1l;
$cost = 0;
if ($s1[$s1x] != $s2[$s2x]) $cost = 1;
else $cons -= 0.1;
$dist = min(
(lev($s1,$s1x + 1,$s1l - 1,$cons) + 1),(lev($s1,$s2x + 1,$s2l - 1,$cons) + $cost)
);
$GLOBALS['lvn_memo'][$key] = $dist + $cons;
return $dist + $cons;
}
你应该注意$cons – = 0.1;是我添加值以确定连续值的优先级的部分.该公式将针对大型字符串数据库进行检查. (高达20,000 – 50,000)我已经使用PHP内置的levenshtein进行了基准测试 Message Time Change Memory
PHP N/A 9300128
End PHP 1ms 9300864
End Mine 20ms 9310736
Array
(
[0] => 3
[1] => 3
[2] => 0
)
Array
(
[0] => 2.5
[1] => 1.9
[2] => -1.5
)
基准测试代码: $string1 = "kitten";
$string2 = "sitter";
$string3 = "sitting";
$log = new Logger("PHP");
$distances = array();
$distances[] = levenshtein($string1,$string3);
$distances[] = levenshtein($string2,$string3);
$distances[] = levenshtein($string3,$string3);
$log->log("End PHP");
$distances2 = array();
$distances2[] = levenshtein_rating($string1,$string3);
$distances2[] = levenshtein_rating($string2,$string3);
$distances2[] = levenshtein_rating($string3,$string3);
$log->log("End Mine");
echo $log->status();
echo "<pre>" . print_r($distances,true) . "</pre>";
echo "<pre>" . print_r($distances2,true) . "</pre>";
我认识到PHP的内置功能可能总是比我的本质更快.但我想知道是否有办法加快我的速度? 所以问题是:有没有办法加快速度?我在这里的另一种选择是运行levenshtein,然后搜索最高的X结果并另外优先考虑它们. 根据Leigh的评论,复制PHP内置的Levenhstein形式将时间缩短到3ms. (编辑:发布连续字符扣除的版本.这可能需要调整,似乎工作.) function levenshtein_rating($s1,$cons = 0,$cost_ins = 1,$cost_rep = 1,$cost_del = 1) {
$s1l = strlen($s1);
$s2l = strlen($s2);
if ($s1l == 0) return $s2l;
if ($s2l == 0) return $s1l;
$p1 = array();
$p2 = array();
for ($i2 = 0; $i2 <= $s2l; ++$i2) {
$p1[$i2] = $i2 * $cost_ins;
}
$cons = 0;
$cons_count = 0;
$cln = 0;
$tbl = $s1;
$lst = false;
for ($i1 = 0; $i1 < $s1l; ++$i1) {
$p2[0] = $p1[0] + $cost_del;
$srch = true;
for($i2 = 0; $i2 < $s2l; ++ $i2) {
$c0 = $p1[$i2] + (($s1[$i1] == $s2[$i2]) ? 0 : $cost_rep);
if ($srch && $s2[$i2] == $tbl[$i1]) {
$tbl[$i1] = " ";
$srch = false;
$cln += ($cln == 0) ? 1 : $cln * 1;
}
$c1 = $p1[$i2 + 1] + $cost_del;
if ($c1 < $c0) $c0 = $c1;
$c2 = $p2[$i2] + $cost_ins;
if ($c2 < $c0) $c0 = $c2;
$p2[$i2 + 1] = $c0;
}
if (!$srch && $lst) {
$cons_count += $cln;
$cln = 0;
}
$lst = $srch;
$tmp = $p1;
$p1 = $p2;
$p2 = $tmp;
}
$cons_count += $cln;
$cons = -1 * ($cons_count * 0.1);
return $p1[$s2l] + $cons;
}
我认为你的功能的主要放缓是它是递归的.
正如我在评论中所说的那样,PHP函数调用对于引擎来说是非常繁重的工作. PHP本身将levenshtein实现为循环,保持插入,替换和删除所产生的成本的总计. 我敢肯定,如果你将代码转换为循环,你会看到一些大规模的性能提升. 我不确切知道你的代码在做什么,但是我已经将原生C代码移植到PHP中,为你提供了一个起点. define('LEVENSHTEIN_MAX_LENGTH',12);
function lev2($s1,$cost_del = 1)
{
$l1 = strlen($s1);
$l2 = strlen($s2);
if ($l1 == 0) {
return $l2 * $cost_ins;
}
if ($l2 == 0) {
return $l1 * $cost_del;
}
if (($l1 > LEVENSHTEIN_MAX_LENGTH) || ($l2 > LEVENSHTEIN_MAX_LENGTH)) {
return -1;
}
$p1 = array();
$p2 = array();
for ($i2 = 0; $i2 <= $l2; $i2++) {
$p1[$i2] = $i2 * $cost_ins;
}
for ($i1 = 0; $i1 < $l1; $i1++) {
$p2[0] = $p1[0] + $cost_del;
for ($i2 = 0; $i2 < $l2; $i2++) {
$c0 = $p1[$i2] + (($s1[$i1] == $s2[$i2]) ? 0 : $cost_rep);
$c1 = $p1[$i2 + 1] + $cost_del;
if ($c1 < $c0) {
$c0 = $c1;
}
$c2 = $p2[$i2] + $cost_ins;
if ($c2 < $c0) {
$c0 = $c2;
}
$p2[$i2 + 1] = $c0;
}
$tmp = $p1;
$p1 = $p2;
$p2 = $tmp;
}
return $p1[$l2];
}
我做了一个快速的基准测试,比较你的,我的和PHP的内部函数,每个100,000次迭代,时间以秒为单位. float(12.954766988754) float(2.4660499095917) float(0.14857912063599) 显然它还没有进行调整,但我相信它们不会减慢它的速度. 如果你真的需要更多的速度提升,一旦你弄清楚如何改变这个功能,它应该很容易将你的更改移植回C,复制PHP函数定义,并实现你自己的本机C版本你修改过的功能. 有很多关于如何进行PHP扩展的教程,所以如果你决定沿着那条路走下去,你不应该有那么多困难. 编辑: 我注意到,正在寻找进一步改善它的方法 $c0 = $p1[$i2] + (($s1[$i1] == $s2[$i2]) ? 0 : $cost_rep);
$c1 = $p1[$i2 + 1] + $cost_del;
if ($c1 < $c0) {
$c0 = $c1;
}
$c2 = $p2[$i2] + $cost_ins;
if ($c2 < $c0) {
$c0 = $c2;
}
是相同的 $c0 = min(
$p1[$i2 + 1] + $cost_del,$p1[$i2] + (($s1[$i1] == $s2[$i2]) ? 0 : $cost_rep),$c2 = $p2[$i2] + $cost_ins
);
我认为这与代码中的min块直接相关.但是,这会显着减慢代码速度. (我猜它是额外函数调用的开销) 使用min()块作为第二个时序的基准. float(2.484846830368) float(3.6055288314819) 你是对的第二个$cost_ins不属于 – 我的复制/粘贴失败. (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
