原文:经典算法题每日演练——第六题 协同推荐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. 二叉查找树C语言实现

    二叉查找树C语言实现 1.      二叉查找树的定义: 左子树不为空的时候,左子树的结点值小于根节点,右子树不为空时,右子树的结点值大于根节点,左右子树分别为二叉查找树 2.      二叉查找树的 ...

  2. 认识Backbone (一)

    Backbone.js为复杂WEB应用程序提供模型(models).集合(collections).视图(views)的结构.其中模型用于绑定键值数据和自定义事件:集合附有可枚举函数的丰富API: 视 ...

  3. HttpLuaModule——翻译(Nginx API for Lua) (转)

    现在我已经将翻译的内容放到:http://wiki.nginx.org/HttpLuaModuleZh Nginx API for Lua Introduction 各种各样的*_by_lua和*_b ...

  4. Directx11学习笔记【七】 游戏定时器的实现

    无论是在动画还是在一些游戏中,定时器总是必不可少的,游戏的帧数动画的播放等都离不开定时器的控制.这里以dx11龙书中提供的定时器为例,具体看看是怎么实现一个简单但精度高使用方便的定时器的. 这个定时器 ...

  5. Linux下 高性能、易用、免费的ASP.NET服务器

    Linux下 高性能.易用.免费的ASP.NET服务器 http://www.jexus.org/#

  6. CI-持续集成(2)-软件工业“流水线”技术实现(转)

    1   概述 持续集成(Continuous Integration)是一种软件开发实践.在本系列文章的前一章节已经对其背景及理论体系进行了介绍.本小节则承接前面提出的理论构想进行具体的技术实现. & ...

  7. 关于“类不能支持Automation操作”错误的解决方法

    一段程序IE上老是提示“类不支持Automation操作”的错误,IE6.7.8都一样,但是Firefox可以,后来网上找到如下解决方法: 重新注册下以下文件,问题便解决了:msscript.ocxd ...

  8. hud 1312 Red and Black

    题目: 链接:pid=1312">点击打开链接 题意: DFS搜索 算法: dfs 思路: 简单题 代码: #include<iostream> #include<c ...

  9. Docker创建MySQL集装箱

    原文链接:Docker创建MySQL集装箱 这样做的目的是创建一个MySQL的image,出来的容器里自己主动启动MySQL服务接受外部连接 步骤: 1. 首先创建一个文件夹并在文件夹下创建一个Doc ...

  10. [SQL Server优化]善用系统监视器,确定系统瓶颈

    原文:[SQL Server优化]善用系统监视器,确定系统瓶颈 来自: http://hi.baidu.com/solorez/blog/item/f82038fa0e71b78d9e51468c.h ...