news 2026/10/15 5:00:50

基于协同过滤的旅游推荐系统设计与实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于协同过滤的旅游推荐系统设计与实现

做旅游推荐系统这个选题,最初的想法很简单:毕设既要体现工作量,又得把数据采集、算法、可视化这几块核心技术串成一条完整的链路。旅游推荐系统恰好能满足这个要求——数据端爬取真实旅游平台的景点和用户行为数据,算法端用协同过滤实现个性化推荐,展示端用可视化大屏呈现分析结果。这套组合拿出去答辩,老师能看出你确实掌握了从数据到应用的闭环能力,而不是只写了某个孤立的模块。

本文以一套2025年落地的旅游推荐系统为例,完整拆解它的设计思路、爬虫方案、协同过滤算法实现、可视化搭建以及实战中踩过的坑。系统基于Python 3.10 + Flask + MySQL + ECharts构建,核心采用基于物品的协同过滤算法(ItemCF),配合旅游平台公开数据爬虫和可视化展示。无论你是正在选毕业设计课题的在校学生,还是想快速搭建一个推荐系统Demo的程序员,这篇文章都值得读完,里面的代码思路和避坑经验可以直接抄作业。

1. 项目定位与技术选型思考

1.1 为什么选旅游推荐系统这个课题

每年毕业设计选题时,很多同学在“管理系统”和“算法类项目”之间反复纠结。纯管理系统太单薄,评委看几分钟就失去兴趣;纯算法项目又容易陷进数学公式,最终跑不出结果。旅游推荐系统恰好站在一个平衡点上,它既有真实的业务场景,又有足够的算法深度。

从项目完整性来看,整个系统覆盖了数据采集、数据清洗、算法建模、Web展示四个阶段。爬虫负责从旅游平台抓取景点信息、用户评分、评论数据;pandas完成数据预处理;协同过滤算法基于用户历史行为计算推荐结果;Flask提供Web接口,ECharts将统计结果可视化。技术栈完整,每一块都有东西可写、有内容可讲。

更重要的是,旅游推荐系统有天然的展示优势。普通电商推荐系统展示的只是“买了又买”的商品列表,而旅游推荐可以结合地图、热力图、景点排行榜、用户画像等多种可视化形式呈现,视觉效果出色。答辩现场把这些图表一放,评委的第一印象就立住了。

1.2 技术栈选型与核心理由

系统最终选用的技术栈如下:

模块技术选型选型理由
后端框架Flask 2.3轻量灵活,适合中小型系统,开发效率高
数据库MySQL 8.0关系型数据存储稳定,SQL查询方便
数据采集requests + BeautifulSoup覆盖面广,社区资料多,遇到问题容易排查
数据处理pandas清洗逻辑简单直观,处理CSV和DataFrame非常顺手
推荐算法基于物品的协同过滤(ItemCF)可解释性强,旅游场景下效果稳定
可视化ECharts + 自研图表接口交互能力强,地图热力、动画效果明显优于静态报表
前端页面Bootstrap + 原生JS无需复杂前端构建工具,降低整体复杂度

为什么用Flask而不用Django?实话实话,Django的ORM、Admin后台确实强大,但对于毕业设计体量的项目来说太重了。Flask路由简单、模板直观,学起来快,部署到服务器也方便。更重要的是,推荐系统的核心逻辑在后端算法层,Flask只需要承担“接收请求-调用算法-返回JSON”的角色,足够胜任。

为什么用基于物品的协同过滤而不是基于用户的协同过滤?这是我在选题阶段就纠结过的问题。旅游场景和电商不一样,用户去过的景点数量有限,但景点总数在不断增长。基于物品的协同过滤计算的是景点与景点的相似度矩阵,这个矩阵相对稳定,不需要每次推荐都重新计算。而基于用户的协同过滤在用户数量增加时,相似度计算的开销会急剧膨胀,而且用户行为稀疏时效果很差。两相比较,ItemCF更适合旅游推荐这个小而精的场景。

2. 数据采集与处理:爬虫方案设计与实战

2.1 数据源分析与爬取目标

做推荐系统,数据质量直接决定了推荐效果的上限。最初我尝试过直接使用公开数据集,但公开的旅游数据要么太旧,要么字段不全,缺乏真实的用户评分关系。最后决定自己爬取某旅游平台的公开页面数据。

