原文:经典算法题每日演练——第六题 协同推荐SlopeOne 算法

相信大家对如下的Category都很熟悉,很多网站都有类似如下的功能,“商品推荐”,"猜你喜欢“,在实体店中我们有导购来为我们服务,在网络上

我们需要同样的一种替代物,如果简简单单的在数据库里面去捞,去比较,几乎是完成不了的,这时我们就需要一种协同推荐算法,来高效的推荐浏览者喜

欢的商品。

一:概念

SlopeOne的思想很简单,就是用均值化的思想来掩盖个体的打分差异,举个例子说明一下:

在这个图中,系统该如何计算“王五“对”电冰箱“的打分值呢?刚才我们也说了,slopeone是采用均值化的思想,也就是:R王五 =4-{[(5-10)+(4-5)]/2}=7 。

下面我们看看多于两项的商品,如何计算打分值。

rb = (n * (ra - R(A->B)) + m * (rc - R(C->B)))/(m+n)

注意: a,b,c 代表“商品”。

ra 代表“商品的打分值”。

ra->b  代表“A组到B组的平均差(均值化)”。

m,n 代表人数。

根据公式,我们来算一下。

r王五 = (2 * (4 - R(洗衣机->彩电)) + 2 * (10 - R(电冰箱->彩电))+ 2 * (5 - R(空调->彩电)))/(2+2+2)=6.8

是的,slopeOne就是这么简单,实战效果非常不错。

二:实现

1:定义一个评分类Rating。

     /// <summary>
/// 评分实体类
/// </summary>
public class Rating
{
/// <summary>
/// 记录差值
/// </summary>
public float Value { get; set; } /// <summary>
/// 记录评分人数,方便公式中的 m 和 n 的值
/// </summary>
public int Freq { get; set; } /// <summary>
/// 记录打分用户的ID
/// </summary>
public HashSet<int> hash_user = new HashSet<int>(); /// <summary>
/// 平均值
/// </summary>
public float AverageValue
{
get { return Value / Freq; }
}
}

2: 定义一个产品类

     /// <summary>
/// 产品类
/// </summary>
public class Product
{
public int ProductID { get; set; } public string ProductName { get; set; } /// <summary>
/// 对产品的打分
/// </summary>
public float Score { get; set; }
}

3:SlopeOne类

参考了网络上的例子,将二维矩阵做成线性表,有效的降低了空间复杂度。

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace SupportCenter.Test
{
#region Slope One 算法
/// <summary>
/// Slope One 算法
/// </summary>
public class SlopeOne
{
/// <summary>
/// 评分系统
/// </summary>
public static Dictionary<int, Product> dicRatingSystem = new Dictionary<int, Product>(); public Dictionary<string, Rating> dic_Martix = new Dictionary<string, Rating>(); public HashSet<int> hash_items = new HashSet<int>(); #region 接收一个用户的打分记录
/// <summary>
/// 接收一个用户的打分记录
/// </summary>
/// <param name="userRatings"></param>
public void AddUserRatings(IDictionary<int, List<Product>> userRatings)
{
foreach (var user1 in userRatings)
{
//遍历所有的Item
foreach (var item1 in user1.Value)
{
//该产品的编号(具有唯一性)
int item1Id = item1.ProductID; //该项目的评分
float item1Rating = item1.Score; //将产品编号字存放在hash表中
hash_items.Add(item1.ProductID); foreach (var user2 in userRatings)
{
//再次遍历item,用于计算俩俩 Item 之间的差值
foreach (var item2 in user2.Value)
{
//过滤掉同名的项目
if (item2.ProductID <= item1Id)
continue; //该产品的名字
int item2Id = item2.ProductID; //该项目的评分
float item2Rating = item2.Score; Rating ratingDiff; //用表的形式构建矩阵
var key = Tools.GetKey(item1Id, item2Id); //将俩俩 Item 的差值 存放到 Rating 中
if (dic_Martix.Keys.Contains(key))
ratingDiff = dic_Martix[key];
else
{
ratingDiff = new Rating();
dic_Martix[key] = ratingDiff;
} //方便以后以后userrating的编辑操作,(add)
if (!ratingDiff.hash_user.Contains(user1.Key))
{
//value保存差值
ratingDiff.Value += item1Rating - item2Rating; //说明计算过一次
ratingDiff.Freq += ;
} //记录操作人的ID,方便以后再次添加评分
ratingDiff.hash_user.Add(user1.Key);
}
}
}
}
}
#endregion #region 根据矩阵的值,预测出该Rating中的值
/// <summary>
/// 根据矩阵的值,预测出该Rating中的值
/// </summary>
/// <param name="userRatings"></param>
/// <returns></returns>
public IDictionary<int, float> Predict(List<Product> userRatings)
{
Dictionary<int, float> predictions = new Dictionary<int, float>(); var productIDs = userRatings.Select(i => i.ProductID).ToList(); //循环遍历_Items中所有的Items
foreach (var itemId in this.hash_items)
{
//过滤掉不需要计算的产品编号
if (productIDs.Contains(itemId))
continue; Rating itemRating = new Rating(); // 内层遍历userRatings
foreach (var userRating in userRatings)
{
if (userRating.ProductID == itemId)
continue; int inputItemId = userRating.ProductID; //获取该key对应项目的两组AVG的值
var key = Tools.GetKey(itemId, inputItemId); if (dic_Martix.Keys.Contains(key))
{
Rating diff = dic_Martix[key]; //关键点:运用公式求解(这边为了节省空间,对角线两侧的值呈现奇函数的特性)
itemRating.Value += diff.Freq * (userRating.Score + diff.AverageValue * ((itemId < inputItemId) ? : -)); //关键点:运用公式求解 累计每两组的人数
itemRating.Freq += diff.Freq;
}
} predictions.Add(itemId, itemRating.AverageValue);
} return predictions;
}
#endregion
}
#endregion #region 工具类
/// <summary>
/// 工具类
/// </summary>
public class Tools
{
public static string GetKey(int Item1Id, int Item2Id)
{
return (Item1Id < Item2Id) ? Item1Id + "->" + Item2Id : Item2Id + "->" + Item1Id;
}
}
#endregion
}

