简介:面向计算机、通信、人工智能、自动化等专业的毕业设计资源,基于Python实现了粒子群算法在多无人机任务分配中的应用。项目核心代码经过调试测试,可直接运行于主流Python环境,既适合作为课程设计、大作业或毕业设计的参考,也便于初学者沿着算法原理到工程实现逐步进阶。资源包共15个文件,包含9个Python脚本、4张结果可视化图、1份Markdown说明文档及Git属性配置,压缩后整体大小约1.41MB。脚本按功能模块划分,覆盖粒子群算法主程序、距离计算、适应度评估、约束条件判断、时间统计等关键环节;可视化图直观呈现分配效果,说明文档辅助快速理解项目结构。当前已有168人学习下载;作为答辩评审98分的个人项目,其模块化设计使代码具备较强的可读性与扩展性,基础较好的读者可直接修改参数或扩展功能,以适配不同任务场景,也可作为期末课程设计或毕业设计的完整参考。
1. 粒子群算法做多无人机任务分配:这份源码到底帮你解决了什么
粒子群算法原理并不复杂:一群粒子在解空间里飞,每个粒子记住自己找到过的最好位置,群体共享全局最好位置,然后靠速度和位置更新公式往这些好位置靠近。但多无人机任务分配不是这种"找一个连续点"的优化问题,而是要把几十个任务点分给几架无人机,再给每架排出访问顺序——这是典型的组合优化。直接拿标准粒子群算法套上去,你会发现算法不知道"任务分配结果"该用什么数据结构表达,跑出来的东西没法直接落到航线上。这套源码的核心价值,就在于把连续粒子位置和离散任务序列之间的映射写好了,距离矩阵、代价函数、约束惩罚和收敛曲线也都配齐。适合想做毕业设计或课程设计的同学:想用智能优化算法当技术主线,又不想从零去抠编码和解码的细节,拿这份源码做二次开发,比自己造轮子省出两到三周时间。但前提是你得先弄明白里面每一段在干什么——这就是这篇文章要带你过一遍的东西。
2. 拿到源码先跑通:最小工程结构和最基本的启动命令
2.1 先认清源码包里常见的模块和文件职责
这类毕业设计源码的工程结构,各版本会有差异,但绝大多数是同一个套路:一个入口文件跑主流程,一个文件放粒子群算法主体,一个文件放无人机和任务建模,再加一个参数配置和可视化模块。常见的文件划分是这样的:
project/ ├── main.py # 程序入口:读参数、跑迭代、打印结果、画图 ├── pso.py # 粒子群算法主体:粒子类、速度位置更新、迭代循环 ├── model.py # 建模:任务点坐标、无人机数量、距离矩阵、代价函数 ├── config.py # 全局参数:任务数、无人机数、种群、迭代、权重系数 └── visual.py # 结果展示:分配拓扑图、收敛曲线拿到源码第一步不是急着运行,而是先打开 config.py,把里面的数字看一遍。你要确认的只有三件事:任务数是多少个、无人机是几架、最大迭代次数是多少。这三项直接影响后续所有实验结果,也是你写论文时"实验设置"一节里必须出现的数字。
如果源码没有单独的配置文件,参数一般会以字典形式直接写在 main.py 顶部,或者从外部 JSON 读取。以下是常见的配置结构:
{ "n_tasks": 20, "n_uavs": 4, "map_size": 100, "home": [0, 0], "max_iter": 200, "pop_size": 50, "w_max": 0.9, "w_min": 0.4, "c1": 1.5, "c2": 1.5 }先别动任何数值,原样跑通一次再改。这个顺序很关键,最忌讳一上来就把种群调成 200、迭代调到 1000,然后反复纠结"为什么跑这么慢"——那是在用错误的方法排查。
2.2 最小启动命令:两条命令走完
依赖就两个,numpy 和 matplotlib。粒子群算法本身不需要 sklearn、不需要 torch,这也是选这类源码做毕业设计最省心的地方。安装和执行如下:
python -m pip install numpy matplotlib python main.py如果你本机连 numpy 都没有,先单独执行python -m pip install numpy,装完验证一下:
python -c "import numpy; print(numpy.__version__)"能打印出版本号就说明环境没问题。换源安装也可以,国内网络环境一般用清华或阿里云的镜像,命令是pip install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy matplotlib,这个属于提速技巧,不是必须。
程序跑起来后,终端会持续输出每一代的最优适应度值,跑完会打印最终的任务分配方案和总航程。典型的输出大致长这样:
iter 1, best fitness = 482.36 iter 2, best fitness = 465.11 ... iter 200, best fitness = 357.82 UAV 0: tasks [3, 12, 7, 15] -> route length 96.5 UAV 1: tasks [1, 9, 4] -> route length 82.3 ... total distance = 357.82看到这种输出,说明算法主体和建模部分的代码是通的。如果程序报 ModuleNotFoundError,多数是环境问题;如果跑完输出一堆 NaN,那是初始化或计算里出了问题,往下看第 5 章的排查部分。
2.3 怎么确认"跑通了"而不只是"没报错"
很多人把"程序正常退出"当成"跑通了",这是毕业设计里最常见的误判。确认跑通至少要满足三个信号:第一,输出里每架无人机分配到的任务序号没有重复、没有遗漏;第二,最优适应度值在迭代过程中呈现下降趋势,而不是一条水平线;第三,多随机运行几次,最终结果落在同一个量级,而不是第一次 350、第二次 900。
给你一个快速验证解码逻辑的独立小脚本,不需要依赖源码包也能跑:
import numpy as np # 模拟一个 5 维粒子,代表 5 个任务的优先级 particle = np.array([0.8, 0.1, 0.6, 0.3, 0.9]) order = np.argsort(particle) print("任务处理顺序:", order) # 输出:任务处理顺序: [1 3 2 0 4]粒子位置的数值本身没有物理含义,它只表示"哪个任务优先级更高"。np.argsort 按值从小到大排序,返回的索引就是任务被处理的先后顺序。后面第 3 章会详细讲这种编码方式,这里先建立直觉:粒子的每个维度对应一个任务,维度值的大小决定任务分配的优先级。
3. 粒子群算法原理与任务分配建模:连续粒子怎么映射成可飞的路径
3.1 粒子群算法原理只需抓四个要素
粒子群算法原理可以从鸟群觅食来理解:每只鸟(粒子)在搜索空间里有一个位置和一个速度,飞的过程中记录自己经历过的最好位置(pbest),同时知道整个群体目前找到的最好位置(gbest)。下一时刻往哪里飞,取决于当前速度、朝自己最好位置飞的倾向、朝群体最好位置飞的倾向,三者加权合成。
写成代码就是速度更新加位置更新:
import numpy as np # pop_size: 粒子个数,dim: 粒子维度 pop_size, dim = 50, 20 pos = np.random.uniform(-1, 1, (pop_size, dim)) # 位置初始化 vel = np.random.uniform(-0.2, 0.2, (pop_size, dim)) # 速度初始化 p_best = pos.copy() # 个体历史最优 g_best = pos[0].copy() # 全局最优,先随便放一个 w, c1, c2 = 0.7, 1.5, 1.5 # 惯性权重、个体学习因子、群体学习因子 for _ in range(200): r1, r2 = np.random.rand(pop_size, dim), np.random.rand(pop_size, dim) vel = w * vel + c1 * r1 * (p_best - pos) + c2 * r2 * (g_best - pos) pos = pos + vel pos = np.clip(pos, -1, 1) # 越界拉回边界这里的矩阵写法一次性更新整个种群,比逐粒子 for 循环快很多。w 控制"保持原来飞行方向"的程度,c1 让粒子往自己发现的好位置靠,c2 让粒子往群体发现的好位置靠。后面的参数章会专门讨论怎么调,这里只需要理解:粒子群算法的迭代本质,就是不断用"个体经验 + 群体经验"修正每个解的方向。
3.2 任务分配先建模成带约束的组合优化问题
多无人机任务分配问题需要先数学化。常见设定是:有 m 架无人机,从同一个出发点(机巢或仓库)起飞,要访问 n 个任务点,每个任务点由一架无人机访问且只能访问一次,目标是所有无人机的总航程最小,附加约束是每架无人机的航程不能超过最大续航里程。
距离矩阵是所有计算的底座。任务点坐标一旦确定,任意两个节点之间的欧氏距离就是固定的,所以应该在初始化阶段一次性算好,而不是在适应度函数里反复重算:
import numpy as np def build_distance_matrix(points, home): # points: 各任务点坐标,home: 机巢坐标 nodes = np.vstack([home, points]) # 0 号节点是机巢 n = len(nodes) dist = np.zeros((n, n)) for i in range(n): for j in range(n): dist[i, j] = np.linalg.norm(nodes[i] - nodes[j]) return distdist[i][j]表示从节点 i 到节点 j 的直线距离。之所以包含机巢节点并放在 0 号位,是因为每架无人机的路径都是从机巢出发、访问若干任务点、再回到机巢,往返都要用距离矩阵。
在此基础上,总航程的代价函数长这样:
def total_distance(paths, dist, home_idx=0): total = 0.0 for path in paths: if len(path) == 0: continue prev = home_idx for t in path: total += dist[prev, t] prev = t total += dist[prev, home_idx] # 最后返回机巢 return total这个函数逐条路径累加:从机巢出发到第一个任务点,任务点之间依次相连,最后一段飞回机巢。它是后面适应度函数的基础。注意空路径的无人机直接跳过,不产生航程。
3.3 解码:把粒子的连续位置翻译成具体的任务分配方案
这是整个源码里最值得花时间理解的一段。标准粒子群算法生成的是浮点数位置,但任务分配需要的是一个离散方案:哪个任务给哪架无人机、给完之后每架按什么顺序访问。最常用的解法是"基于排序的编码"(Ranked Order Value,简称 ROV 编码),也叫升序排列索引法。
核心思想一句话:粒子的第 i 维代表第 i 个任务的优先级,值越小优先级越高。每次解码先对整个粒子做排序,得到一个任务序列,再把序列按某种规则切给各架无人机。排序这一步天然保证了每个任务恰好出现一次,这是这个编码方式最漂亮的地方:
def decode(particle, dist, n_uavs, home_idx=0): # particle: 一维数组,长度 = 任务数 order = np.argsort(particle) # 任务优先级排序 # 初始化每架无人机的路径和累计航程 paths = [[] for _ in range(n_uavs)] cost_cur = [0.0] * n_uavs for task in order: best_u, best_add = 0, float("inf") for u in range(n_uavs): if len(paths[u]) == 0: # 飞一个任务也要计算往返 add = dist[home_idx, task] + dist[task, home_idx] else: prev = paths[u][-1] # 把 task 插到当前路径末尾,新增的航程 add = dist[prev, task] + dist[task, home_idx] - dist[prev, home_idx] if add < best_add: best_u, best_add = u, add paths[best_u].append(task) # 更新该无人机的累计航程:原有航程减去旧回巢段,加上新增段 if len(paths[best_u]) == 1: cost_cur[best_u] = dist[home_idx, task] + dist[task, home_idx] else: prev = paths[best_u][-2] cost_cur[best_u] = cost_cur[best_u] - dist[prev, home_idx] + dist[prev, task] + dist[task, home_idx] return paths, cost_cur这段逻辑拆开看有三步。第一步,np.argsort 得到任务优先级序列;第二步,按"新增航程最小"的贪心原则,把每个任务依次分给当前最优的无人机;第三步,更新该无人机的累计航程,注意这里用新增段替换旧回巢段的方式计算,避免每插入一个任务就把整条路径重新算一遍。
贪心分配不是全局最优,但这没关系。解码器只负责把粒子变成可行的任务分配方案,真正的全局搜索交给粒子群算法迭代完成。粒子越飞越好,解码出来的分配方案也跟着变好。这是这类源码最核心的设计思路:连续编码负责搜索,离散解码负责落地。
3.4 罚函数:把续航约束写进适应度里
现实场景中,无人机有最大航程限制。如果一架无人机被分配了太远的任务串,累计航程超过续航上限,这个解就是非法解。处理约束最常见的办法是罚函数:非法解照样参与迭代,但适应度值被加上一个很大的惩罚项,让它在竞争中被自然淘汰:
def fitness(particle, dist, n_uavs, max_range, home_idx=0, penalty=1e4): paths, cost_cur = decode(particle, dist, n_uavs, home_idx) total = sum(cost_cur) over = 0.0 for c in cost_cur: if c > max_range: over += c - max_range return total + penalty * over这里的关键在 penalty 的量级。合法解的代价一般是几十到几百,如果惩罚系数只有 10 或 100,一个超航程的非法解加上惩罚后可能仍然比合法解更小,算法就钻空子:为了省距离,宁可让某架无人机飞超续航。我一般把罚项系数设成合法代价上限的 1e3 到 1e4 倍,确保任何非法解都不可能靠"总航程小"胜出。
写完罚函数后做一个快速正确性检查:把迭代输出的最优粒子用 decode 解出来,逐条路径打印航程,看看有没有哪条超过 max_range。这一步看起来简单,但能拦住后面大量"为什么结果不合理"的排查时间。
4. 参数怎么调:种群、迭代、惯性权重和学习因子的典型设置
4.1 一张参数表先把结论立住
粒子群算法参数不多,但每个参数的影响面都不小。下面是这类源码里最常见的取值范围和调试方向,直接照抄起步即可:
| 参数 | 常见范围 | 影响什么 | 调试建议 |
|---|---|---|---|
| pop_size 种群规模 | 30~50 | 探索广度,种群太小容易早熟 | 20 个任务以上不要小于 30,跑得动就设 50 |
| max_iter 最大迭代 | 100~300 | 收敛深度 | 看收敛曲线,200 代后不再下降就停止 |
| w 惯性权重 | 0.9 递减到 0.4 | 前期探索、后期开发 | 固定 0.7 能用,线性递减效果更好 |
| c1 个体学习因子 | 1.5~2.0 | 追个体最优的力度 | 多数问题用 1.5 和 1.5 就够 |
| c2 群体学习因子 | 1.5~2.0 | 追全局最优的力度 | 想让收敛更快就抬高 c2 到 2.0 |
| vmax 速度上限 | 搜索范围的 10%~30% | 防止粒子乱飞 | 位置在 [-1,1] 时 vmax 设 0.3 以内 |
这里每条都对应一个可观察现象。比如说结果一直很差、收敛进度缓慢,先查种群是不是太小;比如前 20 代就冲到最终值的 90%、后面完全不动,先怀疑 w 衰减太慢导致粒子一直保持大速度震荡。
4.2 惯性权重为什么要线性递减
w 大,意味着粒子在很大程度上保持原有速度,容易大范围飞来飞去;w 小,粒子速度迅速被个体最优和群体最优拉走,行为更像局部精细搜索。任务分配这类问题的搜索空间是组合的,前期需要快速找到若干个有潜力的区域,后期需要在好区域附近精细调整任务顺序,所以 w 从大到小的线性递减是最经典的做法:
for it in range(max_iter): w = w_max - (w_max - w_min) * it / max_iter vel = w * vel + c1 * r1 * (p_best - pos) + c2 * r2 * (g_best - pos) pos = pos + vel当 w 从 0.9 递减到 0.4,前 25% 的迭代里粒子保持较强的自主飞行能力,探索不同区域;后 25% 的迭代里粒子基本只围绕 pbest 和 gbest 附近做小幅修正。这不是玄学,而是粒子群算法原理里最常见的收敛节奏控制手段。如果固定 w=0.7,也能得到可用结果,但在大规模任务下更容易出现"前期没有跳出去、后期想在局部翻新却翻不动"的局面。
4.3 判断参数好坏的三个观察信号
调参不能靠感觉,要看三个信号。第一,收敛曲线应该呈现"快速下降 → 缓慢下降 → 基本不动"的三段形态,前 10%~30% 的迭代里下降最快。第二,如果曲线从第一次迭代就水平不动,说明算法根本没找到改进方向,问题大概率在解码或代价函数,不在参数。第三,如果最终结果和随机搜索差不了太多,说明 w 衰减太快,粒子在还来得及找到好区域的阶段就丧失了速度。
做个快速实验就能判断当前参数组合是否合格:
import numpy as np def run_once(seed, max_iter=200, pop_size=50): np.random.seed(seed) # 此处换成源码里的完整迭代调用 return best_fitness seeds = [0, 1, 2, 3, 4] results = [run_once(s) for s in seeds] print(results)每次跑都固定随机种子,这是调参的基本功。五组不同种子下的最优值如果都在同一个量级、波动不超过 10%,说明参数稳;如果 350 和 900 相差悬殊,说明种群太小或迭代不够,要么调参加量,要么接受"用多个种子取最优"的答辩策略,具体见第 5 章。
4.4 答辩会被问:和遗传算法比,PSO 到底强在哪
几乎没有毕设答辩不放这个问题。遗传算法的核心操作是选择、交叉、变异,靠种群内的基因交换来产生新解;粒子群算法靠个体记忆和群体共享来引导飞行,没有交叉变异操作,所以实现更短、可调参数更少,通常收敛也更快。但代价是容易早熟:当 gbest 落入一个局部最优区域,所有粒子会迅速靠拢,后期很难跳出。
任务分配问题恰恰是个"局部优很多"的组合优化问题,所以毕设里常见的补救做法是加一个对比组:同样的任务点、同样的距离矩阵、同样的种群和迭代规模,用遗传算法跑一遍。两条收敛曲线放进论文里,哪怕 PSO 最后结果和 GA 接近,你也有得讲:PSO 在前 50 代收敛更快,实现复杂度更低;GA 后期略好是因为交叉操作保持了更多多样性。用数据说话,比空口说"粒子群算法很好"有力得多。
5. 复现和改造常见的五个坑:现象、原因、解决一次说清
5.1 换台电脑结果就变了,答辩现场无法复现
现象:同一份 main.py,在自己笔记本上最优总航程是 358,拿到实验室台式机上跑变成 412,答辩时演示又变成 380,每次都不一样。
原因:粒子群算法从初始化到速度更新全是随机过程,而代码里没有对 np.random 做种子固定。随机种子不同,初始粒子分布就完全不同,最终收敛结果自然有波动,这在随机优化算法里极其正常。
解决:在迭代开始前固定随机种子,并把种子值写进论文实验表。加一行代码即可:
import numpy as np np.random.seed(42) # 任意固定整数嫌一种种子不放心,就跑五个种子(0~4),最终把五个最优值都列进表里,取最小值和均值。这个习惯也是给自己留的"后悔药":论文数据如果哪天需要重跑,种子一致,结果就能完全复现,不用从头再来一遍。
5.2 numpy 报 dimensions mismatch / broadcasting error
现象:运行时报ValueError: operands could not be broadcast together或矩阵维度不匹配的错误,程序直接崩掉。最常见的位置是速度更新那一行,以及 pos 初始化和 vel 初始化之间。
原因:粒子维度必须等于任务数,因为粒子的每个维度对应一个任务优先级。很多人初始化位置时用了任务数,初始化速度时用了无人机数,两个数组形状一个是(pop, n_tasks),另一个是(pop, n_uavs),加减运算直接报错。
解决:统一所有数组的维度。进入迭代前先打印形状验证:
print("pos:", pos.shape) print("vel:", vel.shape) # 期望两者都是 (pop_size, n_tasks)如果 vmax 也想写成数组,可以写成vmax = 0.3 * np.ones(n_tasks),保证广播规则一致。这条排查方式能覆盖掉大部分 numpy 相关崩溃。
5.3 输出路径里出现重复任务或漏分配任务
现象:程序跑完后打印分配方案,发现某个任务被两架无人机都访问了,或者某个任务从头到尾没出现在任何一条路径里。还有更隐蔽的情况:任务总数和所有路径里的任务数对不上。
原因:解码方式用错了。有的人把粒子位置[-1, 1]按数值大小均分成几段,每段对应一架无人机,一段里落几个点就分几个任务。这样导致一个任务可能落在两段边界的重合处,或者某架无人机一个任务都没分到。这种取整分段的方法在论文里也讲不通,因为没法保证约束。
解决:放弃分段,改用排序解码。粒子的每个维度只表达优先级,np.argsort 返回一个排列,天然保证每个任务恰好出现一次,不需要额外检查重复或遗漏。把 3.3 节里的 decode 函数替换进去,问题从根上消失。以后如果要改编码方式,记得在模型一栏写明"基于排列的编码",这是答辩时的加分项。
5.4 惩罚系数太小,算法输出一堆超续航的路径
现象:适应度值看起来很漂亮,总航程 300 出头,但把最优粒子解码后逐条路径打印,发现其中一架无人机的航程是 180,明显超过 max_range=120。最迷惑的地方是:适应度函数里明明写了惩罚项,为什么没用?
原因:惩罚系数设置得太温和。合法解代价在几十到几百的区间里,penalty 如果只设 10 或 100,超航程 60 给加 600 的惩罚,非法解的总代价可能还是比合法解更小。粒子群算法不会主动避开违法解,它只看适应度值的大小——哪个值小就追哪个。
解决:把 penalty 提到 1e3~1e4 量级,确保任何超航程解的总代价都远大于最差的合法解。调完之后做一个合法性校验:
paths, cost_cur = decode(g_best, dist, n_uavs, home_idx) for u, c in enumerate(cost_cur): print(f"UAV {u}, route length = {c:.2f}") assert c <= max_range, f"UAV {u} exceeds max range"assert 一放,非法方案当场报错,最直接。
5.5 任务规模一大就明显变慢
现象:20 个任务时跑得挺快,加到 50 个任务后每次迭代要等好几秒,100 个任务直接慢到没法用。
原因:两个典型浪费。第一,适应度函数里每次迭代重新计算整个距离矩阵,距离矩阵是 O(n^2) 的,任务 100 个就有一万个距离要算;第二,decode 里逐任务的 Python 循环开销随着任务数线性增长,再叠加种群的几十倍,整个程序就卡在无效计算上。
解决:距离矩阵在初始化阶段算一次全局变量存好,适应度里只查表不重算。可视化不要每代都重绘,只画最后一代。decode 的贪心分配循环暂时保留,这是 Python 代码的常态,但如果任务超过 200 个,建议用 numpy 把内层循环改成向量化操作。对毕业设计的数据量来说,前两条优化通常就能让运行时间回到可接受范围。
6. 进阶:把"能跑的源码"变成"能答辩的完整作品"
源码能跑只是起点,答辩能不能站稳,取决于你能不能让这个课题看起来是一个完整的、经得起追问的工作。三个方向性价比最高:加一个对比算法、加一条真实约束、把结果做成可视化。
对比算法最好做的是遗传算法,因为同样是种群迭代范式,实验条件好控制:同样的任务点、同样的距离矩阵、同样的种群规模和迭代次数,只把更新机制换成选择、交叉、变异。跑完对比后画两条收敛曲线,一条 PSO、一条 GA,论文里就有了第一个像样的实验图。再加一条什么都不做的随机搜索作为底线,三条线放在一起,说服力立刻不同。
约束方面,在已有的最大航程惩罚之外,最常见的是给各架无人机设置不同的起飞点,或者给任务点加上时间窗要求,比如某些任务必须在某段时间窗口内完成。这需要改两层:数据层把点坐标换成多个起点,解码层给每架无人机配一个 home 节点。工作量不大,但讲起来是"针对实际约束做的改进",比单纯调参体面得多。
可视化是很多源码自带的短板,一般只画一个静态分配图。如果你想做得更出彩,可以用 matplotlib 画出任务点、机巢和每架无人机的路径连线,颜色区分:
import matplotlib.pyplot as plt # 假设 paths 是解码结果,points 是任务点坐标,home 是机巢坐标 colors = ["r", "g", "b", "m"] for u, path in enumerate(paths): xs = [home[0]] + [points[t][0] for t in path] + [home[0]] ys = [home[1]] + [points[t][1] for t in path] + [home[1]] plt.plot(xs, ys, color=colors[u % 4], marker="o", label=f"UAV {u}") plt.scatter(home[0], home[1], c="k", marker="s", label="Home") plt.legend() plt.show()如果想让答辩演示更生动,还可以用 opencv-python 把每一代的最优路径渲染成帧,做成逐帧动画。安装命令是python -m pip install opencv-python,把每次迭代后的 gbest 路径画到画布上,再用 VideoWriter 写成视频文件。这个工作量大约两个小时,但演示效果往往比一堆数据表格更能说明问题。
最后提醒答辩必准备的三问。一问"你怎么保证每个任务只执行一次",答排序编码,argsort 保证是一个排列。二问"惯性权重为什么从小到大衰减",答前期全局探索、后期局部精细搜索。三问"粒子群算法容易早熟,你怎么处理",答多随机种子取最优、和遗传算法做对比实验、必要时在迭代后期加局部搜索扰动。这三问答顺了,答辩就立住了。我自己当年最深刻的教训是实验数据没有存档,答辩前一天想补一张收敛曲线图,结果换个随机种子跑出来的结果对不上论文里的数字,被迫全家桶重跑。把种子、参数、结果文件名都留好,这是毕设季最值钱的习惯。希望帮到你。
本文还有配套的精品资源,点击获取