PHP Conference Japan 2024

similar_text

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

similar_text計算兩個字串的相似度

描述

similar_text(字串 $string1, 字串 $string2, 浮點數 &$percent = null): 整數

此函式會計算兩個字串之間的相似度,如同 Oliver 在「Programming Classics: Implementing the World's Best Algorithms」一書中所描述(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 %)

參見

新增註解

使用者貢獻的註解 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!
10 年前
實際上,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
23 年前
如果效能是一個問題,你可能會想改用 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 年前
為了計算兩個字串之間的相似度百分比,而不依賴參數的順序並且不區分大小寫,我使用這個基於 Levenshtein 距離的函式

<?php

// 使用 levenshtein 計算字串相似度
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