原文:经典算法题每日演练——第六题 协同推荐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. 2.大约QT数据库操作,简单的数据库连接操作,增删改查数据库,QSqlTableModel和QTableView,事务性操作,大约QItemDelegate 代理

     Linux下的qt安装,命令时:sudoapt-get install qt-sdk 安装mysql数据库,安装方法參考博客:http://blog.csdn.net/tototuzuoquan ...

  2. Java NIO 系列教程(转)

    原文中说了最重要的3个概念,Channel 通道Buffer 缓冲区Selector 选择器其中Channel对应以前的流,Buffer不是什么新东西,Selector是因为nio可以使用异步的非堵塞 ...

  3. docker-gitlab(转)

    Issues Docker is a relatively new project and is active being developed and tested by a thriving com ...

  4. SoC嵌入式软件架构设计II:没有MMU的CPU虚拟内存管理的设计和实现方法

    大多数的程序代码是必要的时,它可以被加载到内存中运行.手术后,可直接丢弃或覆盖其它代码. 我们PC然在同一时间大量的应用,地址空间差点儿能够整个线性地址空间(除了部分留给操作系统或者预留它用).能够觉 ...

  5. 【转】Android内存机制分析1——了解Android堆和栈

    昨天用Gallery做了一个图片浏览选择开机画面的功能,当我加载的图片多了就出现OOM问题.以前也出现过这个问题,那时候并没有深究.这次打算好好分析一下Android的内存机制. 因为我以前是做VC+ ...

  6. Python 获取Twitter用户与Friends和Followers的关系(eg, 交集,差集)

    CODE: #!/usr/bin/python # -*- coding: utf-8 -*- ''' Created on 2014-7-30 @author: guaguastd @name: f ...

  7. 小牟Andorid下面MD5具体实现的思路总结

    Android的开发往往需要一定数目demo 从今起MD5一些加密算法提取物 看看是如何实现的 首先,我们必须明确为什么加密? 1 数据安全处理 2 防止数据窃取 3 有效的避免恶意攻击 4 保证文件 ...

  8. Arduino 数码管LED驱动器 阵列方法

    样品谈到最后一个驱动程序LED数码管,采用了最简单的解决方案之一,对于每一个LED高低电平控制,这样的好处是每个LED控制可检.避免短路造成的错觉,因为,但是对于数字的变化是,它是多余的写,因此,这种 ...

  9. MySQL之终端(Terminal)管理MySQL

    原文:MySQL之终端(Terminal)管理MySQL 前言:MySQL有很多的可视化管理工具,比如“mysql-workbench”和“sequel-pro-”. 现在我写MySQL的终端命令操作 ...

  10. hadoop编程小技巧(7)---自己定义输出文件格式以及输出到不同文件夹

    代码測试环境:Hadoop2.4 应用场景:当须要定制输出数据格式时能够採用此技巧,包含定制输出数据的展现形式.输出路径.输出文件名称称等. Hadoop内置的输出文件格式有: 1)FileOutpu ...