similar_text

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

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

描述

similar_text(string $string1, string $string2, float &$percent = null): int

此函数计算两个字符串的相似度,如 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
"similarity: $sim ($perc %)\n";
$sim = similar_text('barfoo', 'bafoobar', $perc);
echo
"similarity: $sim ($perc %)\n";

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

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

参见

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

添加备注

用户贡献的笔记 11 个笔记

111
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
?>
96
daniel dot karbach at localhorst dot tv
13 年前
请注意,此函数计算两个空字符串的相似度为 0(零)。

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

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

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

echo $percent; // 输出为 75,但在没有 strtoupper 的情况下输出为 50
21
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
18
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: ' . (similarity($str1, $str2) * 100) . '%';
?>
11
daniel at reflexionsdesign dot com
22 年前
如果性能是一个问题,您可能希望使用 levenshtein() 函数,该函数的复杂度要好得多,为 O(str1 * str2)。
14
julius at infoguiden dot no
21 年前
如果您在数据库中有保留名称,您不希望其他人使用,我发现这非常有效。
我将 strtoupper 添加到变量中以仅验证输入。考虑大小写将降低相似度。

<?php
$query
= mysql_query("select * from $table") or die("Query failed");

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
"The name you entered is too similar the reserved name &quot;".$row['reserved']."&quot;";
break;
}
}
?>
10
Paul
17 年前
similar_text 的速度问题似乎只针对长文本(> 20000 个字符)才会出现。

我发现通过在调用 similar_text 之前测试要测试的字符串是否小于 20000 个字符,我的应用程序的性能有了很大的提高。

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

> $percent: 66.666666666666671
3
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 并不是最快的方案?
希望这有帮助。
Georges
-1
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