PHP Conference Japan 2024

similar_text

(PHP 4, PHP 5, PHP 7, PHP 8)

similar_text计算两个字符串之间的相似度

描述

similar_text(字符串 $string1, 字符串 $string2, 浮点数 &$percent = null): 整数

此函数根据 Oliver 编著的《编程经典:实现世界最佳算法》(ISBN 0-131-00413-1)中所述方法,计算两个字符串之间的相似度。请注意,此实现未使用 Oliver 的伪代码中的堆栈,而是使用了递归调用,这可能会加快或减慢整个过程。另请注意,此算法的复杂度为 O(N**3),其中 N 是最长字符串的长度。

参数

string1

第一个字符串。

string2

第二个字符串。

注意:

交换 string1string2 可能会产生不同的结果;请参见下面的示例。

percent

通过将引用作为第三个参数传递,similar_text() 将计算相似度百分比,方法是将 similar_text() 的结果除以给定字符串长度的平均值再乘以 100

返回值

返回两个字符串中匹配字符的数量。

匹配字符的数量是通过查找最长的第一个公共子字符串,然后对前缀和后缀递归执行此操作来计算的。所有找到的公共子字符串的长度相加。

示例

示例 #1 similar_text() 参数交换示例

此示例显示交换 string1string2 参数可能会产生不同的结果。

<?php
$sim
= similar_text('bafoobar', 'barfoo', $perc);
echo
"相似度:$sim ($perc %)\n";
$sim = similar_text('barfoo', 'bafoobar', $perc);
echo
"相似度:$sim ($perc %)\n";

以上示例将输出类似以下内容

similarity: 5 (71.428571428571 %)
similarity: 3 (42.857142857143 %)

参见

  • levenshtein() - 计算两个字符串之间的 Levenshtein 距离
  • soundex() - 计算字符串的 Soundex 键

添加注释

用户贡献的注释 11 条注释

SPAM HATER
12 年前
大家好,

使用此函数时请注意,如果您想计算相似度百分比,则传递字符串的顺序非常重要,事实上,更改变量会产生非常不同的结果,例如

<?php
$var_1
= 'PHP IS GREAT';
$var_2 = 'WITH MYSQL';

similar_text($var_1, $var_2, $percent);

echo
$percent;
// 27.272727272727

similar_text($var_2, $var_1, $percent);

echo
$percent;
// 18.181818181818
?>
daniel dot karbach at localhorst dot tv
13 年前
请注意,此函数计算两个空字符串的相似度为 0(零)。

<?php
similar_text
("", "", $sim);
echo
$sim; // "0"
?>
I_HATE_SPAMMER- PAZ!
10 年前
实际上 similar_text() 并不差...
它运行良好。但在处理之前,我认为进行如下小的修改是一个好方法

$var_1 = strtoupper("doggy");
$var_2 = strtoupper("Dog");

similar_text($var_1, $var_2, $percent);

echo $percent; // 输出为 75,但如果不使用 strtoupper 则输出为 50
ryan at derokorian dot com
10 年前
请注意,此函数区分大小写

<?php

$var1
= 'Hello';
$var2 = 'Hello';
$var3 = 'hello';

echo
similar_text($var1, $var2); // 5
echo similar_text($var1, $var3); // 4
vasyl at vasyltech dot com
8 年前
递归算法通常非常优雅。我找到了一种无需递归即可获得更高精度的方案。想象一下两条不同(或相同)长度的丝带,每条丝带上都有字母。您只需将一条丝带向左移动,直到它与第一条丝带上的字母匹配。

<?php

function similarity($str1, $str2) {
$len1 = strlen($str1);
$len2 = strlen($str2);

$max = max($len1, $len2);
$similarity = $i = $j = 0;

while ((
$i < $len1) && isset($str2[$j])) {
if (
$str1[$i] == $str2[$j]) {
$similarity++;
$i++;
$j++;
} elseif (
$len1 < $len2) {
$len1++;
$j++;
} elseif (
$len1 > $len2) {
$i++;
$len1--;
} else {
$i++;
$j++;
}
}

return
round($similarity / $max, 2);
}

$str1 = '12345678901234567890';
$str2 = '12345678991234567890';

