如何在Golang中构建图书推荐应用_根据用户评分推荐书籍

来源:建站技术作者:广州SEO公司头衔:草根站长
导读:本期聚焦于小伙伴创作的《如何在Golang中构建图书推荐应用_根据用户评分推荐书籍》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何在Golang中构建图书推荐应用_根据用户评分推荐书籍》有用,将其分享出去将是对创作者最好的鼓励。

构建基于用户评分的图书推荐应用,核心是通过分析不同用户的评分数据,找到兴趣相似的用户群体,再基于相似用户的评分记录为目标用户推荐尚未阅读的书籍。整个流程需要设计合理的数据存储结构,实现评分数据的读写,以及核心的推荐算法逻辑。

如何在Golang中构建图书推荐应用_根据用户评分推荐书籍

核心数据结构设计

首先需要定义存储图书信息、用户信息和评分记录的结构体,方便后续的数据操作。我们可以用内存存储的方式简化实现,实际生产中可以替换为数据库存储。

package main

import (
	"fmt"
	"sort"
)

// 图书结构体,存储图书基础信息
type Book struct {
	ID    int     // 图书唯一ID
	Name  string  // 图书名称
	Score float64 // 图书平均评分,后续计算用
}

// 用户结构体,存储用户基础信息
type User struct {
	ID   int    // 用户唯一ID
	Name string // 用户名称
}

// 评分记录结构体,存储用户对图书的评分
type Rating struct {
	UserID int     // 评分用户ID
	BookID int     // 被评分图书ID
	Score  float64 // 评分值,范围1-5
}

评分数据初始化

我们可以先初始化一批测试数据,包含多本图书、多个用户以及对应的评分记录,方便后续测试推荐逻辑。

// 初始化图书数据
func initBooks() map[int]Book {
	books := make(map[int]Book)
	books[1] = Book{ID: 1, Name: "Go语言编程", Score: 0}
	books[2] = Book{ID: 2, Name: "算法导论", Score: 0}
	books[3] = Book{ID: 3, Name: "人类简史", Score: 0}
	books[4] = Book{ID: 4, Name: "三体", Score: 0}
	books[5] = Book{ID: 5, Name: "代码整洁之道", Score: 0}
	return books
}

// 初始化用户数据
func initUsers() map[int]User {
	users := make(map[int]User)
	users[1] = User{ID: 1, Name: "张三"}
	users[2] = User{ID: 2, Name: "李四"}
	users[3] = User{ID: 3, Name: "王五"}
	return users
}

// 初始化评分数据
func initRatings() []Rating {
	ratings := []Rating{
		{UserID: 1, BookID: 1, Score: 4.5},
		{UserID: 1, BookID: 2, Score: 5.0},
		{UserID: 1, BookID: 3, Score: 3.0},
		{UserID: 2, BookID: 1, Score: 4.0},
		{UserID: 2, BookID: 2, Score: 4.5},
		{UserID: 2, BookID: 4, Score: 5.0},
		{UserID: 3, BookID: 3, Score: 4.0},
		{UserID: 3, BookID: 4, Score: 4.5},
		{UserID: 3, BookID: 5, Score: 5.0},
	}
	return ratings
}

计算用户相似度

基于用户评分的推荐常用皮尔逊相关系数计算用户之间的相似度,系数越接近1说明两个用户兴趣越相似。我们需要先整理每个用户的评分映射,再计算两两用户之间的相似度。

// 整理用户评分映射,key为用户ID,value为图书ID到评分的映射
func buildUserRatingMap(ratings []Rating) map[int]map[int]float64 {
	userRatingMap := make(map[int]map[int]float64)
	for _, r := range ratings {
		if _, ok := userRatingMap[r.UserID]; !ok {
			userRatingMap[r.UserID] = make(map[int]float64)
		}
		userRatingMap[r.UserID][r.BookID] = r.Score
	}
	return userRatingMap
}

