关于php的levenshtein函数能否给个通俗易懂的解释,手册看不懂!


如题,越详细越好。谢谢了!


 levenshtein("Hello World","ello World");

它只要在第二个参数添加个'H',只作了1个步骤!也当然返回'1'啦!
这个函数还是蛮简单的,可是:


 levenshtein("Hello World","ello World",10,20,30);

第3个参数:插入一个字符的代价。默认是 1。
第4个参数:替换一个字符的代价。默认是 1。
第5个参数:删除一个字符的代价。默认是 1。
它们的意义在哪?
这个例子中它分别填了10,20,30。
然后返回'30'我不懂了!
它指的'代价'是什么意思?
10,20,30它又分别代表什么意思?


 levenshtein('aaa','aab',0,1,0);

这个例子中它只需替换一次就够了,为什么返回的步骤次数是'0'?

php 字符串函数 levenshtein

老衲不是和尚 11 years, 2 months ago

@怡红公子 已经回答的很好了,我想从底层实现上补充一下题主说的 levenshtein('aaa','aab',0,1,0); 这个例子,为什么会返回0?

PHP底层使用的算法是经典的矩阵方式(稍有改动),即分别将 s1 s2 的每个字符作为矩阵的行( i[0,m] )和列( j[0,n] ),每个位置按顺序进行两两比较,如果相等则 cost=0 (因为不需要任何操作),否则 cost=1 (这个 cost=1 就是我们不传递后面3个参数时默认操作的cost了);但是,这里矩阵该项 M[i,j] 的值还不能直接就等于cost,因为要保证前面操作的传递性(比如你在前面插入了1个字符,后面的字符就要跟着往后挪一位;你在前面插删除了2个字符,后面就要跟着往前挪一位), M[i,j] 的值等于 M[i-1, j]+1 , M[i, j-1]+1 , M[i-1, j-1]+cost 三个值中最小的(3个值分别表示插入、替换、删除的代价,取最小值表示采用操作代价最小的方式)。这样直到计算到项 M[m, n] 的值,就是我们需要求的“编辑距离”了。(最后我贴出了php底层c代码对应的php实现)

levenshtein('aaa','aab',1,1,1); 的计算演示,单元格[3,3]为最终结果:

a a a
0 1 2 3
a 1 0 1 2
a 2 1 0 1
b 3 2 1 1

levenshtein('aaa','aab',0,1,0); 的计算演示,单元格[3,3]为最终结果(因为[1,1]=0, 而且插入的cost设置成了0,导致后面M[i, j-1]的结果都是0,而0是最小值,导致最终返回0):

a a a
0 1 2 3
a 1 0 0 0
a 2 0 0 0
b 3 0 0 0

这里可以看出,levenshtein后面传递的3个参数,是对应的插入、替换、删除三种操作的当前代价(即替换上面求项 M[i,j] 值的时候后面+的1),而从算法角度来说,任何操作的最小代价单位是1,那么如果我们要得到一个“合理”的返回值,就不能传递0值。传递0就可能导致返回一个不合理,或者说没有实际参考价值的结果。当然,这是跟实际采用的算法密切相关的,就本例来说,最佳操作应该是替换(将aab中的b替换为a),但是PHP采用的算法来看,取的是3种操作方式中最小的值,而且要带入传递性,导致最终结果为0。


 #php底层c代码对应的php实现
function levenshtein_php($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;
    }

    $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;
    }
    $c0 = $p1[$l2];

    return $c0;
}

echo levenshtein_php('aaa', 'aab', 1, 10, 1));

PS: 实际上有比利用矩阵结构更优的算法,就不在本题展开了。我没太多算法基础,尝试分析,欢迎批评指正!

Mouse酱 answered 11 years, 2 months ago

Your Answer