本文目录导读:

- 莱文斯坦距离(Levenshtein Distance)
- similar_text 函数
- 余弦相似度(Cosine Similarity)
- Jaccard 相似度
- 使用第三方库(推荐)
- 完整示例:文本比较系统
- 性能优化建议
- 选择合适的算法
- 注意事项
在PHP项目中实现相似度计算,主要可以通过以下几种常见方法,我会从基础到高级逐步介绍:
莱文斯坦距离(Levenshtein Distance)
PHP内置函数,计算两个字符串之间的编辑距离。
<?php
// 基础用法
$text1 = "hello world";
$text2 = "hallo world";
$distance = levenshtein($text1, $text2);
echo "编辑距离: " . $distance; // 输出: 1
// 转换为相似度百分比
function levenshteinSimilarity($str1, $str2) {
$distance = levenshtein($str1, $str2);
$maxLen = max(strlen($str1), strlen($str2));
if ($maxLen == 0) return 1; // 两个空字符串完全相似
return 1 - ($distance / $maxLen);
}
echo "相似度: " . levenshteinSimilarity($text1, $text2) * 100 . "%";
similar_text 函数
PHP内置函数,基于字符匹配。
<?php $text1 = "PHP is great"; $text2 = "PHP is good"; $percent = 0; similar_text($text1, $text2, $percent); echo "相似度: " . round($percent, 2) . "%"; // 输出: 66.67%
余弦相似度(Cosine Similarity)
适用于文本向量化后的比较,特别适合TF-IDF场景。
<?php
function cosineSimilarity($text1, $text2) {
// 分词和获取词频
$words1 = array_count_values(str_word_count(strtolower($text1), 1));
$words2 = array_count_values(str_word_count(strtolower($text2), 1));
// 获取所有唯一词
$allWords = array_unique(array_merge(array_keys($words1), array_keys($words2)));
// 构建向量
$dotProduct = 0;
$magnitude1 = 0;
$magnitude2 = 0;
foreach ($allWords as $word) {
$count1 = $words1[$word] ?? 0;
$count2 = $words2[$word] ?? 0;
$dotProduct += $count1 * $count2;
$magnitude1 += $count1 * $count1;
$magnitude2 += $count2 * $count2;
}
$magnitude1 = sqrt($magnitude1);
$magnitude2 = sqrt($magnitude2);
if ($magnitude1 == 0 || $magnitude2 == 0) return 0;
return $dotProduct / ($magnitude1 * $magnitude2);
}
$text1 = "I love programming in PHP";
$text2 = "I love coding in PHP";
echo "余弦相似度: " . round(cosineSimilarity($text1, $text2) * 100, 2) . "%";
Jaccard 相似度
基于集合的交集与并集。
<?php
function jaccardSimilarity($text1, $text2) {
$words1 = array_unique(str_word_count(strtolower($text1), 1));
$words2 = array_unique(str_word_count(strtolower($text2), 1));
$intersection = array_intersect($words1, $words2);
$union = array_unique(array_merge($words1, $words2));
if (count($union) == 0) return 1;
return count($intersection) / count($union);
}
$text1 = "the quick brown fox";
$text2 = "the quick brown dog";
echo "Jaccard相似度: " . round(jaccardSimilarity($text1, $text2) * 100, 2) . "%";
使用第三方库(推荐)
使用 PHP NLP Tools
composer require php-nlp-tools/php-nlp-tools
<?php require_once 'vendor/autoload.php'; use NlpTools\Similarity\CosineSimilarity; use NlpTools\Similarity\JaccardSimilarity; // 余弦相似度 $cosine = new CosineSimilarity(); $vector1 = ['php' => 1, 'programming' => 1, 'language' => 1]; $vector2 = ['php' => 1, 'coding' => 1, 'language' => 1]; echo $cosine->similarity($vector1, $vector2); // 0.666... // Jaccard相似度 $jaccard = new JaccardSimilarity(); $set1 = ['php', 'programming', 'language']; $set2 = ['php', 'coding', 'language']; echo $jaccard->similarity($set1, $set2); // 0.5
使用 Text Similarity PHP
composer require yooper/php-text-analysis
<?php require_once 'vendor/autoload.php'; use PhpTextAnalysis\Similarity\CosineSimilarity; $similarity = new CosineSimilarity(); $text1 = "The quick brown fox jumps over the lazy dog"; $text2 = "The quick brown fox jumps over the sleeping cat"; $score = $similarity->similarity($text1, $text2); echo "相似度分数: " . $score;
完整示例:文本比较系统
<?php
class TextSimilarity {
/**
* 计算文本相似度
*/
public function calculateSimilarity($text1, $text2, $method = 'cosine') {
switch ($method) {
case 'cosine':
return $this->cosineSimilarity($text1, $text2);
case 'jaccard':
return $this->jaccardSimilarity($text1, $text2);
case 'levenshtein':
return $this->levenshteinSimilarity($text1, $text2);
default:
throw new InvalidArgumentException("Unknown method: $method");
}
}
/**
* 文本预处理
*/
private function preprocess($text) {
// 转为小写
$text = strtolower($text);
// 移除标点符号
$text = preg_replace('/[^\w\s]/', '', $text);
// 移除多余空格
$text = preg_replace('/\s+/', ' ', $text);
return trim($text);
}
/**
* 获取词频向量
*/
private function getWordFrequency($text) {
$words = str_word_count($this->preprocess($text), 1);
return array_count_values($words);
}
private function cosineSimilarity($text1, $text2) {
$vector1 = $this->getWordFrequency($text1);
$vector2 = $this->getWordFrequency($text2);
// 简化实现,使用前述的cosineSimilarity函数
// ...
}
private function jaccardSimilarity($text1, $text2) {
// 简化实现
// ...
}
private function levenshteinSimilarity($text1, $text2) {
$distance = levenshtein($text1, $text2);
$maxLen = max(strlen($text1), strlen($text2));
return 1 - ($distance / $maxLen);
}
}
// 使用示例
$similarity = new TextSimilarity();
$text1 = "Natural language processing is fascinating";
$text2 = "Natural language processing is interesting";
echo "余弦相似度: " . $similarity->calculateSimilarity($text1, $text2, 'cosine');
性能优化建议
<?php
// 缓存文本向量
class VectorCache {
private $cache = [];
public function getVector($text) {
$hash = md5($text);
if (!isset($this->cache[$hash])) {
$this->cache[$hash] = $this->buildVector($text);
}
return $this->cache[$hash];
}
private function buildVector($text) {
// 构建向量逻辑
// ...
}
}
选择合适的算法
- 简短文本(如用户名):莱文斯坦距离
- 中等长度文本(如句子):余弦相似度 或 Jaccard
- 长文本比较 (如文章):TF-IDF + 余弦相似度
- 商品、产品名称:similar_text
注意事项
- 中文支持:上述方法主要针对英文,中文需要分词
- 性能:大量文本比较时考虑缓存和索引
- 精度:根据具体场景选择合适的方法
- 内存:处理大规模数据时注意内存使用
如果处理中文文本,建议使用 jieba-php 等分词库进行预处理。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播