<?php
declare(strict_types=1);
namespace App\Common\Algorithm;
use Exception;
/**
*@author guicheng
*@visit www.mxreality.cn
*/
class AlgorithmSimilarity
{
/**
* 计算两个值之间的分数 1 / (abs($v2 - $v1) / $divide)
*
* @param $v1
* @param $v2
* @param boolean $equal 强相关参数,为true时只有0/1两种返回结果
* @param integer $divide $v1和$v2差距敏感度,大于0的整数, 得分与该数值成正比
* @return float 分数$score[0-1]
*/
function calcScore($v1, $v2, bool $equal = false, int $divide = 1): float
{
if ($divide <= 0) {
throw new Exception('敏感度参数不能小于0');
}
if ($v1 == $v2) {
return 1;
}
$sub = abs($v2 - $v1);
if ($sub < $divide) {
return 1;
}
$score = 1 / ($sub / $divide);
if ($equal && $score) {
return 0;
}
return $score;
}
/**
* 余玄相似度
*
* 设A、B为多维矩阵
*
* ∑(Ai•Bi) ∑(Ai•Bi) ∑(USER1_i•USER2_i)
* cos∂ = --------------- = ----------------- = -------------------------
* |Ai|•|Bi| √∑ Ai² • √∑ Bi² √∑ USER1_i² • √∑ USER2_i²
*
* @param array $item1 项目1评分数组,eg:['1','3','4','5','1']
* @param array $item2 项目2评分数组,eg:['2','2','4','5','1']
*
* @return string 两个项目相似度
*/
function calcSimilarity(array $item1, array $item2): float
{
$sumAiBi = '0';
$sumAi = '0';
$sumBi = '0';
$len = count($item1);
if (count($item1) != count($item2)) {
throw new Exception('相似计算两组评分结构不一致');
}
for ($i = 0; $i < $len; $i++) {
//∑(Ai•Bi)
$sumAiBi += $item1[$i] * $item2[$i];
//∑USER1_i
$sumAi += pow($item1[$i], 2);
//∑USER2_i
$sumBi += pow($item2[$i], 2);
}
return $sumAiBi / (sqrt($sumAi) * sqrt($sumBi));
}
}
使用方法
<?php
/**
*
* @params $goods 被计算评分的数据列表
* @params $target 计算评分的商品
*
*/
function test($goods,$target){
foreach ($goods as $item) {
$scores = [
$algorithm->calcScore($item->getType(), $target->getType(), true), //类目评分,强相关,一致则评分最高1,不一致评分最低0
$algorithm->calcScore($item->getCity(), $target->getCity(), true), //区域评分,强相关,区域id一致则评分最高1,不一致评分为0
$algorithm->calcScore($item->getPrice(), $target->getPrice(), false, 10), //价格评分,10元为基准点,差距越大评分越低
$algorithm->calcScore($item->getCreateAt(), time(), false, 2 * 24 * 60 * 60), //创建时间相关评分,以2天为基准点,越晚评分越低
$algorithm->calcScore($item->badRate(),$target->badRate(),false,0.6) // 产品合格率评分
];
// 最高基准评分
$expectations = [1, 1, 1, 1, 1];
// 得到该商品对于我的评分
$score = $algorithm->calcSimilarity($scores, $expectations);
// 保存产品ID,用户ID和对应的评分,避免实时计算
$recommendModel->updateOrCreate($userId, $item->getId(), $score);
}
}
欢迎大家批评指正!