4: 测试类Program

这里我们灌入了userid=1000,2000,3000的这三个人,然后我们预测userID=3000这个人对 “彩电” 的打分会是多少?

     public class Program
{
static void Main(string[] args)
{
SlopeOne test = new SlopeOne(); Dictionary<int, List<Product>> userRating = new Dictionary<int, List<Product>>(); //第一位用户
List<Product> list = new List<Product>()
{
new Product(){ ProductID=, ProductName="洗衣机",Score=},
new Product(){ ProductID=, ProductName="电冰箱", Score=},
new Product(){ ProductID=, ProductName="彩电", Score=},
new Product(){ ProductID=, ProductName="空调", Score=},
}; userRating.Add(, list); test.AddUserRatings(userRating); userRating.Clear();
userRating.Add(, list); test.AddUserRatings(userRating); //第二位用户
list = new List<Product>()
{
new Product(){ ProductID=, ProductName="洗衣机",Score=},
new Product(){ ProductID=, ProductName="电冰箱", Score=},
new Product(){ ProductID=, ProductName="彩电", Score=},
new Product(){ ProductID=, ProductName="空调", Score=},
}; userRating.Clear();
userRating.Add(, list); test.AddUserRatings(userRating); //第三位用户
list = new List<Product>()
{
new Product(){ ProductID=, ProductName="洗衣机", Score=},
new Product(){ ProductID=, ProductName="电冰箱", Score=},
new Product(){ ProductID=, ProductName="空调", Score=},
}; userRating.Clear();
userRating.Add(, list); test.AddUserRatings(userRating); //那么我们预测userID=3000这个人对 “彩电” 的打分会是多少?
var userID = userRating.Keys.FirstOrDefault();
var result = userRating[userID]; var predictions = test.Predict(result); foreach (var rating in predictions)
Console.WriteLine("ProductID= " + rating.Key + " Rating: " + rating.Value);
}
}