// 计算两个用户的皮尔逊相关系数
func calcPearsonCorrelation(user1Ratings, user2Ratings map[int]float64) float64 {
	// 找到两个用户共同评分的图书
	commonBooks := make([]int, 0)
	for bookID := range user1Ratings {
		if _, ok := user2Ratings[bookID]; ok {
			commonBooks = append(commonBooks, bookID)
		}
	}
	// 没有共同评分则相似度为0
	if len(commonBooks) == 0 {
		return 0
	}

	// 计算评分和、平方和、乘积和
	sum1 := 0.0
	sum2 := 0.0
	sum1Sq := 0.0
	sum2Sq := 0.0
	sumProduct := 0.0
	for _, bookID := range commonBooks {
		score1 := user1Ratings[bookID]
		score2 := user2Ratings[bookID]
		sum1 += score1
		sum2 += score2
		sum1Sq += score1 * score1
		sum2Sq += score2 * score2
		sumProduct += score1 * score2
	}

	n := float64(len(commonBooks))
	// 计算分子分母
	numerator := sumProduct - (sum1 * sum2 / n)
	denominator := (sum1Sq - sum1*sum1/n) * (sum2Sq - sum2*sum2/n)
	if denominator == 0 {
		return 0
	}
	return numerator / denominator
}

生成推荐列表

得到用户相似度后,我们可以为目标用户找到最相似的K个用户,再收集这些相似用户评分较高但目标用户未读过的图书,按加权评分排序后输出推荐结果。

// 为用户生成推荐图书列表
func recommendBooks(targetUserID int, ratings []Rating, books map[int]Book, topK int) []Book {
	userRatingMap := buildUserRatingMap(ratings)
	targetRatings, ok := userRatingMap[targetUserID]
	if !ok {
		fmt.Println("目标用户不存在评分记录")
		return nil
	}

	// 计算目标用户和其他所有用户的相似度
	type userSim struct {
		UserID int
		Sim    float64
	}
	simList := make([]userSim, 0)
	for userID, ratings := range userRatingMap {
		if userID == targetUserID {
			continue
		}
		sim := calcPearsonCorrelation(targetRatings, ratings)
		simList = append(simList, userSim{UserID: userID, Sim: sim})
	}

	// 按相似度降序排序,取前topK个用户
	sort.Slice(simList, func(i, j int) bool {
		return simList[i].Sim > simList[j].Sim
	})
	if len(simList) > topK {
		simList = simList[:topK]
	}

	// 收集候选图书,计算加权评分
	candidateBooks := make(map[int]float64)
	for _, us := range simList {
		similarUserRatings := userRatingMap[us.UserID]
		for bookID, score := range similarUserRatings {
			// 跳过目标用户已经评分过的图书
			if _, ok := targetRatings[bookID]; ok {
				continue
			}
			// 加权评分:相似度 * 评分
			candidateBooks[bookID] += us.Sim * score
		}
	}

	// 按加权评分降序排序,生成推荐列表
	type bookScore struct {
		BookID int
		Score  float64
	}
	bsList := make([]bookScore, 0)
	for bookID, score := range candidateBooks {
		bsList = append(bsList, bookScore{BookID: bookID, Score: score})
	}
	sort.Slice(bsList, func(i, j int) bool {
		return bsList[i].Score > bsList[j].Score
	})

	// 转换为图书结构体列表
	result := make([]Book, 0)
	for _, bs := range bsList {
		book := books[bs.BookID]
		book.Score = bs.Score
		result = append(result, book)
	}
	return result
}

完整调用示例

最后我们可以编写主函数,初始化数据后为目标用户生成推荐列表,验证推荐逻辑的正确性。

func main() {
	books := initBooks()
	users := initUsers()
	ratings := initRatings()

	// 为目标用户1生成推荐,取最相似的2个用户
	targetUserID := 1
	recommendList := recommendBooks(targetUserID, ratings, books, 2)

	fmt.Printf("为用户 %s 推荐的图书:n", users[targetUserID].Name)
	if len(recommendList) == 0 {
		fmt.Println("暂无推荐图书")
		return
	}
	for _, book := range recommendList {
		fmt.Printf("图书名称:%s,推荐评分:%.2fn", book.Name, book.Score)
	}
}

优化方向

上述实现是基础的内存版本,实际落地时可以做更多优化。比如将评分数据存储到MySQL或者Redis中,提升数据读写性能;可以引入更多特征,比如图书的分类、标签信息,优化相似度计算逻辑;还可以增加评分数据的实时更新逻辑,让推荐结果更及时反映用户的最新偏好。如果推荐数据量较大,也可以考虑使用Go的并发特性优化相似度计算的速度,提升推荐效率。

Golang图书推荐应用用户评分协同过滤推荐算法修改时间:2026-07-21 12:21:40

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。