基于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){//遍历所有的Itemforeach (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中所有的Itemsforeach (var itemId in this.hash_items){//过滤掉不需要计算的产品编号if (productIDs.Contains(itemId))continue;Rating itemRating = new Rating();// 内层遍历userRatingsforeach (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.mzph.cn/news/154906.shtml

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

相关文章

小迪笔记(1)——操作系统文件下载反弹SHELL防火墙绕过

名词解释 POC&#xff1a;验证漏洞存在的代码&#xff1b; EXP&#xff1a;利用漏洞的代码&#xff1b; payload&#xff1a;漏洞利用载荷&#xff0c; shellcode&#xff1a;漏洞代码&#xff0c; webshell&#xff1a;特指网站后门&#xff1b; 木马&#xff1a;强调控制…

rotation matrix reflection matrix

文章目录 1. rotation matrix1.1 结论 2. reflection matrix2.1 结论 1. rotation matrix 图像逆时针旋转 θ \theta θ的矩阵 Q r o t a t e [ cos ⁡ θ − sin ⁡ θ sin ⁡ θ cos ⁡ θ ] (1) Q_{rotate}\begin{bmatrix}\cos\theta&-\sin\theta\\\sin\theta&\c…

洗涤厂污水处理设备厂家工艺流程

诸城市鑫淼环保小编带大家了解一下洗涤厂污水处理设备厂家工艺流程 1.格栅&#xff1a;格栅是初级处理中常用的设备&#xff0c;用于拦截较大的杂质和固体颗粒。我们的格栅采用优质不锈钢制造&#xff0c;结构坚固耐用&#xff0c;可有效防止堵塞和腐蚀。 2.活性污泥池&#xf…

数据湖的概念、发展背景和价值

数据湖是一个集中化的存储系统&#xff0c;旨在以低成本、大容量的方式&#xff0c;无需预先对数据进行结构化处理&#xff0c;存储各种结构化和非结构化数据。以下是数据湖概念、发展背景和价值的详细介绍。 数据湖概念 数据湖的概念源自于对传统数据仓库的补充。传统数据仓…

Ajax之基本语法

【一】前后端传输数据的编码格式(contentType) 主要研究POST请求数据的编码格式 因为GET请求数据就是直接放在url后面的 可以朝后端发送post请求的方式 form请求ajax请求api工具 【1】form表单 前后端传输数据的格式 urlencoded formdata json 【2】编码格式 form表单默认…

zookeeper应用之分布式队列

队列这种数据结构都不陌生&#xff0c;特点就是先进先出。有很多常用的消息中间件可以有现成的该部分功能&#xff0c;这里使用zookeeper基于发布订阅模式来实现分布式队列。对应的会有一个生产者和一个消费者。 这里理论上还是使用顺序节点。生产者不断产生新的顺序子节点&am…

Java修仙记之记录一次与前端女修士论道的经历

文章开始之前&#xff0c;想跟我念一句&#xff1a;福生无量天尊&#xff0c;无量寿佛&#xff0c;阿弥陀佛 第一场论道&#xff1a;id更新之争 一个天气明朗的下午&#xff0c;前端的小美女长发姐告诉我&#xff1a;嘿&#xff0c;小后端&#xff0c;你的代码报错了 我答道&am…

SpringBoot 全局请求参数转驼峰、响应参数转换为下划线

文章目录 前言请求参数将下划线转换为驼峰响应参数将驼峰转换为下划线方式一 使用Jackson方式处理方式二 在配置文件中修改jackson.default-property-inclusion 说明jackson.property-naming-strategy 说明前言 在开发SpringBoot项目时,我们经常需要处理参数的命名规范。有时…

springboot -sse -flux 服务器推送消息

先说BUG处理&#xff0c;遇到提示异步问题 Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported&…

如何保障亚马逊多账户的安全,防止关联?

在亚马逊平台上拥有多个账户可以扩大销售渠道&#xff0c;但同时也需要注意账户的安全&#xff0c;以防止被关联。本文将介绍一些重要的措施&#xff0c;帮助您保护亚马逊多账户的安全&#xff0c;预防账号关联。 一、亚马逊关联是什么&#xff1f; 在亚马逊平台上&#xff0…

单节点服务架构

单节点的服务架构&#xff1a; LNMP l:lilnux系统 n:nginx静态页面&#xff0c;转发动态请求 m:mysql数据库&#xff0c;后端服务器&#xff0c;保存用户和密码信息&#xff0c;以及论坛的信息 p:PHP&#xff0c;处理动态请求&#xff0c;动态请求转发数据库&#xff0c;然…

3PC(三阶段提交)

三阶段提交 3PC&#xff08;Three-Phase Commit&#xff09;是一种分布式系统中用于实现事务一致性的协议&#xff0c;它是在2PC&#xff08;Two-Phase Commit&#xff09;的基础上发展而来&#xff0c;旨在解决2PC的一些缺点。与2PC的两个阶段&#xff08;准备和提交&#xf…

iptables的一次修复日志

iptables的一次修复日志 搭建配置wireguard后&#xff0c;使用内网连接设备十分方便&#xff0c;我采用的是星型连接&#xff0c;即每个节点都连接到中心节点&#xff0c;但是突然发生了重启wg后中心节点不转发流量的问题&#xff0c;即每个接入的节点只能与中心节点连接&…

M2 Mac Xcode编译报错 ‘***.framework/‘ for architecture arm64

In /Users/fly/Project/Pods/YYKit/Vendor/WebP.framework/WebP(anim_decode.o), building for iOS Simulator, but linking in object file built for iOS, file /Users/fly/Project/Pods/YYKit/Vendor/WebP.framework/WebP for architecture arm64 这是我当时编译模拟器时报…

Mars3d-vue最简项目模板集成使用Mars3d的UI控件样板

备注说明&#xff1a; 1.小白可看步骤一二&#xff0c;进阶小白可直接看步骤三 步骤一&#xff1a;新建文件夹<uitest>&#xff0c;在mars3d仓库拉一份最简项目模板&#xff1a; git clone mars3d-vue-template: Vue3.x 技术栈下的Mars3D项目模板 步骤二&#xff1a;运…

java: 无法访问org.mybatis.spring.annotation.MapperScan

java: 无法访问org.mybatis.spring.annotation.MapperScan错误的类文件: /E:/maven/repository/org/mybatis/mybatis-spring/3.0.1/mybatis-spring-3.0.1.jar!/org/mybatis/spring/annotation/MapperScan.class类文件具有错误的版本 61.0, 应为 52.0请删除该文件或确保该文件位…

本地部署 EmotiVoice易魔声 多音色提示控制TTS

本地部署 EmotiVoice易魔声 多音色提示控制TTS EmotiVoice易魔声 介绍ChatGLM3 Github 地址部署 EmotiVoice准备模型文件准备预训练模型推理 EmotiVoice易魔声 介绍 EmotiVoice是一个强大的开源TTS引擎&#xff0c;支持中英文双语&#xff0c;包含2000多种不同的音色&#xff…

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

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

electron项目开机自启动

一、效果展示&#xff1a;界面控制是否需要开机自启动 二、代码实现&#xff1a; 1、在渲染进程login.html中&#xff0c;画好界面&#xff0c;默认勾选&#xff1b; <div class"intro">开机自启动 <input type"checkbox" id"checkbox&quo…

C++纯虚函数和抽象类 制作饮品案例(涉及知识点:继承,多态,实例化继承抽象类的子类,多文件实现项目)

一.纯虚函数的由来 在多态中&#xff0c;通常父类中虚函数的实现是毫无意义的&#xff0c;主要都是调用子类重写的内容。例如&#xff1a; #include<iostream>using namespace std;class AbstractCalculator { public:int m_Num1;int m_Num2;virtual int getResult(){r…