经典算法题每日演练——第六题 协同推荐SlopeOne 算法的更多相关文章

  1. 经典算法题每日演练——第十七题 Dijkstra算法

    原文:经典算法题每日演练--第十七题 Dijkstra算法 或许在生活中,经常会碰到针对某一个问题,在众多的限制条件下,如何去寻找一个最优解?可能大家想到了很多诸如“线性规划”,“动态规划” 这些经典 ...

  2. 经典算法题每日演练——第十一题 Bitmap算法

    原文:经典算法题每日演练--第十一题 Bitmap算法 在所有具有性能优化的数据结构中,我想大家使用最多的就是hash表,是的,在具有定位查找上具有O(1)的常量时间,多么的简洁优美, 但是在特定的场 ...

  3. 经典算法题每日演练——第八题 AC自动机

    原文:经典算法题每日演练--第八题 AC自动机 上一篇我们说了单模式匹配算法KMP,现在我们有需求了,我要检查一篇文章中是否有某些敏感词,这其实就是多模式匹配的问题. 当然你也可以用KMP算法求出,那 ...

  4. 经典算法题每日演练——第七题 KMP算法

    原文:经典算法题每日演练--第七题 KMP算法 在大学的时候,应该在数据结构里面都看过kmp算法吧,不知道有多少老师对该算法是一笔带过的,至少我们以前是的, 确实kmp算法还是有点饶人的,如果说红黑树 ...

  5. 经典算法题每日演练——第十一题 Bitmap算法 (转)

    http://www.cnblogs.com/huangxincheng/archive/2012/12/06/2804756.html 在所有具有性能优化的数据结构中,我想大家使用最多的就是hash ...

  6. 经典算法题每日演练——第十六题 Kruskal算法

    原文:经典算法题每日演练--第十六题 Kruskal算法 这篇我们看看第二种生成树的Kruskal算法,这个算法的魅力在于我们可以打一下算法和数据结构的组合拳,很有意思的. 一:思想 若存在M={0, ...

  7. 经典算法题每日演练——第十四题 Prim算法

    原文:经典算法题每日演练--第十四题 Prim算法 图论在数据结构中是非常有趣而复杂的,作为web码农的我,在实际开发中一直没有找到它的使用场景,不像树那样的频繁使用,不过还是准备 仔细的把图论全部过 ...

  8. java算法题每日一练01,java入门简单算法题小练

    1.给数组做反序 public class Ak01 { public static void main(String[] args) { int[] a = new int[]{22,48,41,2 ...

  9. codeforces水题100道 第六题 Yandex.Algorithm 2011 Qualification 2 A. Double Cola (math)

    题目链接:www.codeforces.com/problemset/problem/82/A题意:五个人排队喝可乐,一个人喝完一杯,就在可乐的最后面放两杯自己喝的可乐,问第n个喝的人是谁.C++代码 ...

随机推荐

  1. git commit -s -m 注释中的换行 [加入signed-off-by

    windows环境下的Git Bash中注释的换行: 使用单引号. 或者是在Linux系统里面用终端 git add . git commit -m ' . this is the test . up ...

  2. Hive自己定义函数的使用——useragent解析

    想要从日志数据中分析一下操作系统.浏览器.版本号使用情况.可是hive中的函数不能直接解析useragent,于是能够写一个UDF来解析.useragent用于表示用户的当前操作系统,浏览器版本号信息 ...

  3. log4net使用特定的解释

    说明:该程序演示如何使用log4net记录日志信息. log4net它是-known开源组件的日志记录功能.使用log4net可以很容易地将信息记录到文件.控制台.Windows事件日志和数据库(含有 ...

  4. 简单的dialog菜单

    本文介绍的是我自己做的一个简单的dialog菜单,可以直接植入到类中. 方法代码: /** 显示一个靠右上的dialog列表菜单*/private void showTopBarRightButton ...

  5. 推断序列的端部的大小C规划

    概念 big-endian格式:高字节字符数据被存储在低地址.字数据的低字节存放在高地址. 小尾数格式:相较于大端存储格式,在小尾数格式.低地址存储在字数据的低字节.的是字数据的高字节. 如5A6C中 ...

  6. js比量undefined种类

    js比量undefined种类 if (reValue== undefined) {     alert("undefined"); } 发现推断不出来.最后查了下资料要用type ...

  7. IOS开发——手动设置屏幕旋转

    在移动开发过程.您可能需要跨越看看你的手机.有可能是所有的接口必须跨越,有可能是一个交叉通过电话,当用户当,你的接口也希望他能跨越.还有可能的是,界面的一部分需要被侧向显示.视情况而定,有不同的方法来 ...

  8. 摆弄【Nhibernate 协会制图--导乐陪伴分娩】

    现有两个实体,Dog和 Master,映射到数据库表中如上图所看到的.一个Dog仅仅同意相应一个Master,但一个Master能够有多个Dog.我们在查询Dog的时候.往往还须要知道其主人Maste ...

  9. MyEclipse—怎样在MyEclipse中创建servlet3.0

    servlet3.0随着Java EE6规范一起公布,那么怎样在MyEclipse中创建3.0版的servlet呢? 1.启动MyEclipse.获得下图: 2.点击上图"File" ...

  10. 类似的微博推断客户关系sql声明

    类别似新浪微博的关注和共同关心 不知道别人是怎么设计的. 反正我是例如以下设计的 ID   USER   FRIEND 1     A         B 2      B         A 3   ...