echo
'相似度: ' . (similarity($str1, $str2) * 100) . '%';
?>
daniel at reflexionsdesign dot com
23年前
如果性能是一个问题,你可能希望使用levenshtein()函数代替,它具有更好的复杂度O(str1 * str2)。
julius at infoguiden dot no
21年前
如果你在数据库中有保留名称,你不希望其他人使用,我发现这工作得很好。
我将strtoupper添加到变量中以仅验证输入。考虑大小写会降低相似度。

<?php
$query
= mysql_query("select * from $table") or die("查询失败");

while (
$row = mysql_fetch_array($query)) {
similar_text(strtoupper($_POST['name']), strtoupper($row['reserved']), $similarity_pst);
if (
number_format($similarity_pst, 0) > 90){
$too_similar = $row['reserved'];
print
"您输入的名称与保留名称&quot;".$row['reserved']."&quot;太相似了";
break;
}
}
?>
Paul
17年前
similar_text的速度问题似乎仅在较长的文本段(>20000个字符)中才会出现。

我发现,在我的应用程序中,在调用similar_text之前,只需测试要测试的字符串是否小于20000个字符,就可以大幅提高性能。

20000+需要3-5秒才能处理,其他任何内容(10000及以下)都只需花费一小部分时间。
幸运的是,对于我来说,只有少数几个实例超过20000个字符,我无法获得比较百分比。
匿名用户
4年前
$result = similar_text ('ab', 'a', $percent);

> $percent: 66.666666666666671
georgesk at hotmail dot com
22年前
好吧,如上所述,速度为O(N^3),我做了一个最长公共子序列方法,它是O(m.n),其中m和n分别是str1和str2的长度,结果是一个百分比,它似乎与similar_text百分比完全相同,但性能更好……这是我使用的3个函数……

<?php
function LCS_Length($s1, $s2)
{
$m = strlen($s1);
$n = strlen($s2);

//此表格用于计算LCS长度,每个字符串仅考虑128个字符
$LCS_Length_Table = array(array(128),array(128));


//重置表格中的两列
for($i=1; $i < $m; $i++) $LCS_Length_Table[$i][0]=0;
for(
$j=0; $j < $n; $j++) $LCS_Length_Table[0][$j]=0;

for (
$i=1; $i <= $m; $i++) {
for (
$j=1; $j <= $n; $j++) {
if (
$s1[$i-1]==$s2[$j-1])
$LCS_Length_Table[$i][$j] = $LCS_Length_Table[$i-1][$j-1] + 1;
else if (
$LCS_Length_Table[$i-1][$j] >= $LCS_Length_Table[$i][$j-1])
$LCS_Length_Table[$i][$j] = $LCS_Length_Table[$i-1][$j];
else
$LCS_Length_Table[$i][$j] = $LCS_Length_Table[$i][$j-1];
}
}
return
$LCS_Length_Table[$m][$n];
}

function
str_lcsfix($s)
{
$s = str_replace(" ","",$s);
$s = ereg_replace("[��������]","e", $s);
$s = ereg_replace("[������������]","a", $s);
$s = ereg_replace("[��������]","i", $s);
$s = ereg_replace("[���������]","o", $s);
$s = ereg_replace("[��������]","u", $s);
$s = ereg_replace("[�]","c", $s);
return
$s;
}

function
get_lcs($s1, $s2)
{
//好的,现在用空字符串替换所有空格
$s1 = strtolower(str_lcsfix($s1));
$s2 = strtolower(str_lcsfix($s2));

$lcs = LCS_Length($s1,$s2); //最长公共子序列

$ms = (strlen($s1) + strlen($s2)) / 2;

return ((
$lcs*100)/$ms);
}
?>

如果您不担心重音字符之类的问题,可以跳过调用str_lcsfix,或者可以对其进行补充或修改以提高性能,我认为ereg不是最快的方法?
希望这有帮助。
乔治斯
pablo dot pazos at cabolabs dot com
3 年前
为了计算两个字符串之间的相似度百分比,而不依赖于参数的顺序并且不区分大小写,我使用基于莱文斯坦距离的这个函数

<?php

// 使用莱文斯坦距离计算字符串相似度
static function similarity($a, $b)
{
return
1 - (levenshtein(strtoupper($a), strtoupper($b)) / max(strlen($a), strlen($b)));
}

?>

这将始终返回 0 到 1 之间的一个数字,表示百分比,例如 0.8 表示 80% 相似的字符串。

如果希望区分大小写,只需删除 strtoupper() 函数即可。
To Top