Java+MySQL实现智能推荐书单 基于阅读历史的个性化推荐
·
技术架构设计
采用Spring Boot作为后端框架,MySQL存储用户数据和书籍信息,协同过滤算法实现个性化推荐。前端可通过Vue.js或React构建交互界面,后端通过RESTful API提供数据服务。
数据库设计
用户表(user)
CREATE TABLE user (
user_id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE,
password VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
书籍表(book)
CREATE TABLE book (
book_id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(100),
author VARCHAR(50),
category VARCHAR(30),
description TEXT
);
用户阅读历史表(user_reading_history)
CREATE TABLE user_reading_history (
history_id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
book_id INT,
read_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES user(user_id),
FOREIGN KEY (book_id) REFERENCES book(book_id)
);
协同过滤算法实现
基于用户的协同过滤(UserCF)计算相似度矩阵: $$ sim(u,v) = \frac{\sum_{i \in I_{uv}}(r_{ui} - \bar{r}u)(r{vi} - \bar{r}v)}{\sqrt{\sum{i \in I_{uv}}(r_{ui} - \bar{r}u)^2} \sqrt{\sum{i \in I_{uv}}(r_{vi} - \bar{r}_v)^2}} $$
Java核心代码片段
// 计算用户相似度矩阵
public Map<Integer, Map<Integer, Double>> calculateUserSimilarity() {
List<User> users = userRepository.findAll();
Map<Integer, Map<Integer, Double>> similarityMatrix = new HashMap<>();
for (User u : users) {
Map<Integer, Double> similarities = new HashMap<>();
for (User v : users) {
if (!u.equals(v)) {
double sim = cosineSimilarity(u, v);
similarities.put(v.getUserId(), sim);
}
}
similarityMatrix.put(u.getUserId(), similarities);
}
return similarityMatrix;
}
private double cosineSimilarity(User u, User v) {
Set<Integer> commonBooks = findCommonBooks(u, v);
if (commonBooks.isEmpty()) return 0.0;
double dotProduct = 0.0;
double normU = 0.0;
double normV = 0.0;
for (Integer bookId : commonBooks) {
double ratingU = getNormalizedRating(u, bookId);
double ratingV = getNormalizedRating(v, bookId);
dotProduct += ratingU * ratingV;
normU += Math.pow(ratingU, 2);
normV += Math.pow(ratingV, 2);
}
return dotProduct / (Math.sqrt(normU) * Math.sqrt(normV));
}
推荐逻辑实现
基于最近邻的推荐策略
public List<Book> recommendBooks(int userId, int topN) {
Map<Integer, Double> userSimilarities = similarityMatrix.get(userId);
List<Integer> nearestNeighbors = userSimilarities.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.limit(50)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
Set<Integer> alreadyRead = readingHistoryRepository.findByUserId(userId)
.stream()
.map(ReadingHistory::getBookId)
.collect(Collectors.toSet());
Map<Integer, Double> candidateBooks = new HashMap<>();
for (Integer neighborId : nearestNeighbors) {
List<ReadingHistory> neighborHistory = readingHistoryRepository.findByUserId(neighborId);
for (ReadingHistory rh : neighborHistory) {
if (!alreadyRead.contains(rh.getBookId())) {
candidateBooks.merge(rh.getBookId(),
userSimilarities.get(neighborId),
(oldVal, newVal) -> oldVal + newVal);
}
}
}
return candidateBooks.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.limit(topN)
.map(e -> bookRepository.findById(e.getKey()).orElse(null))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
性能优化方案
建立阅读历史索引加速查询:
CREATE INDEX idx_user_book ON user_reading_history(user_id, book_id);
使用Redis缓存推荐结果:
@Cacheable(value = "recommendations", key = "#userId")
public List<Book> getCachedRecommendations(int userId) {
return recommendBooks(userId, 10);
}
效果评估指标
采用准确率评估推荐质量: $$ Precision@k = \frac{| { \text{推荐书籍} } \cap { \text{实际喜欢书籍} } |}{k} $$
实现AB测试框架:
public class RecommendationEvaluator {
public double calculatePrecision(int userId, List<Integer> recommended, List<Integer> actuallyLiked) {
Set<Integer> recommendedSet = new HashSet<>(recommended);
recommendedSet.retainAll(actuallyLiked);
return (double) recommendedSet.size() / recommended.size();
}
}
更多推荐


所有评论(0)