爬虫部分需要采集两类核心数据:

  • 景点基础信息:景点名称、所在城市、景区评分、热度排名、门票价格、景点类型(自然风光、人文古迹、主题公园等)。这些字段用于数据分析和可视化展示。
  • 用户行为数据:用户ID、景点ID、评分、评论内容、评论时间。这里的用户ID经过匿名化处理,只保留数字编号,不涉及任何个人身份信息。评分是1-5分的整数,共采集约6000条有效行为记录,覆盖300多个热门景点。

这里说一句题外话,设计字段时不要想当然。一开始我把门票价格设计成了浮点数,结果爬下来发现很多景点是“免票”或“待定”,导致后面清洗时多花了不少时间。建议在爬虫阶段就把字段的边界情况考虑清楚,比如“免票”记为0、“待定”记为NULL,不要硬塞进数值类型。

2.2 反爬应对与请求策略

旅游平台的页面通常有一定的反爬机制,直接暴力请求会很快被拦截。我在实践中总结了一套温和但有效的请求策略:

  1. 请求头伪装:模拟真实浏览器的User-Agent,包括Chrome、Safari、Edge等常见标识,并在每次请求时随机轮换。不要频繁更换IP,同一IP下控制请求频率更重要。
  2. 随机延时:在两次请求之间加入2-5秒的随机sleep,降低访问频率特征。核心逻辑是让请求节奏看起来像真人浏览,而不是机器速刷。
  3. 失败重试机制:遇到请求超时或返回异常状态码时,记录日志并自动重试,最多重试3次。如果连续失败超过10次,则暂停爬虫30分钟,避免触发更严厉的封禁。
  4. 增量爬取与断点续爬:把已爬取的景点ID记录在本地文件里,重启爬虫时自动跳过已完成的部分,避免重复劳动。

下面是一段简化后的爬虫核心代码,仅供参考:

