Java实现基于机器学习的图书推荐系统:协同过滤与矩阵分解实战
简介这份资源是一套基于机器学习的图书推荐系统完整源码面向计算机与软件工程专业学生、Java Web开发者及机器学习入门实践者可用于课程设计、毕业设计或推荐算法学习。系统涵盖图书管理、读书会管理以及科技、人文、历史、中小学等多类别图书的分类推荐核心采用协同过滤算法先提取图书特征再依据用户点击行为记录偏好最后计算图书间距离并推荐最近邻结果。资源包共172个文件约17MB包含25个java源文件、32个jsp页面、58个class编译文件、12个jar依赖包以及8个xml配置、1个sql建库脚本和若干图片与文档前端基于HTML、CSS实现后端采用SpringMVC框架结构完整便于二次开发。目前已有258人学习下载读者可借此掌握协同过滤的工程落地方式、推荐流程与前后端整合思路并直接运行调试快速理解个性化推荐服务的实现细节。1. 图书推荐系统为什么值得用 Java 重写一遍电商平台的推荐栏里图书类目是最早跑通协同过滤的场景之一。但很多团队在做图书推荐时第一反应是上 Python 脚本离线算相似度算完导出 CSV 再塞回业务库。这套流程在数据量小的时候没问题一旦图书量过万、用户行为每天新增几十万条离线脚本和在线服务之间的断层就会变成玄学问题特征对不上、ID 映射错位、推荐结果延迟一天。基于机器学习的图书推荐系统核心诉求不是把算法做到多深而是让“训练—存储—召回—排序—返回”这条链路在一个工程体系里闭环。Java 在这个场景里的优势不是算法库多而是它天然适合做在线服务、事务控制和缓存编排。这套源码适合两类人一是手里有图书业务数据、想自己搭一套可控推荐链路的后端工程师二是学过协同过滤但不知道怎么把模型塞进 Spring 服务里的学生或转行者。下面按“数据怎么进、模型怎么算、服务怎么出、坑怎么避”的顺序拆开讲。2. 图书推荐的数据管道与特征工程从借阅日志到评分矩阵2.1 图书推荐系统的输入到底长什么样图书推荐和电影推荐最大的区别在于行为稀疏性和内容强关联。一个用户可能一年只借 5 本书但每本书的标题、作者、分类、出版社这些内容特征非常稳定。所以图书推荐系统通常走混合路线协同过滤负责“相似的人喜欢什么”内容特征负责“这本书和那本书像不像”。原始数据一般来自三张表用户表、图书表、行为表。行为表里常见字段是 user_id、book_id、behavior_type借阅/收藏/评分/浏览、timestamp、duration。其中 duration 是图书场景特有的强信号——借了 3 天和借了 30 天偏好强度完全不同。我一般会把行为表先做一次加权聚合把隐式反馈转成显式评分。常见做法是-- 把借阅行为聚合成 1-5 的隐式评分 SELECT user_id, book_id, -- 借阅次数权重 0.5收藏权重 0.3评分直接映射 LEAST(5, GREATEST(1, ROUND( SUM(CASE behavior_type WHEN borrow THEN 0.5 * LEAST(duration_days, 30) / 30 WHEN collect THEN 0.3 WHEN rate THEN score / 5.0 ELSE 0 END) * 5 ) )) AS implicit_rating, MAX(event_time) AS last_active FROM user_behavior WHERE event_time DATE_SUB(NOW(), INTERVAL 180 DAY) GROUP BY user_id, book_id HAVING implicit_rating 1;这段 SQL 的逻辑是把不同行为类型映射到 0-1 区间再统一放大到 1-5。LEAST(duration_days, 30)是为了防止长期借阅把权重拉爆30 天封顶。HAVING implicit_rating 1过滤掉噪声行为。参数上180 天窗口是图书场景的经验值——图书偏好衰减比短视频慢但超过半年基本失去参考意义。2.2 用 Java 构建评分矩阵和 ID 映射拿到聚合后的行为数据下一步是构建用户-图书评分矩阵。Java 里不要用double[][]硬扛图书量上万后内存和稀疏性都会出问题。常见做法是用MapLong, MapLong, Double做稀疏存储再配合两个 ID 映射表。import java.util.*; public class RatingMatrixBuilder { // 用户ID - 内部索引 private final MapLong, Integer userIndex new HashMap(); // 图书ID - 内部索引 private final MapLong, Integer itemIndex new HashMap(); // 稀疏评分矩阵用户索引 - (图书索引 - 评分) private final ListMapInteger, Double matrix new ArrayList(); public void addRating(long userId, long bookId, double rating) { int u userIndex.computeIfAbsent(userId, k - { matrix.add(new HashMap()); return matrix.size() - 1; }); int i itemIndex.computeIfAbsent(bookId, k - itemIndex.size()); matrix.get(u).put(i, rating); } public double getRating(long userId, long bookId) { Integer u userIndex.get(userId); Integer i itemIndex.get(bookId); if (u null || i null) return 0.0; return matrix.get(u).getOrDefault(i, 0.0); } public int userCount() { return userIndex.size(); } public int itemCount() { return itemIndex.size(); } }这里的关键设计是内部索引从 0 连续递增外部 ID 只做映射。这样后续做矩阵分解或相似度计算时可以直接用数组或List下标访问避免HashMap在热路径上的开销。computeIfAbsent保证了索引分配和矩阵扩容的原子性。注意itemIndex.size()在computeIfAbsent内部调用是安全的因为此时新 key 还没插入。参数上评分矩阵的稀疏度要监控。如果非零评分数 / (用户数 × 图书数) 0.01说明数据太稀疏纯协同过滤会退化必须引入内容特征做兜底。2.3 图书内容特征的向量化处理图书的标题、作者、分类、简介是协同过滤冷启动阶段的救命稻草。常见做法是把这些文本字段拼接后做 TF-IDF 或直接哈希成定长向量。import java.util.*; public class BookFeatureEncoder { private static final int VECTOR_DIM 64; // 把图书元数据编码成定长向量 public static double[] encode(String title, String author, String category, String publisher) { double[] vec new double[VECTOR_DIM]; // 分类和出版社用哈希桶标题和作者用字符 n-gram 哈希 hashInto(vec, cat: category, 1.0); hashInto(vec, pub: publisher, 0.8); hashInto(vec, author: author, 0.6); // 标题做 2-gram 哈希捕捉词序信息 for (int i 0; i title.length() - 1; i) { hashInto(vec, title: title.substring(i, i 2), 0.4); } normalize(vec); return vec; } private static void hashInto(double[] vec, String token, double weight) { int h Math.abs(token.hashCode()) % VECTOR_DIM; vec[h] weight; } private static void normalize(double[] vec) { double norm 0; for (double v : vec) norm v * v; norm Math.sqrt(norm); if (norm 1e-9) { for (int i 0; i vec.length; i) vec[i] / norm; } } }这段代码用哈希技巧把任意长度的文本压到 64 维。hashInto里的权重是经验值分类最重要出版社次之作者再次标题 n-gram 权重最低但能补充细粒度信息。normalize做 L2 归一化保证后续算余弦相似度时不用重复除模长。注意哈希冲突不可避免64 维对图书量不大的场景够用如果图书超过 10 万本建议把维度提到 128 或 256。3. 协同过滤与矩阵分解在 Java 里的落地实现3.1 基于物品的协同过滤为什么更适合图书场景图书推荐里基于物品的协同过滤ItemCF通常比基于用户的UserCF更稳。原因很直接图书的数量远小于用户数量物品相似度矩阵可以离线算好、增量更新而用户相似度会随着用户增长频繁失效。另外图书的“共现”关系比用户的“共同借阅”关系更稳定——两本讲 Java 并发的书被同一批人借的概率长期都高。ItemCF 的核心是算物品相似度。常见公式是余弦相似度加活跃度惩罚import java.util.*; public class ItemCFSimilarity { // 计算两本图书的相似度带活跃度惩罚 public static double similarity(MapInteger, Double itemA, MapInteger, Double itemB, MapInteger, Integer itemPopularity) { double dot 0.0, normA 0.0, normB 0.0; for (Map.EntryInteger, Double e : itemA.entrySet()) { int user e.getKey(); double ra e.getValue(); Double rb itemB.get(user); if (rb ! null) { // 活跃用户惩罚借书越多的用户单次行为权重越低 double penalty 1.0 / Math.log(1 itemPopularity.getOrDefault(user, 1)); dot ra * rb * penalty; } normA ra * ra; } for (double rb : itemB.values()) normB rb * rb; if (normA 1e-9 || normB 1e-9) return 0.0; return dot / (Math.sqrt(normA) * Math.sqrt(normB)); } }itemPopularity这里实际存的是用户活跃度借阅图书数命名上容易混淆实际使用时建议改叫userActivity。惩罚项1 / log(1 activity)是经典做法活跃度越高惩罚越重防止“借书狂魔”主导相似度。参数上相似度阈值一般设 0.1 到 0.2低于这个值的物品对直接丢弃否则相似度矩阵会爆炸。3.2 矩阵分解的 SGD 训练循环与参数设置当评分矩阵足够稠密时矩阵分解MF能比 ItemCF 捕捉更细的隐语义。Java 里实现 SGD 版本的 MF 不需要任何外部库核心就是一个双层循环。import java.util.*; public class MatrixFactorization { private int factors 20; // 隐因子维度 private double lr 0.01; // 学习率 private double reg 0.02; // 正则化系数 private int epochs 30; // 迭代轮数 private double[][] userVec; // 用户隐向量 private double[][] itemVec; // 图书隐向量 private double[] userBias; // 用户偏置 private double[] itemBias; // 图书偏置 private double globalMean; public void train(Listint[] samples, ListDouble ratings, int userCount, int itemCount) { Random rand new Random(42); userVec new double[userCount][factors]; itemVec new double[itemCount][factors]; userBias new double[userCount]; itemBias new double[itemCount]; // 初始化小随机数避免对称性 for (double[] v : userVec) for (int f 0; f factors; f) v[f] rand.nextGaussian() * 0.01; for (double[] v : itemVec) for (int f 0; f factors; f) v[f] rand.nextGaussian() * 0.01; globalMean ratings.stream().mapToDouble(D - d).average().orElse(0); for (int epoch 0; epoch epochs; epoch) { double loss 0; for (int s 0; s samples.size(); s) { int u samples.get(s)[0]; int i samples.get(s)[1]; double r ratings.get(s); double pred globalMean userBias[u] itemBias[i]; for (int f 0; f factors; f) pred userVec[u][f] * itemVec[i][f]; double err r - pred; loss err * err; // SGD 更新 userBias[u] lr * (err - reg * userBias[u]); itemBias[i] lr * (err - reg * itemBias[i]); for (int f 0; f factors; f) { double uf userVec[u][f]; double if_ itemVec[i][f]; userVec[u][f] lr * (err * if_ - reg * uf); itemVec[i][f] lr * (err * uf - reg * if_); } } if (epoch % 5 0) System.out.printf(epoch %d loss%.4f%n, epoch, loss); } } public double predict(int u, int i) { double pred globalMean userBias[u] itemBias[i]; for (int f 0; f factors; f) pred userVec[u][f] * itemVec[i][f]; return pred; } }这段代码里几个参数需要重点调factors在图书场景一般 15 到 30太小欠拟合太大过拟合且训练慢lr从 0.01 起步如果 loss 震荡就降到 0.005reg控制过拟合图书数据稀疏时建议 0.05 到 0.1epochs看 loss 曲线通常 20 到 50 轮收敛。rand.nextGaussian() * 0.01的初始化很关键全零初始化会让所有隐向量对称训练不动。3.3 离线评估指标与线上召回的衔接模型训完不能只看 loss要用留出集算 RMSE 和 Top-N 命中率。RMSE 衡量评分预测准不准但推荐系统更关心排序。常见做法是每个用户留最后 20% 行为做测试算 Recall10 和 NDCG10。// 计算 Top-N 召回率 public static double recallAtN(MatrixFactorization mf, MapInteger, SetInteger testItems, MapInteger, SetInteger trainItems, int N) { double hit 0, total 0; for (Map.EntryInteger, SetInteger e : testItems.entrySet()) { int u e.getKey(); SetInteger truth e.getValue(); if (truth.isEmpty()) continue; total truth.size(); // 对训练集里没出现过的物品打分取 Top-N PriorityQueueint[] pq new PriorityQueue( (a, b) - Double.compare(b[1], a[1])); for (int i 0; i mf.itemCount(); i) { if (trainItems.getOrDefault(u, Collections.emptySet()).contains(i)) continue; pq.offer(new int[]{i, (int) (mf.predict(u, i) * 1000)}); } SetInteger recommended new HashSet(); for (int k 0; k N !pq.isEmpty(); k) recommended.add(pq.poll()[0]); for (int item : truth) if (recommended.contains(item)) hit; } return total 0 ? 0 : hit / total; }这里用PriorityQueue做 Top-N 选择predict结果乘 1000 转 int 是为了避免浮点比较的精度问题。实际线上服务不会对全量物品打分而是先用 ItemCF 或热门榜召回几百个候选再用 MF 精排。离线评估时也要模拟这个两阶段流程否则 Recall 会虚高。4. 推荐服务的工程化封装从模型文件到 HTTP 接口4.1 模型持久化与热加载的 Java 实现训练好的隐向量不能只留在内存里。常见做法是序列化成二进制文件服务启动时加载并通过定时任务或文件监听做热更新。import java.io.*; import java.nio.file.*; public class ModelStore { private volatile MatrixFactorization currentModel; public void save(MatrixFactorization mf, String path) throws IOException { try (DataOutputStream out new DataOutputStream( new BufferedOutputStream(new FileOutputStream(path)))) { out.writeInt(mf.userCount()); out.writeInt(mf.itemCount()); out.writeInt(mf.factors()); for (double[] v : mf.userVec()) for (double d : v) out.writeDouble(d); for (double[] v : mf.itemVec()) for (double d : v) out.writeDouble(d); for (double d : mf.userBias()) out.writeDouble(d); for (double d : mf.itemBias()) out.writeDouble(d); out.writeDouble(mf.globalMean()); } } public void load(String path) throws IOException { try (DataInputStream in new DataInputStream( new BufferedInputStream(new FileInputStream(path)))) { int u in.readInt(), i in.readInt(), f in.readInt(); MatrixFactorization mf new MatrixFactorization(u, i, f); // 按相同顺序读回向量和偏置 // ... 省略逐项读取 this.currentModel mf; // volatile 写保证可见性 } } public MatrixFactorization get() { return currentModel; } }volatile修饰currentModel是关键保证热加载时读线程能看到新模型。文件格式用DataOutputStream而不是 Java 原生序列化是为了跨语言兼容——Python 训练、Java 服务时也能读。注意读写顺序必须严格一致否则会读出乱码这是血泪经验。4.2 推荐接口的请求参数与返回结构设计一个图书推荐接口的入参通常包括用户 ID、当前图书 ID详情页相关推荐、召回数量、是否排除已借阅。返回结构要包含图书 ID、预测分、推荐理由。public class RecommendRequest { private long userId; private Long currentBookId; // 详情页场景必填 private int topN 10; private boolean excludeBorrowed true; } public class RecommendItem { private long bookId; private double score; private String reason; // 相似图书 / 猜你喜欢 } public class RecommendResponse { private ListRecommendItem items; private long costMs; private String modelVersion; }reason字段看着不起眼但线上点击率能差 15% 以上。图书场景里“借过这本书的人还借了”比“猜你喜欢”更有说服力。modelVersion用于排查问题时定位模型版本没有这个字段线上效果波动时只能抓瞎。4.3 用缓存和降级策略扛住高并发推荐接口的 QPS 通常远高于训练频率所以缓存是必须的。常见做法是 Redis 缓存用户维度的推荐列表TTL 设 10 到 30 分钟。但要注意缓存穿透和雪崩。public ListRecommendItem recommend(RecommendRequest req) { String cacheKey rec:u: req.getUserId() :n: req.getTopN(); // 先查缓存 ListRecommendItem cached redis.get(cacheKey); if (cached ! null) return cached; // 缓存未命中走模型 try { ListRecommendItem result modelRecommend(req); // 异步回写TTL 加随机抖动防雪崩 int ttl 600 ThreadLocalRandom.current().nextInt(300); redis.setex(cacheKey, ttl, result); return result; } catch (Exception e) { // 降级返回热门榜 return hotBooks(req.getTopN()); } }TTL 加随机抖动是防雪崩的标准操作。降级返回热门榜虽然简单但至少保证接口不挂。注意modelRecommend里要做超时控制模型推理超过 50ms 就应该触发降级否则线程池会被拖垮。5. 图书推荐系统避坑与排查5 个真实翻车现场5.1 现象推荐结果全是同一类书多样性极差原因ItemCF 相似度矩阵没有做类别打散热门分类的图书互相“抱团”Top-N 被同一类目垄断。解决在召回后加一层类别打散每个一级分类最多占 Top-N 的 40%。代码上可以在返回前做一次重排// 类别打散每个分类最多保留 maxPerCategory 本 public ListRecommendItem diversify(ListRecommendItem items, MapLong, String bookCategory, int maxPerCategory) { MapString, Integer count new HashMap(); ListRecommendItem result new ArrayList(); for (RecommendItem item : items) { String cat bookCategory.getOrDefault(item.getBookId(), unknown); if (count.getOrDefault(cat, 0) maxPerCategory) continue; count.merge(cat, 1, Integer::sum); result.add(item); } return result; }5.2 现象新用户进来推荐为空接口返回 500原因新用户没有行为数据评分矩阵里找不到对应行模型预测时数组越界。解决做冷启动兜底新用户直接走热门榜或内容相似推荐。判断逻辑放在服务层不要等到模型层抛异常。5.3 现象训练时 loss 正常下降线上效果却越来越差原因离线训练用的是历史全量数据线上服务加载的是旧模型或者特征工程在训练和服务两端的处理逻辑不一致。解决统一特征处理代码训练和服务共用同一个BookFeatureEncoder并在模型文件里写入训练时间戳服务启动时校验模型新鲜度。5.4 现象Redis 缓存命中率突然掉到 10% 以下原因缓存 key 里带了topN参数不同 topN 请求各占一份缓存导致缓存碎片化。解决缓存只存固定长度比如 Top-50的推荐列表不同 topN 请求从缓存结果里截取。这样缓存 key 收敛命中率能回到 80% 以上。5.5 现象矩阵分解训练到第 10 轮后 loss 变成 NaN原因学习率过大导致梯度爆炸或者评分数据里有 NaN 值。解决训练前做一次数据清洗过滤评分为 NaN 或超出 1-5 范围的记录学习率从 0.01 降到 0.005并加梯度裁剪。排查时先打印每轮 loss如果某轮突然跳变基本就是学习率问题。6. 把推荐效果再往上推一档混合召回与在线评估纯协同过滤的天花板在图书场景里来得很快因为图书行为太稀疏。我一般会在 ItemCF 和 MF 之外再加一路内容召回三路结果做加权融合。内容召回用第 2 章里的BookFeatureEncoder算图书向量余弦相似度解决新书和新用户的冷启动。融合权重不要拍脑袋用线上 AB 测试调。常见起步比例是 ItemCF 0.5、MF 0.3、内容 0.2然后按点击率和借阅转化率微调。AB 测试要注意分流一致性——同一个用户在整个实验周期内必须落在同一组否则数据会互相污染。// 多路召回加权融合 public ListRecommendItem hybridRecall(long userId, Long currentBookId, int topN) { MapLong, Double scoreMap new HashMap(); // ItemCF 召回 for (RecommendItem item : itemCFRecall(userId, topN * 3)) scoreMap.merge(item.getBookId(), item.getScore() * 0.5, Double::sum); // MF 召回 for (RecommendItem item : mfRecall(userId, topN * 3)) scoreMap.merge(item.getBookId(), item.getScore() * 0.3, Double::sum); // 内容召回 if (currentBookId ! null) { for (RecommendItem item : contentRecall(currentBookId, topN * 3)) scoreMap.merge(item.getBookId(), item.getScore() * 0.2, Double::sum); } // 排序取 Top-N return scoreMap.entrySet().stream() .sorted((a, b) - Double.compare(b.getValue(), a.getValue())) .limit(topN) .map(e - new RecommendItem(e.getKey(), e.getValue(), hybrid)) .collect(Collectors.toList()); }在线评估不能只看点击率。图书场景里借阅转化周期长点击后可能过几天才借。所以指标要分两层短期看点击率和详情页停留时长长期看 7 日借阅转化率。我习惯在推荐接口里埋一个requestId把曝光、点击、借阅串起来否则归因就是一笔糊涂账。最后说一个我踩过的坑有段时间推荐效果怎么调都不涨排查了两周才发现是图书 ID 映射表在服务重启后重新生成顺序变了导致模型里的物品索引全部错位。后来改成 ID 映射表持久化到文件启动时加载而不是重建。这个教训让我养成了一个习惯——任何跟模型索引相关的映射关系必须和模型文件一起版本化管理不能靠运行时动态生成。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

AI率检测原理与降AI率工具实测:从困惑度到个人化改写全指南

AI率检测原理与降AI率工具实测:从困惑度到个人化改写全指南

这几年我是真切感受到一个变化:打开电脑写论文、做课程设计、交实训报告,越来越多人直接用AI把初稿跑出来,然后卡在最后一道坎上——AI率检测。尤其是专科生群体,时间紧、课程项目多、导师盯得细,很多人不是不想自己写…

2026/10/13 15:18:53 阅读更多 →
青少年开源论坛:从兴趣到贡献,如何让少年真正融入开源社区

青少年开源论坛:从兴趣到贡献,如何让少年真正融入开源社区

1. 为什么社区需要一场面向少年的开源聚会先讲一个我自己的感受。这几年我在开源社区里跑得挺勤,身边一起维护项目、写文档、审 PR 的同伴,年龄构成越来越固定。不是能力不行,是来源太单一了:大学里计算机专业的学生、工作之后的开…

2026/10/13 15:18:53 阅读更多 →
Windows命名管道传输JSON的实战指南:低延迟IPC方案

Windows命名管道传输JSON的实战指南:低延迟IPC方案

1. 项目概述:为什么命名管道是Windows本地进程通信的“隐形高速公路”在Windows系统里,两个程序想交换数据,方法其实不少——文件读写、共享内存、套接字、消息队列……但如果你要的是零延迟、高吞吐、不依赖网络栈、天然支持结构化数据、且只…

2026/10/13 15:18:53 阅读更多 →

最新新闻

STM32电源闭环控制:ADC触发+双环PID+硬件保护实战

STM32电源闭环控制:ADC触发+双环PID+硬件保护实战

简介:本资源为电子设计大赛(电赛)电源类专项程序代码合集,面向高校电子类专业学生、电赛备赛团队及嵌入式电源系统开发者,聚焦电源管理、闭环控制与硬件协同等核心难点,助力快速构建稳定可靠的电源子系统。…

2026/10/13 15:58:04 阅读更多 →
Android无障碍服务实战:微信抢红包插件原理与鲁棒实现

Android无障碍服务实战:微信抢红包插件原理与鲁棒实现

简介:这是一份面向Android逆向与插件开发学习者的微信抢红包Xposed模块源码,适用于具备Java基础和Android开发经验的中高级开发者,帮助理解系统级Hook机制、第三方应用行为干预等核心场景。资源共33个文件,含10个Java核心逻辑类&a…

2026/10/13 15:58:04 阅读更多 →
多智能体协作工作台:开源MyCoWork让AI团队落地真实业务

多智能体协作工作台:开源MyCoWork让AI团队落地真实业务

这段时间跟很多团队聊下来,发现大家普遍卡在同一个地方:模型能力早就够用了,但真要让 AI 在业务里“干活”,总觉得差了点意思。要么是对话机器人只会聊不会做,要么是单点功能做出来了,却跟真实的工作流对不…

2026/10/13 15:58:04 阅读更多 →
Jetson+STM32双芯片架构:移动机器人实时控制源码包解析

Jetson+STM32双芯片架构:移动机器人实时控制源码包解析

简介:这份源码资源完整呈现了一台基于Jetson Orin NX(8GB)与STM32协同控制的移动机器人项目,面向正在入门ROS机器人开发、嵌入式运动控制或激光雷达与视觉融合应用的开发者。包内包含ROS工作空间、STM32控制逻辑、机械臂与车身运动控制相关代码&#xff…

2026/10/13 15:58:04 阅读更多 →
WebSocket实时推送实战:握手、心跳与断线重连的避坑指南

WebSocket实时推送实战:握手、心跳与断线重连的避坑指南

做过实时推送的同学,大概率都经历过这么一段:后端一把数据塞进数据库,前端为了拿到它,要么死循环轮询接口,要么用长轮询把请求挂着,延迟和服务器压力两头受罪。后来我把项目里的推送链路逐步迁到了 WebSock…

2026/10/13 15:58:04 阅读更多 →
AI-Gomoku实战:Minimax+Alpha-Beta打造可部署五子棋AI

AI-Gomoku实战:Minimax+Alpha-Beta打造可部署五子棋AI

简介:AI-Gomoku是一款面向Python初学者与AI算法爱好者的五子棋智能对弈程序,聚焦博弈搜索与轻量级AI实现,适用于算法实践、课程设计及人机对战场景。资源包共11个文件,含4个核心Python源码(如eval.py、referee.py、alg…

2026/10/13 15:57:03 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →