基于C#实现协同推荐 SlopeOne 算法

一、概念

相信大家对如下的 Category 都很熟悉,很多网站都有类似如下的功能,“商品推荐”,"猜你喜欢“,在实体店中我们有导购来为我们服务,在网络上我们需要同样的一种替代物,如果简简单单的在数据库里面去捞,去比较,几乎是完成不了的,这时我们就需要一种协同推荐算法,来高效的推荐浏览者喜欢的商品。
image.pngimage.png
SlopeOne 的思想很简单,就是用均值化的思想来掩盖个体的打分差异,举个例子说明一下:
image.png
在这个图中,系统该如何计算“王五“对”电冰箱“的打分值呢?刚才我们也说了,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 代表人数。
image.png
根据公式,我们来算一下。
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 类

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

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 += 1;
                          }

                          //记录操作人的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) ? 1 : -1));

                     //关键点:运用公式求解 累计每两组的人数
                     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=1, ProductName="洗衣机",Score=5},
             new Product(){ ProductID=2, ProductName="电冰箱", Score=10},
             new Product(){ ProductID=3, ProductName="彩电", Score=10},
             new Product(){ ProductID=4, ProductName="空调", Score=5},
         };

         userRating.Add(1000, list);

         test.AddUserRatings(userRating);

         userRating.Clear();
         userRating.Add(1000, list);

         test.AddUserRatings(userRating);

         //第二位用户
         list = new List<Product>()
         {
             new Product(){ ProductID=1, ProductName="洗衣机",Score=4},
             new Product(){ ProductID=2, ProductName="电冰箱", Score=5},
             new Product(){ ProductID=3, ProductName="彩电", Score=4},
              new Product(){ ProductID=4, ProductName="空调", Score=10},
         };

         userRating.Clear();
         userRating.Add(2000, list);

         test.AddUserRatings(userRating);

         //第三位用户
         list = new List<Product>()
         {
             new Product(){ ProductID=1, ProductName="洗衣机", Score=4},
             new Product(){ ProductID=2, ProductName="电冰箱", Score=10},
             new Product(){ ProductID=4, ProductName="空调", Score=5},
         };

         userRating.Clear();
         userRating.Add(3000, 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);
     }
 }

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/171953.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【ISP】噪声--sensor(2)

1.热噪声 也叫KT/C噪声&#xff0c;或者叫暗电流噪声。电子的热运动的导致&#xff0c;温度上升&#xff0c;噪声增大。 2.FPN固定模式噪声 由于每个像素点的元器件制造的会有偏差&#xff0c;也就是这些器件的工作参数相对理论值的漂移就构成一种固定模式噪声。 3.光子散粒噪…

No appropriate protocol -- Mysql

DataGrip连接mysql报以下异常信息&#xff1a; javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate) The following required algorithms might be disabled: SSLv3, TLSv1, TLSv1.1, RC4, DES, MD5wi…

网站为什么一定要安装SSL证书

随着互联网的普及和发展&#xff0c;网络安全问题日益凸显。在这个信息爆炸的时代&#xff0c;保护用户隐私和数据安全已经成为各大网站和企业的首要任务。而SSL证书作为一种网络安全技术&#xff0c;已经成为网站必备的安全工具。那么&#xff0c;为什么网站一定要安装SSL证书…

C编译流程

1.预处理 hello.c 经过预处理得到 hello.i gcc -E hello.c -o hello.i -E的含义&#xff1a;说明这是一个预处理操作 生成预处理文件(.i) 预处理阶段做了什么事&#xff1a; 1.1 头文件展开 我们发现 原先只有几行的hello.c变成了上千行的hello.i 实际上 预处理完成的是 将头…

二百零五、Flume——数据流监控工具Ganglia单机版安装以及使用Ganglia监控Flume任务的数据流(附流程截图)

一、目的 Flume采集Kafka的数据流需要实时监控&#xff0c;这时就需要用到监控工具Ganglia 二、Ganglia简介 Ganglia 由 gmond、gmetad 和 gweb 三部分组成。 &#xff08;一&#xff09;第一部分&#xff1a;gmond gmond&#xff08;Ganglia Monitoring Daemon&#xff09;…

.Net6 Api Swagger配置

1、定义个Swagger版本&#xff08;组&#xff09;的枚举 namespace WebApp.Enums {/// <summary>/// api版本枚举/// </summary>public enum ApiVersion{/// <summary>/// v1版本/// </summary>v1 1,/// <summary>/// v2版本/// </summary&…

深度学习之基于Django+Tensorflow动物识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 基于Django和TensorFlow的动物识别系统可以被设计成能够使用深度学习算法自动识别上传的图像中的动物种类&#xff…

【AICFD案例教程】多孔介质歧管流动传热

AICFD是由天洑软件自主研发的通用智能热流体仿真软件&#xff0c;用于高效解决能源动力、船舶海洋、电子设备和车辆运载等领域复杂的流动和传热问题。软件涵盖了从建模、仿真到结果处理完整仿真分析流程&#xff0c;帮助工业企业建立设计、仿真和优化相结合的一体化流程&#x…

