在计算用户相似度的过程中,首先对于两个用户共同打分过的所有条目,计算他们对于每个条目的评分差值,对差值求平方、求和,再对结果求平方根,这样得到的值称为欧氏距离,但这并不足以作为显示度计算的度量值。相似度与距离的概念在某种程度上说是互反的,就其意义而言,欧氏距离越小,两个用户相似度就越大。相似度与距离这种反序关系很容易就可以调整过来,比如只要第一显示度为欧氏距离加1,再取倒数。
02data.php
<?php
$data = array(
'Frank'=>array(
'Tears'=>5,
'La'=>4,
'Robinson'=>5,
'Yesterday'=>4,
'Wizard'=>5,
'Mozart'=>5,
'Bethoven'=>5
),
'Constantine'=>array(
'Tears'=>5,
'Fiddler'=>5,
'Robinson'=>5,
'Wonderful World'=>4,
'Wizard'=>4,
'Let It Be'=>5,
'Mozart'=>5
),
'Catherine'=>array(
'Tears'=>1,
'Robinson'=>2,
'Yesterday'=>2,
'Beethoven'=>3,
'Sunnday'=>1,
'Let It Be'=>2,
),
'David'=>array(
'Tears'=>1,
'Robinson'=>2,
'Yesterday'=>2,
'Let It Be'=>2,
'Bethoven'=>5
)
);
?>
getSimilarity.php
<?php
class getSimilarity
{
/*
* 获取两个用户共同条目
*/
private function getCommonItems($user1,$user2)
{
$commonItems = 0;
$sim = 0;
foreach ($user1 as $key1=>$value1){
if(isset($user2[$key1])){
$commonItems++;
$sim+=pow($value1-$user2[$key1], 2);
}
}
return array($commonItems, $sim);
}
/*
* 计算相似度
*/
public function getSim($user1,$user2)
{
$cs = $this->getCommonItems($user1, $user2);
if($cs[0]>0){
$sim = sqrt($cs[1]/(double)$cs[0]);
$sim = 1 - tanh($sim);
$maxCommonItems = min(count($user1),count($user2));
$sim = $sim*((double)$cs[0]/(double)$maxCommonItems);
return $sim;
}else{
return 0;
}
}
}
include_once '02data.php';
$user1 = $data['Frank'];
$user2 = $data['Constantine'];
$user3 = $data['Catherine'];
$user4 = $data['David'];
$similarity = new getSimilarity();
echo $similarity->getSim($user1,$user2);
echo '<br />';
echo $similarity->getSim($user1,$user3);
echo '<br />';
echo $similarity->getSim($user1,$user4);
echo '<br />';
echo $similarity->getSim($user2,$user3);
echo '<br />';
echo $similarity->getSim($user2,$user4);
echo '<br />';
echo $similarity->getSim($user3,$user4);
?>
上面的代码先求出两个用户有一些共同评分的歌曲,那么把他们对其中每首歌曲评分差值的平方和除以这部分歌曲的数量,再对商取平方根,最后用1减去这个双切正切函数的返回值,最后还考虑的两个用户共有的条目的量与两个人所有可能共有条目量的比率。