import requests import time import random from bs4 import BeautifulSoup HEADERS_LIST = [ {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}, {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"}, ] def fetch_with_retry(url, max_retries=3): for attempt in range(max_retries): try: headers = random.choice(HEADERS_LIST) resp = requests.get(url, headers=headers, timeout=10) if resp.status_code == 200: return resp.text elif resp.status_code == 403: print(f"[403] 被拒绝访问: {url}, 等待重试...") time.sleep(30) else: print(f"[{resp.status_code}] 访问异常: {url}") except requests.exceptions.RequestException as e: print(f"[网络异常] {url}, 第{attempt + 1}次尝试失败: {e}") time.sleep(random.uniform(2, 5)) return None def parse_scenic_info(html): soup = BeautifulSoup(html, "html.parser") name = soup.select_one(".scenic-name").text.strip() score = float(soup.select_one(".score").text.strip()) # 其他字段解析略... return {"name": name, "score": score} url = "https://example-travel-site.com/scenic/{id}" for scenic_id in range(1, 500): html = fetch_with_retry(url.format(id=scenic_id)) if html: info = parse_scenic_info(html) # 存入数据库或CSV time.sleep(random.uniform(2, 5))

2.3 数据清洗与入库

爬取完成后,原始数据非常粗糙,必须经过清洗才能用于算法建模。我按以下顺序处理:

  1. 去重:景点名称和ID存在少量重复记录,按景点ID去重后,剩余有效景点310个。
  2. 缺失值处理:门票价格缺失的景点,如果是热门景点,用同类景点(同城市、同类型)的中位数填充;信息缺失过多的记录直接删除。
  3. 文本字段处理:景点类型字段存在分类不统一的问题,比如“自然·风光”和“自然风光”其实是同一个意思,需要手动归并。
  4. 数值类型转换:把评分、热度等字段从字符串转为浮点数,为后续计算做准备。

清洗完的数据统一写入MySQL。用户评分表的建表语句简化为:

CREATE TABLE user_rating ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, scenic_id INT NOT NULL, rating FLOAT NOT NULL, rating_time DATETIME, comment VARCHAR(500), UNIQUE KEY uk_user_scenic (user_id, scenic_id) );

这里设置联合唯一键是为了保证同一个用户对同一个景点只有一条评分记录,在算法计算时不会出现重复计数的问题。

提示:爬虫的合规性值得注意。我只爬取平台公开的信息,并自觉遵守网站的robots协议,控制访问频率,采集数据仅用于学习演示。如果你的课题需要更大的数据量,建议结合公开数据集或申请官方API,效果更稳定也更体面。

3. 协同过滤推荐算法核心实现

3.1 协同过滤的基本原理与数学表达

推荐系统的核心问题可以表述为:给定一个用户u,预测他对未访问过的景点i的评分,并选出评分最高的N个景点推荐给他。协同过滤算法的关键假设是“物以类聚、人以群分”——过去行为相似的用户,未来的偏好也相似。

基于物品的协同过滤(ItemCF)实现思路分三步:

  1. 根据用户的历史评分数据,构建“用户-景点”评分矩阵。
  2. 计算景点之间的相似度,生成相似度矩阵。
  3. 对于用户未访问过的景点,利用用户已访问景点的评分,加权求和得到预测评分,取TopN推荐。

景点间相似度采用余弦相似度计算。将两个景点被所有用户评分的向量看作空间中的两个向量,余弦相似度衡量的是两个向量的夹角大小。公式如下:

similarity(i, j) = (向量i · 向量j) / (|向量i| * |向量j|)

如果两个景点经常被同一批用户评分,它们的评分向量方向越接近,相似度得分越高。生活化地理解:篮球和足球经常被同一群体育爱好者购买,它们的评分向量就很接近,余弦相似度自然很高。

3.2 基于物品的协同过滤Python实现

整个算法计算过程用pandas可以非常优雅地实现。核心流程如下:

import pandas as pd import numpy as np def load_rating_data(): # 从MySQL读取用户评分数据 df = pd.read_sql("SELECT user_id, scenic_id, rating FROM user_rating", engine) return df def build_user_scenic_matrix(df): # 构建用户-景点评分矩阵,缺失值填0 matrix = df.pivot_table(index='user_id', columns='scenic_id', values='rating').fillna(0) return matrix def compute_item_similarity(matrix): # 计算景点间的余弦相似度 item_matrix = matrix.T # 转置后每行是一个景点的评分向量 # 手动实现余弦相似度,不使用sklearn,便于在报告中推导公式 norm = np.sqrt((item_matrix ** 2).sum(axis=1)) similarity = pd.DataFrame( np.dot(item_matrix, item_matrix.T) / np.outer(norm, norm), index=item_matrix.index, columns=item_matrix.index ) return similarity def predict_rating(user_id, scenic_id, rating_matrix, similarity_matrix, k=10): # 预测用户对未访问景点的评分 if scenic_id in rating_matrix.columns: user_ratings = rating_matrix.loc[user_id] # 找到该用户评分过的景点 rated_items = user_ratings[user_ratings > 0].index if len(rated_items) == 0: return 0 # 按相似度排序取前k个相近的已评分景点 sim_scores = similarity_matrix.loc[scenic_id, rated_items] sim_scores = sim_scores.sort_values(ascending=False).head(k) # 排除相似度为0的项 sim_scores = sim_scores[sim_scores > 0] if len(sim_scores) == 0: return 0 # 加权平均计算预测评分 weighted_sum = np.sum(sim_scores.values * user_ratings[sim_scores.index].values) return weighted_sum / np.sum(sim_scores.values) return 0 def recommend(user_id, rating_matrix, similarity_matrix, top_n=10): # 对用户所有未评分的景点进行预测评分并排序 unrated_items = rating_matrix.columns[rating_matrix.loc[user_id] == 0] predictions = [] for scenic_id in unrated_items: pred = predict_rating(user_id, scenic_id, rating_matrix, similarity_matrix) predictions.append((scenic_id, pred)) predictions.sort(key=lambda x: x[1], reverse=True) return predictions[:top_n]

这里有一个细节值得展开讲:为什么取前k个相似景点时要先排除相似度为0的项?因为旅游数据比较稀疏,很多景点之间根本没有共同的评分用户,余弦相似度天然为0。如果保留这些0分项,加权平均时它们会稀释掉有效相似景点的权重,导致预测结果整体偏低。我在调参时对比过,排除相似度为0的项之后,推荐结果的命中率提升了约10个百分点。

3.3 冷启动问题的兜底策略

协同过滤算法有一个天然缺陷:冷启动。新用户没有任何评分记录,算法无法为他计算个性化推荐。新景点也没有用户评分,无法参与相似度计算。

我的处理策略是双层的:

  • 新用户冷启动:如果用户历史上没有评分,则直接返回平台的热门景点TopN。热门程度的衡量方式是“热度指数 = 平均评分 × 0.7 + 评论数量 × 0.3”,这样综合了质量与热度,比单纯按评分排序更合理。
  • 新景点冷启动:如果景点没有评分向量,则根据它的类型标签(自然风光、人文古迹等)和城市信息,找到同类景点的平均评分作为预测值,参与排序但不置顶显示,等待后续有用户评分后自然纳入协同过滤计算。

兜底策略虽然技术上不复杂,但它在答辩时很加分,能体现你对推荐系统业务问题的完整思考。

4. 可视化大屏设计与实现

4.1 可视化需求拆解

推荐系统有了数据和算法,最终要用直观的方式把结果呈现出来。可视化的设计思路围绕三条主线展开:

  1. 数据概览:让用户一眼看到平台的景点总量、用户总量、总评分记录数、平均评分等核心指标。
  2. 景点分布分析:按城市、景区类型进行统计,用地图热力图和柱状图展示景点热度。
  3. 推荐结果展示:用户输入自己的ID后,系统展示为他推荐的景点列表,同时展示他的评分历史画像。

整体页面采用Dashboard布局,左侧为各类统计图表,右侧为推荐结果面板。选这个布局是因为它信息密度高,一张屏幕能看完核心内容,演示起来非常方便。

4.2 图表类型与选型逻辑

不同类型的图表承担不同功能,不能为了炫技而乱用:

展示内容图表类型选型原因
各省/城市景点数量分布中国地图热力图空间感强,直观看出旅游资源的地区分布
热门景点Top10横向柱状图景点名称较长,横向排列更易阅读
景点类型占比环形饼图分类占比场景清晰
评分区间分布直方图直观反映评分数据的整体分布形态
景点热度与评分散点图散点图观察“高分低热”和“高热带低分”的异常景点

ECharts地图热力图的数据格式是经纬度加数值,需要把景点所在城市经纬度加工进去。我整理了一份城市经纬度对照表,爬虫采集到城市名称后直接关联上去,前端地图自动渲染。

4.3 Flask后端数据接口设计

前端页面通过AJAX请求后端接口获取数据。后端统一返回JSON格式,前端拿到数据后构造ECharts的option对象。接口设计如下:

from flask import Flask, jsonify, request import json app = Flask(__name__) @app.route('/api/overview') def api_overview(): # 返回景区总数、用户总数、评分总数、平均评分 return jsonify({ "total_scenic": 310, "total_user": 520, "total_rating": 6283, "avg_score": 4.2 }) @app.route('/api/hot_top10') def api_hot_top10(): # 返回热门景点Top10名称和热度指数 data = get_hot_top10_from_db() return jsonify(data) @app.route('/api/city_distribution') def api_city_distribution(): # 返回城市景点分布数据,格式适合地图热力图 data = get_city_stats_from_db() return jsonify(data) @app.route('/api/recommend/<int:user_id>') def api_recommend(user_id): # 调用协同过滤算法,返回TopN推荐列表 result = recommend_for_user(user_id) return jsonify({"user_id": user_id, "recommendations": result})

一个实用的开发技巧:前端调试阶段,先在后端写死返回几条测试数据,确认图表能正常渲染后,再逐步对接真实数据接口。这样可以避免“前端报错分不清是数据问题还是渲染问题”的情况。

5. 系统集成与完整流程演示

5.1 项目代码结构规划

系统前端展示、后端接口、算法模块、数据脚本四类代码必须分开组织,不然项目到后期混乱到你自己都找不到文件。我的项目目录结构是:

travel_recommend/ ├── app.py # Flask入口,路由注册 ├── requirements.txt # 依赖软件包列表 ├── config.py # 数据库连接等配置 ├── crawler/ │ ├── scenic_spider.py # 景点信息爬虫 │ └── rating_spider.py # 用户评分爬虫 ├── model/ │ ├── data_preprocess.py # 数据清洗 │ ├── item_cf.py # 协同过滤算法 │ └── evaluate.py # 离线评测 ├── static/ │ ├── css/ │ └── js/ ├── templates/ │ └── index.html # 可视化大屏页面 ├── data/ │ ├── scenic_info.csv │ └── user_rating.csv └── docs/ └── 设计报告.md # 项目文档

保持目录清晰的好处很多,最直观的一点:答辩时评委让你现场演示系统,你能快速定位到关键文件讲解,而不是在十几个命名混乱的文件夹里翻找。

5.2 系统核心流程演示

从用户打开系统到获得推荐,完整流程共分五步:

  1. 用户打开http://localhost:5000,页面加载可视化大屏。
  2. 页面通过AJAX请求概览、热门景点、城市分布等接口,渲染统计图表。
  3. 用户在推荐面板输入自己的用户ID(如10001),点击“获取推荐”。
  4. Flask接收请求,调用item_cf.py中封装好的推荐函数,加载评分矩阵与相似度矩阵,计算Top10推荐。
  5. 推荐结果以卡片列表形式返回前端,包括景点名称、城市、预测评分和推荐理由。

第4步中有一个工程上的优化点:相似度矩阵在系统启动时只计算一次并缓存到内存,而不是每次用户请求时重新计算。因为ItemCF的相似度矩阵只依赖于评分数据,评分数分钟级别才会更新一次。如果每次请求都从原始评分数据重新计算,响应时间会从毫秒级变成秒级,系统体验会大打折扣。

5.3 推荐效果离线评估

为了证明算法有效,我做了简单的离线评估。评估方式采用留一法:随机抽取每个用户一条评分记录作为测试集,其余作为训练集。在训练集上构建协同过滤模型,预测测试集中用户的评分,然后看推荐列表中是否包含用户真正访问过的景点。

评估指标数值
测试用户数120
平均预测评分误差(MAE)0.83
Top5命中率31.7%
Top10命中率46.9%

Top10命中率46.9%意味着约一半的用户,其真实访问过的景点出现在推荐列表前十中。对于稀疏评分数据来说,这个效果已经可以接受了。补充说明,如果提高相似度计算中的k值(取前k个相近景点数),命中率会略有提升,但计算耗时也会增加,实际使用中k取10-20比较合适。

6. 常见问题与踩坑实录

6.1 爬虫频繁被拦截怎么办

这是整个项目里最让人头疼的问题。一开始我为了追求速度,把并发开到了8个线程,结果不到5分钟全部IP被限制。后来的解决思路是“慢就是快”:单线程 + 随机延时2-5秒 + 请求头轮换,整体爬取速度虽然慢了,但稳定性大幅提升,再也没有出现大规模封禁。

另外建议把已爬取的数据定期备份到本地CSV,即使爬虫中途挂掉,也能断点续爬,不用从头再来。这算是爬虫工程的底线意识。

6.2 数据稀疏导致推荐结果差

用户评分矩阵中,约78%的格子是空的。这种情况下计算出的景点相似度会偏向“大众化”,推荐结果容易变成热门景点排行,失去个性化特质。

我试过几种缓解方案,实际效果从好到差排列为:引入时间衰减因子 > 调整相似度计算方式(只统计共同评分用户数超过5的相似对) > 直接填充均值。时间衰减因子很好理解:用户最近的评分行为比几个月前的行为更能反映当前偏好,计算权重时可以给它更高的系数。

6.3 可视化图表加载缓慢

地图热力图数据量大时,ECharts渲染卡顿明显。排查后发现原因有二:一是接口返回的城市数据未经聚合,300多个点全部传给前端;二是地图GeoJSON文件过大,加载耗时长。

解决方案很简单:后端聚合数据,把同一城市内的景点数量合并成一个数据点,前端只需渲染几十个点。地图GeoJSON选用精简版,保留省级边界即可,渲染速度提升了近一倍。

6.4 系统启动报错快速排查

把整个开发过程中遇到的高频报错整理成速查表,直接对照解决:

报错现象可能原因解决方案
ModuleNotFoundError: No module named 'flask'未安装依赖执行pip install -r requirements.txt
pandas.read_sql失败缺少pymysql驱动执行pip install pymysql,并在连接串中加上charset=utf8mb4
端口被占用5000端口被其他程序占用改用app.run(port=5001)
页面图表空白前端JS报错,多为数据格式不匹配打开浏览器开发者工具,查看Network中接口返回字段是否与前端代码一致
中文乱码数据库字符集不是utf8建库时指定DEFAULT CHARACTER SET utf8mb4

项目做完之后我的体会很深:这类综合项目的难点不在某个单一技术,而在于模块之间的衔接。爬虫采到数据后要想到数据库字段设计,数据库中存什么样的表结构直接决定算法代码好不好写,算法算出来的结果又要考虑前端如何展示。每一步都在为上一步兜底,也在为下一步铺路。

最后再分享一个小技巧:答辩演示前,一定要准备一套预置好的演示用户ID,这些用户在数据集里拥有丰富的历史评分记录,推荐结果更有说服力。现场从零推荐一个新注册用户出来的效果,大概率只能给你推荐热门景点,说服力差很多。提前准备好3个不同行为偏好的用户ID,可以让你的系统展示出更强的个性化能力。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 5:00:46

5款电脑端批量剪辑混剪工具实测盘点,效率与差异化兼得

2026年了&#xff0c;如果你还在靠人工一帧一帧地剪短视频、一天只能交三五条成片&#xff0c;那我建议你真的要认真看一下电脑端批量剪辑混剪工具这几年进化到什么程度了。我自己是因为接了矩阵账号的代运营需求&#xff0c;每天固定要出二三十条不同主题的短视频&#xff0c;…

作者头像 李华
网站建设 2026/10/15 5:00:42

Vue动态路由后端封装:JavaBean设计与树形组装实战

1. 从后端视角拆解动态路由&#xff1a;为什么JavaBean封装是绕不开的一环做过后端权限系统的朋友大概率都碰过这样一个场景&#xff1a;前端用 Vue 搭好了框架&#xff0c;路由表却没法写死。不同角色登录后看到的菜单不一样&#xff0c;同一个页面不同用户能点的按钮也不一样…

作者头像 李华
网站建设 2026/10/15 5:00:31

Unity PC游戏增量更新插件PATCH:文件级比对与差量分发实战

1. 这个补丁插件到底在解决什么问题做过PC端独立游戏发布的朋友应该都有过这种体验&#xff1a;游戏上线第一天&#xff0c;玩家反馈一个致命bug&#xff0c;你火急火燎改完代码&#xff0c;重新打包&#xff0c;上传一个几百兆甚至几个G的完整安装包&#xff0c;然后告诉玩家“…

作者头像 李华
网站建设 2026/10/15 4:59:52

MySQL视图实战指南:从封装SQL到性能优化与避坑全攻略

MySQL 的视图我用了好几年&#xff0c;说实话&#xff0c;刚开始接触时我完全没把它当回事——不就是把一段 SELECT 语句存起来嘛&#xff0c;数据还是那份数据&#xff0c;查询的时候展开一下&#xff0c;没什么技术含量。但后来真正在项目里用得多了&#xff0c;我才发现这玩…

作者头像 李华
网站建设 2026/10/15 4:59:46

软件工程实习记录:真实开发断点与全栈能力演进图谱

简介&#xff1a;本资源是一份本科软件工程专业学生的真实企业实习记录文档&#xff0c;面向计算机相关专业学生及初入职场的开发者&#xff0c;解决实习报告撰写缺乏实操参考、对软件开发全流程认知模糊等实际问题。文档以周为单位详述2018年暑期在软件公司为期三周的完整实习…

作者头像 李华
网站建设 2026/10/15 4:59:03

xtu C语言基础-试除法判断素数

//试除法判断素数 int is_prime(int n){if(n<2) return 0; if(n2) return 1;if(n%20) return 0; //除2以外所有偶数都不是素数//排除了偶数之后只用遍历奇数for(int i3;i*i<n;i2){ //i*i可以避免浮点数运算if(n%i0) return 0;}return 1; }

作者头像 李华