Linux C 网络编程概述

网络编程 计算机网络概述分类网络体系结构通信协议通信流程网络通信帧格式以太网帧格式分析ARP 协议分析IP 数据报分析IP分类IP 分配子网掩码 TCP 段分析 TCP三次握手协议 ⭐TCP四次挥手协议 ⭐ TCP编程基于 TCP 客户端编程-步骤说明基于 TCP 服务器端编程-步骤说明基于 TCP 服…

中间件安全:Apache Tomcat 弱口令.(反弹 shell 拿到服务器的最高控制权.)

中间件安全&#xff1a;Apache Tomcat 弱口令. Tomcat 是 Apache 软件基金会&#xff08;Apache Software Foundation&#xff09;的 Jakarta 项目中的一个核心项目&#xff0c;由 Apache、Sun 和其他一些公司及个人共同开发而成。 通过弱口令登录后台&#xff0c;部署 war 包…

【狂神说Java】redis

✅作者简介&#xff1a;CSDN内容合伙人、信息安全专业在校大学生&#x1f3c6; &#x1f525;系列专栏 &#xff1a;【狂神说Java】 &#x1f4c3;新人博主 &#xff1a;欢迎点赞收藏关注&#xff0c;会回访&#xff01; &#x1f4ac;舞台再大&#xff0c;你不上台&#xff0c…

Linux02 VIM编辑器

Linux02 VIM编辑器 基本上 vi/vim 共分为三种模式&#xff0c; 分别是命令模式&#xff08;Command mode&#xff09;&#xff0c;输入模式&#xff08;Insert mode&#xff09;和底线命令模式&#xff08;Last line mode&#xff09;。 三种状态进行切换 插入模式&#xff1a…

如何优雅的避免空指针异常

文章目录 1.数据准备2.实战&#xff1a;获取用户所在的城市2.1.直接获取&#xff1b;容易出现空指针异常。2.2.使用if-else判断&#xff1b;避免了出现空指针的问题&#xff0c;但是代码结构层次嵌套多&#xff0c;不美观2.3.使用工具类美化一下if判断代码2.4.使用Optional解决…

html实现计算器源码

文章目录 1.设计来源1.1 主界面1.2 计算效果界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/134532725 html实现计算器源码&#xff0c;计算器源码&#xff0c;简易计…

2022最新版-李宏毅机器学习深度学习课程-P51 BERT的各种变体

之前讲的是如何进行fine-tune&#xff0c;现在讲解如何进行pre-train&#xff0c;如何得到一个pre train好的模型。 CoVe 其实最早的跟预训练有关的模型&#xff0c;应该是CoVe&#xff0c;是一个基于翻译任务的一个模型&#xff0c;其用encoder的模块做预训练。 但是CoVe需要…

CV计算机视觉每日开源代码Paper with code速览-2023.11.17

点击CV计算机视觉&#xff0c;关注更多CV干货 论文已打包&#xff0c;点击进入—>下载界面 点击加入—>CV计算机视觉交流群 1.【点云分割】&#xff08;CVPR2023&#xff09;Center Focusing Network for Real-Time LiDAR Panoptic Segmentation 论文地址&#xff1a;…

【每日OJ —— 232.用栈实现队列(栈)】

每日OJ —— 232.用栈实现队列&#xff08;栈&#xff09; 1.题目&#xff1a;232.用栈实现队列&#xff08;栈&#xff09;2.解法2.1.方法讲解2.1.1.算法讲解2.1.2.代码实现2.1.3.提交通过展示 1.题目&#xff1a;232.用栈实现队列&#xff08;栈&#xff09; 2.解法 2.1.方法…

图解算法数据结构-LeetBook-栈和队列04_望远镜中最高的海拔_滑动窗口

科技馆内有一台虚拟观景望远镜&#xff0c;它可以用来观测特定纬度地区的地形情况。该纬度的海拔数据记于数组 heights &#xff0c;其中 heights[i] 表示对应位置的海拔高度。请找出并返回望远镜视野范围 limit 内&#xff0c;可以观测到的最高海拔值。 示例 1&#xff1a; 输…

AI技术实力认证,宏电股份荣获2023年度AI天马“领军企业”

近日&#xff0c;由中国新一代人工智能发展战略研究院指导&#xff0c;深圳市人工智能产业协会主办&#xff0c;广东未来产业研究院承办的2023年度“AI天马”认定最终结果公布&#xff0c;宏电股份荣获AI天马“领军企业”奖项。 宏电股份基于20余年的技术沉淀&#xff0c;在工业…

【OpenGauss 列存储学习总结 2】

OpenGauss 列存储学习总结 2 概述文章链接 概述 列存储是一种优化技术&#xff0c;用于在数据库系统中存储和查询大量数据。与传统的行存储方式不同&#xff0c;列存储将每个列的数据分别存储在独立的存储单元中&#xff0c;而不是按照行的方式存储。这种存储方式在分析性查询、…