gpt4 book ai didi

PHP - 优化 - 具有优先级的 Levenshtein 距离

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:41:51 30 4
gpt4 key购买 nike

我正在尝试实现 levenshtein algorithm有一个小插件。我想优先考虑具有连续匹配字母的值。我尝试使用以下代码实现我自己的形式:

function levenshtein_rating($string1, $string2) {
$GLOBALS['lvn_memo'] = array();
return lev($string1, 0, strlen($string1), $string2, 0, 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, $s2, $s2x, $s2l, $cons) + 1),
(lev($s1, $s1x, $s1l, $s2, $s2x + 1, $s2l - 1, $cons) + 1),
(lev($s1, $s1x + 1, $s1l - 1, $s2, $s2x + 1, $s2l - 1, $cons) + $cost)
);
$GLOBALS['lvn_memo'][$key] = $dist + $cons;
return $dist + $cons;
}

您应该注意 $cons -= 0.1; 是我添加一个值以优先考虑连续值的部分。该公式将针对大型字符串数据库进行检查。 (高达 20,000 - 50,000)我用 levenshtein

中内置的 PHP 完成了基准测试
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 的评论,复制以 Levenhstein 形式构建的 PHP 可将时间缩短至 3 毫秒。 (编辑:发布了带有连续字符扣除的版本。这可能需要调整,似乎可以工作。)

function levenshtein_rating($s1, $s2, $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] = "\0";
$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 实现为一个循环,保持插入、替换和删除所产生的总成本。

我敢肯定,如果您也将代码转换为循环,您会看到性能大幅提升。

我不确切知道您的代码在做什么,但我已将 native C 代码移植到 PHP 以给您一个起点。

define('LEVENSHTEIN_MAX_LENGTH', 12);

function lev2($s1, $s2, $cost_ins = 1, $cost_rep = 1, $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 函数定义,并实现你自己的 native 您修改后的函数的 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 block 直接相关。但是,这会显着减慢代码。 (我猜这是额外函数调用的开销)

min() block 作为第二个计时的基准。

float(2.484846830368)
float(3.6055288314819)

关于第二个 $cost_ins 不属于你是对的 - 复制/粘贴对我来说失败了。

关于PHP - 优化 - 具有优先级的 Levenshtein 距离,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14525743/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com