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

news2024/11/17 13:56:37

一、概念

相信大家对如下的 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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CTF-栈溢出-基本ROP-【ret2syscall】

文章目录 ret2syscallBxMCTF 2023 Anti-Libcmainwrite_bufflush_obufreadintread_buf 思路exp ret2syscall 即控制程序执行系统调用&#xff0c;获取 shell。 BxMCTF 2023 Anti-Libc main write_buf 写入字符的&#xff0c;待会输出 flush_obuf 把字符输出到屏幕 read…

集群创建(flannel)时候,没有自动创建出cni0网卡

给旧的集群加入四台新的服务器启动时候发现都是正常的&#xff0c;但是pod通信报错 集群通信失败&#xff0c;第一时刻想看看是不是cni0和flannel.1的网段是不是通的&#xff0c;点进去一看发现cni0网卡没有生成。 部署是通过kubeadm方式部署的集群&#xff0c;目前有两种解决…

mysql8 group by出错:this is incompatible with sql_mode=only_full_group_by

第一步&#xff1a;先检查自己windows电脑上有没有my.ini文件&#xff0c;没有则创建一个my.ini 第二步&#xff1a;将下面内容复制进入my.ini保存&#xff0c;重启Mysql即可 [mysqld] sql_modeSTRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,…

请求的接口响应状态为已取消的原因

有趣的iframe问题 今天遇到一个问题&#xff0c;当点击了按钮----跳转页面时----F12键点击网络中的状态报了已取消&#xff0c;类型是 document说明是前端页面的问题&#xff0c;如果是xhr那可能是接口的问题。 原本是写了3个iframe,页面刷新的时候请求了第一个iframe,然后就…

android生成jks文件

jks文件用来校验微信支付 生成的方法&#xff1a;

论文《A recurrent latent variable model for sequential data》笔记:详解VRNN

A recurrent latent variable model for sequential data 背景 1 通过循环神经网络的序列建模 循环神经网络&#xff08;RNN&#xff09;可以接收一个可变长度的序列 x ( x 1 , x 2 , . . . , x T ) x (x_1, x_2, ..., x_T) x(x1​,x2​,...,xT​)作为输入&#xff0c;并通…

install安装路径设定

因为安装路径的前缀 CMAKE_INSTALL_PREFIX 都在外面都写了。 所以在具体的dll 在安装时&#xff0c;就写相对路径就行了。

基于51单片机交通灯仿真_紧急开关+黄灯倒计时+可调时间(proteus+代码+报告+讲解视频)

基于51单片机交通灯_紧急开关黄灯倒计时可调时间 ☑️开题报告☑️仿真图&#xff08;提供源文件&#xff09;&#xff1a;☑️系统硬件设计☑️主控制器选择☑️系统硬件结构图☑️时钟及复位电路☑️指示灯及倒计时模块 ☑️倒计时模块&#xff1a;☑️程序☑️软件主流程框架…

PPT画饼不能信,那现场连数据生成BI报表呢?

PPT说的天花乱坠&#xff0c;不如真正地看到、体验到BI报表效果&#xff0c;眼见为实&#xff01;奥威BI大数据分析工具推出的这套BI方案就将真实的BI报表效果搬到了BI选型会议上了&#xff0c;可以让业务人亲自上手体验三步完成BI报表分析的过程。 只需三步&#xff0c;立即生…

对比学习15篇顶会论文及代码合集,2023最新

对比学习&#xff08;contrastive learning&#xff09;是现在无监督学习中一种常用的学习机制&#xff0c;它可以在没有标签的数据上进行学习&#xff0c;避免依赖大量标签数据&#xff0c;从而帮助我们更好地理解和利用数据集中的信息&#xff0c;提高模型的性能和表现。 作…

Java 开源重试类 guava-retrying 使用案例

使用背景 需要重复尝试执行某些动作&#xff0c;guava-retrying 提供了成型的重试框架 依赖 <dependency><groupId>com.github.rholder</groupId><artifactId>guava-retrying</artifactId><version>${retrying.version}</version>…

python tkinter 使用

python tkinter 使用 ython可以使用多种GUI库来创建窗口页面&#xff0c;例如Tkinter、PyQt、wxPython等。 本篇文章主要讲述如何使用tkinter。 1&#xff1a;导入 import tkinter as tk这时如果运行的话会提示&#xff1a; ModuleNotFoundError: No module named ‘tkint…

小趴菜教你如何用Python开发手机App..

Python语言虽然很万能&#xff0c;但用它来开发app还是显得有点不对路&#xff0c;因此用Python开发的app应当是作为编码练习、或者自娱自乐所用&#xff0c;加上目前这方面的模块还不是特别成熟&#xff0c;bug比较多&#xff0c;总而言之&#xff0c;劝君莫轻入。 准备工作 …

Rust错误处理机制:优雅地管理错误

大家好&#xff01;我是lincyang。 今天&#xff0c;我们要探讨的是Rust语言中的错误处理机制。 Rust作为一种系统编程语言&#xff0c;对错误处理的重视程度是非常高的。它提供了一套既安全又灵活的机制来处理可能出现的错误。 Rust错误处理的两大类别 在Rust中&#xff0…

酒店品牌纷纷冲击中高端,东呈集团能否“快人一步”?

过去两年酒店行业加速洗牌&#xff0c;“强者恒强”的马太效应正持续凸显。 报告显示&#xff0c;2022年排名前10名的酒店集团分别为锦江国际、华住、首旅如家、格林、东呈集团、尚美数智、亚朵、德胧、逸柏、都市酒店。以上10家酒店集团客房规模在连锁酒店市场占有率为62.36&…

同星智能完成A+轮超亿元融资,国投招商领投

2023年10月&#xff0c;上海同星智能科技有限公司成功完成超亿元A轮融资。本轮融资由国投招商管理的先进制造产业投资基金二期领投&#xff0c;老股东丰年资本超额跟投。 本轮融资将用于产品研发和全球化市场拓展。 同星智能成立于2017年&#xff0c;一直专注于研发国产自主可控…

docker-compose安装harbor

docker-compose安装harbor 环境&#xff1a;centos7 1、安装docker 官方文档 https://docs.docker.com/engine/install/centos/ 1、卸载旧版本 $ sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate …

02【SpringBoot静态处理、错误处理】

目录 一、SpringBoot的WEB开发 1.1 静态资源的处理 1.1.1 静态资源目录 1&#xff09;SpringBoot静态资源处理 2&#xff09;关于静态资源处理的配置 3&#xff09;欢迎页面的处理 4&#xff09;修改SpringBoot资源访问路径 1.1.2 WebJars资源 1.2 注册Servlet三大组件…

渗透实例------2个星期艰难的渗透纪实

2个星期艰难的渗透纪实 kyo327 入侵原因,需删一帖子,目标用www.111.com代替,前期通过初期的网站文件暴力猜解,扫描到robots.txt这个文件,有以下目录。如图1: 图1 再通过对这些文件的访问,从3gadm.php文件的标题栏得到该网站采用的是diy-page8.3的cms,自然可以先用搜索…

动捕设备如何推动线下活动以虚拟主持人创新升级互动形式

随着元宇宙概念兴起&#xff0c;虚拟主持人结合全身动捕设备可以依托大屏、全息等形式直观呈现于线下活动&#xff0c;通过动捕设备实时驱动虚拟主持人&#xff0c;将现实活动场景与虚拟相连接&#xff0c;让活动以科技感、多元化的形式呈现&#xff0c;给活动参与者一种新的视…