三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

禁忌搜索算法:原理、实现与在数学建模竞赛中的应用

禁忌搜索算法:原理、实现与在数学建模竞赛中的应用

1. 项目概述:为什么是禁忌搜索?

如果你正在备战数学建模竞赛,尤其是像美赛(MCM/ICM)这样强调创新与求解效率的赛事,那么“禁忌搜索算法”绝对是你武器库里不可或缺的一件利器。我第一次在国赛里用它解决一个复杂的车辆路径规划问题时,那种看着算法一步步跳出局部最优、找到更优解的感觉,至今记忆犹新。它不像遗传算法那样需要设计复杂的交叉变异,也不像模拟退火那样对降温曲线敏感,禁忌搜索的核心思想非常直观:记住最近走过的“坏”路,强迫自己去探索新的方向。这种“健忘”与“强制探索”的巧妙结合,让它特别适合解决那些解空间巨大、传统方法容易陷入局部最优的组合优化问题,比如美赛中常见的调度问题、网络设计、资源分配等。

简单来说,禁忌搜索是一种元启发式算法。它不是针对某个特定问题的精确解法,而是一个求解框架。你可以把它理解为一个有“短期记忆”的登山者:他想爬到最高的山顶(找到全局最优解),但山峦起伏,他很容易爬到一个小山包(局部最优)就以为到顶了。为了避免这种情况,他会记住最近几步走过的路(放入“禁忌表”),在一段时间内禁止回头走这些路,从而被迫去探索其他可能通向更高主峰的小径。这个“短期记忆”机制,就是它被称为“禁忌”的原因。

对于美赛而言,掌握禁忌搜索的价值在于:

  1. 模型通用性强:无论是A题的连续型优化,还是B、C题中复杂的离散组合优化,经过适当设计,禁忌搜索都能派上用场。
  2. 求解质量高:相比纯粹的贪心算法,它能有效避免早熟收敛,找到质量更高的解。
  3. 算法思路清晰,论文好写:其迭代过程、禁忌表、藐视准则等组件逻辑分明,非常适合在论文的“模型建立与求解”部分进行清晰阐述,体现建模的深度。
  4. 与其它算法融合方便:常作为混合算法的核心局部搜索部件,例如与遗传算法、模拟退火结合,提升整体性能。

接下来,我将结合多年参赛和指导的经验,从算法核心拆解到美赛实战编码,为你呈现一份详尽的禁忌搜索备战指南。

2. 禁忌搜索算法核心原理深度拆解

要真正用好禁忌搜索,不能只停留在“调用工具箱”的层面,必须理解其每一个部件的设计逻辑和相互配合。这就像了解汽车的发动机、变速箱和底盘如何协同工作,才能开得又快又稳。

2.1 核心组件与工作流程

一个标准的禁忌搜索算法框架包含以下几个核心部分,其工作流程如下图所示(概念性描述):

  1. 初始解:算法需要一个起点。生成一个高质量的初始解能显著加快收敛速度。常用方法包括随机生成、贪心算法构造等。
  2. 邻域结构:这是算法的“探索能力”定义。它规定了如何从当前解出发,生成一系列“邻居”解。例如,在旅行商问题中,邻域操作可以是交换两个城市的位置、逆转一段路径等。邻域设计的好坏直接决定了算法搜索的效率和效果。
  3. 禁忌表:算法的“短期记忆”核心。它记录最近进行的移动(或移动导致的解的特征),并禁止在短期内重复这些移动,以避免循环和陷入局部最优。禁忌表通常有长度(禁忌期限),超过长度的记录会被释放。
  4. 藐视准则:也称为“破禁准则”。这是算法的“灵活性”保障。当一个被禁忌的移动能产生一个优于历史最优的解时,即使它被禁忌,也允许执行。这保证了算法不会错过真正优秀的解。
  5. 评价函数:用于评估解的质量,通常是目标函数本身。但在复杂邻域中,有时会采用增量计算来加速评估。
  6. 终止准则:决定算法何时停止。常见的有:迭代次数达到上限、连续若干代最优解未改进、或运行时间达到限制。

算法的迭代过程可以概括为:从当前解出发,在其邻域中寻找未被禁忌(或满足藐视准则)的最佳移动,执行该移动更新当前解,并更新禁忌表和历史最优解,直到满足终止条件。

2.2 关键参数的设计逻辑与经验值

禁忌搜索的性能很大程度上依赖于参数设置。以下是关键参数的设计逻辑和我从实战中总结的经验范围:

参数作用设计逻辑与经验
禁忌长度控制短期记忆的强度。长度太短易循环,太长则限制搜索。动态调整效果往往优于固定值。例如,可以根据搜索进程(解的质量变化、多样性)在区间[5, 20]内调整。对于中等规模问题(如50-100个节点),初始可设为7-10
邻域大小每次迭代考察的候选解数量。影响每步的计算开销和探索广度。需要在广度和深度间权衡。一种策略是逐步扩大:初期使用小邻域快速定位,后期使用大邻域精细搜索。也可根据计算资源设定一个上限,如每次考察100-500个邻居。
终止准则平衡求解时间与质量。**“迭代次数”结合“最大停滞代数”**是最实用的组合。例如,设定最大迭代5000次,同时若连续200代最优解无改进,则提前终止。
初始解策略决定搜索起点的高度。不要满足于纯随机解。即使是一个简单的贪心算法(如最近邻法)构造的初始解,也能为禁忌搜索提供一个极佳的起点,节省大量前期迭代。

实操心得:参数没有“银弹”。最有效的方法是针对你的具体问题,设计一个小规模的实验。固定其他参数,变化一个参数(如禁忌长度),观察算法收敛曲线和解的质量变化,从而找到适合你问题规模的参数敏感区间。这个调参过程本身就可以成为论文中“模型灵敏度分析”的一部分。

2.3 邻域结构设计:算法的灵魂

邻域结构是禁忌搜索最具创造性的部分。以经典的旅行商问题为例,常见的邻域操作有:

  • 2-opt:随机选择两条不相邻的边,断开并重新交叉连接。这是最常用的操作之一,能有效打破路径中的交叉。
  • 交换:随机交换两个城市在路径中的位置。
  • 插入:将一个城市从原位置取出,插入到路径的另一个位置。

在美赛的实际问题中,你需要根据问题特性设计邻域。例如:

  • 资源调度问题:邻域操作可以是交换两个任务的处理顺序,或者将一个任务移动到另一个空闲时间段。
  • 网络布局问题:可以是随机添加/删除一条边,或者改变一个节点的连接对象。

一个高级技巧是使用多种邻域操作的混合。在迭代中随机或按一定规则选择不同的邻域操作,可以增加搜索的多样性,避免陷入某种特定邻域结构导致的局部最优。

3. 美赛实战:从问题到代码的完整实现

我们以一个美赛可能出现的简化问题为例:“校园快递点包裹配送路径规划”。假设有1个快递中心(仓库)和N个宿舍楼点(客户),每个点有已知坐标,要求找出一条从仓库出发,访问所有客户点恰好一次并回到仓库的最短路径(TSP问题)。

3.1 问题建模与算法设计

  1. 解的表达:一个解就是一条访问序列,例如[0, 3, 1, 4, 2, 0],其中0代表仓库,其他数字代表客户点。
  2. 目标函数:总路径长度,即相邻点间欧氏距离之和。
  3. 初始解:采用最近邻贪心算法构造。
  4. 邻域结构:采用2-opt操作。即随机选择两个位置ij(i < j),将路径中ij之间的子路径反转。
  5. 禁忌对象:禁忌被反转的边(即城市对)。例如,如果一次2-opt操作反转了边(A,B)(C,D),变成了(A,C)(B,D),那么就将(A,C)(B,D)这两条新边加入禁忌表,在禁忌期内禁止再次生成它们。
  6. 藐视准则:如果候选解的目标函数值优于历史全局最优解,则无视禁忌,接受该移动。

3.2 Python代码实现与逐行解析

下面是一个完整的、注释详细的Python实现。你可以直接将其作为模板,修改目标函数和邻域操作以适应你的具体问题。

import numpy as np import random import math import time class TabuSearchTSP: def __init__(self, coordinates, tabu_size=10, max_iter=1000, max_stagnation=200): """ 初始化禁忌搜索求解器 :param coordinates: 节点坐标列表,例如 [(x1,y1), (x2,y2), ...] :param tabu_size: 禁忌表大小 :param max_iter: 最大迭代次数 :param max_stagnation: 最大停滞代数(最优解未更新) """ self.coords = np.array(coordinates) self.num_nodes = len(coordinates) self.tabu_size = tabu_size self.max_iter = max_iter self.max_stagnation = max_stagnation # 计算距离矩阵,加速距离计算 self.dist_matrix = self._calc_distance_matrix() # 初始化 self.best_solution = None self.best_cost = float('inf') self.current_solution = None self.current_cost = float('inf') self.tabu_list = {} # 使用字典存储禁忌边及其剩余禁忌期 self.iter_history = [] # 记录每代最优成本,用于画图分析 def _calc_distance_matrix(self): """计算所有点之间的欧氏距离矩阵""" dist = np.zeros((self.num_nodes, self.num_nodes)) for i in range(self.num_nodes): for j in range(i+1, self.num_nodes): d = np.linalg.norm(self.coords[i] - self.coords[j]) dist[i][j] = dist[j][i] = d return dist def _calc_path_cost(self, path): """计算一条给定路径的总长度""" # 路径格式: [0, a, b, c, ..., 0] cost = 0 for i in range(len(path)-1): cost += self.dist_matrix[path[i]][path[i+1]] return cost def _generate_initial_solution(self): """使用最近邻贪心算法生成初始解""" unvisited = list(range(1, self.num_nodes)) # 客户点,0是仓库 path = [0] # 从仓库开始 current = 0 while unvisited: # 找出离当前点最近的未访问点 nearest = min(unvisited, key=lambda city: self.dist_matrix[current][city]) path.append(nearest) unvisited.remove(nearest) current = nearest path.append(0) # 回到仓库 return path def _get_2opt_neighbors(self, path): """生成当前路径的所有2-opt邻居(生成器,节省内存)""" n = len(path) - 1 # 去掉首尾重复的仓库?注意我们的路径是[0,1,2,3,0],实际城市段是path[1:-1] # 更高效的生成方式:只对内部节点进行2-opt操作 for i in range(1, n-1): # i从1开始,避免动起始仓库 for j in range(i+1, n): # j到n-1,避免动结束仓库 if j - i == 1: continue # 相邻点反转等同于原路径,跳过 # 执行2-opt:反转i到j之间的段落 new_path = path[:i] + path[i:j+1][::-1] + path[j+1:] # 计算成本变化量(增量计算,大幅提升效率) # 旧边: path[i-1]->path[i], path[j]->path[j+1] # 新边: path[i-1]->path[j], path[i]->path[j+1] delta = (self.dist_matrix[path[i-1]][path[j]] + self.dist_matrix[path[i]][path[j+1]]) - \ (self.dist_matrix[path[i-1]][path[i]] + self.dist_matrix[path[j]][path[j+1]]) yield new_path, delta, (path[i-1], path[j]), (path[i], path[j+1]) # 返回新边用于禁忌 def solve(self): """执行禁忌搜索主循环""" # 1. 生成初始解 self.current_solution = self._generate_initial_solution() self.current_cost = self._calc_path_cost(self.current_solution) self.best_solution = self.current_solution[:] self.best_cost = self.current_cost print(f"初始解成本: {self.best_cost:.2f}") stagnation = 0 for iteration in range(self.max_iter): best_move = None best_move_delta = float('inf') best_move_edges = None best_neighbor = None # 2. 探索邻域,寻找最佳候选移动 for neighbor, delta, edge1, edge2 in self._get_2opt_neighbors(self.current_solution): # 检查禁忌状态 is_tabued = (edge1 in self.tabu_list and self.tabu_list[edge1] > 0) or \ (edge2 in self.tabu_list and self.tabu_list[edge2] > 0) # 评价移动:delta是成本变化,越小越好 # 藐视准则:如果移动能产生新的全局最优解(current_cost + delta < best_cost),则允许破禁 if (not is_tabued) or (self.current_cost + delta < self.best_cost - 1e-9): if delta < best_move_delta: best_move_delta = delta best_move = (edge1, edge2) best_neighbor = neighbor # 3. 如果没有找到可行移动(理论上不会,但安全起见) if best_move is None: # 可选策略:选择非禁忌中最好的,即使成本增加(允许爬山) for neighbor, delta, edge1, edge2 in self._get_2opt_neighbors(self.current_solution): if delta < best_move_delta: best_move_delta = delta best_move = (edge1, edge2) best_neighbor = neighbor # 4. 执行最佳移动 if best_neighbor is not None: self.current_solution = best_neighbor self.current_cost += best_move_delta # 增量更新成本 # 5. 更新禁忌表 # 禁忌本次移动引入的新边 for edge in best_move: self.tabu_list[edge] = self.tabu_size # 设置禁忌期 # 禁忌表项禁忌期减1,并清除过期项 expired_edges = [] for edge in self.tabu_list: self.tabu_list[edge] -= 1 if self.tabu_list[edge] <= 0: expired_edges.append(edge) for edge in expired_edges: del self.tabu_list[edge] # 6. 更新历史最优解 if self.current_cost < self.best_cost - 1e-9: # 考虑浮点误差 self.best_cost = self.current_cost self.best_solution = self.current_solution[:] stagnation = 0 print(f"迭代 {iteration}: 发现新最优解 {self.best_cost:.2f}") else: stagnation += 1 else: stagnation += 1 self.iter_history.append(self.best_cost) # 7. 检查终止条件 if stagnation >= self.max_stagnation: print(f"提前终止:连续 {stagnation} 代未改进。") break print(f"搜索完成。最终最优成本: {self.best_cost:.2f}") print(f"最优路径: {self.best_solution}") return self.best_solution, self.best_cost, self.iter_history # ========== 示例:运行算法 ========== if __name__ == "__main__": # 随机生成20个点的坐标(包括仓库0) np.random.seed(42) # 固定随机种子,确保结果可复现 num_points = 20 coordinates = [(0, 0)] # 仓库设在(0,0) coordinates.extend([(np.random.uniform(0, 100), np.random.uniform(0, 100)) for _ in range(num_points-1)]) # 创建求解器实例并运行 ts_solver = TabuSearchTSP(coordinates, tabu_size=7, max_iter=500, max_stagnation=50) start_time = time.time() best_path, best_cost, history = ts_solver.solve() end_time = time.time() print(f"总运行时间: {end_time - start_time:.2f} 秒") # 简单可视化(需要matplotlib) try: import matplotlib.pyplot as plt plt.figure(figsize=(12, 5)) plt.subplot(1, 2, 1) coords_array = np.array(coordinates) plt.scatter(coords_array[:, 0], coords_array[:, 1], c='red', s=50, label='节点') plt.scatter(coords_array[0, 0], coords_array[0, 1], c='blue', s=200, marker='s', label='仓库') # 绘制最优路径 path_coords = coords_array[best_path, :] plt.plot(path_coords[:, 0], path_coords[:, 1], 'b-', alpha=0.6, linewidth=1, label='最优路径') for i, (x, y) in enumerate(coordinates): plt.text(x, y, str(i), fontsize=8, ha='center', va='center') plt.title(f"最优路径规划 (成本: {best_cost:.2f})") plt.legend() plt.axis('equal') plt.subplot(1, 2, 2) plt.plot(history, 'g-', linewidth=1) plt.xlabel('迭代次数') plt.ylabel('历史最优成本') plt.title('收敛曲线') plt.grid(True, alpha=0.3) plt.tight_layout() plt.show() except ImportError: print("未安装matplotlib,跳过可视化。")

3.3 代码关键点解析与调优建议

  1. 距离矩阵预计算:在__init__中计算所有点对间的距离并存储为矩阵。这是一个典型的空间换时间的优化。在邻域搜索中需要频繁计算路径长度变化,使用距离矩阵的增量计算(delta)比重新计算整条路径快几个数量级。

  2. 邻域生成器_get_2opt_neighbors函数使用yield返回一个生成器,而不是一次性生成所有邻居列表。对于节点数较多的问题,邻域规模可能非常庞大(O(n²)),一次性生成会消耗巨大内存。使用生成器可以按需生成、逐个评估,内存友好。

  3. 增量评估:这是性能优化的核心。在2-opt操作中,路径总成本的变化只与四条边有关(断开两条旧边,连接两条新边)。代码中直接计算这四条边距离的变化量delta,避免了O(n)的完整路径重计算。

  4. 禁忌表实现:使用字典tabu_list,键为被禁忌的边(元组(city1, city2),确保city1 < city2以保持唯一性),值为剩余禁忌期。每迭代一次,所有禁忌期减1,到期删除。这种实现简单高效。

  5. 破禁准则实现:在评估候选移动时,条件(self.current_cost + delta < self.best_cost - 1e-9)就是破禁准则。1e-9是一个很小的容差值,用于避免浮点数比较的精度问题。

实操心得:在美赛论文中展示算法时,伪代码配合关键代码片段是黄金组合。先给出清晰的结构化伪代码说明算法流程,再附上像上面_get_2opt_neighbors或主循环这样的核心代码片段,并加以解释。这既能体现你的建模能力,也能展示编程实现水平。切忌在论文中粘贴大段完整代码。

4. 美赛应用场景拓展与模型融合思路

禁忌搜索在美赛中很少孤立使用,更多的是作为混合智能算法的核心部件。理解其在不同题型中的应用场景和融合方式,能让你在解题时思路更开阔。

4.1 典型美赛问题适配指南

美赛问题类型可能的应用场景解的表达与邻域操作设计思路
A题 (连续型)参数优化、曲线拟合、复杂函数寻优。将连续变量离散化到一定精度,解表示为参数向量。邻域操作可以是轻微扰动某个参数值,或同时扰动几个参数。
B题 (离散型/大数据)资源调度(如人员排班、机器调度)、网络流优化、聚类分析。解可以是调度序列、分配矩阵或聚类标签。邻域操作:交换任务顺序、移动任务到其他资源、改变节点聚类归属等。
C题 (数据挖掘/洞察)特征选择、模型参数调优、最佳策略搜索。解可以是特征子集(二进制串)或策略组合。邻域操作:翻转某个特征的选择状态、交换两个策略参数等。
D题 (运筹学/网络)车辆路径问题、设施选址、供应链网络设计。这是禁忌搜索的传统强项。解为路径或网络结构。邻域操作常用2-opt、交换、插入、λ-interchange等。
E题 (环境科学)可再生能源系统优化布局、污染物扩散控制点选址。解为选址方案或控制策略序列。邻域操作:增加/删除一个选址点,调整控制策略的参数或顺序。
F题 (政策/社科)政策组合效果模拟、资源分配方案优化。解为一系列政策开关或资源分配比例向量。邻域操作:调整某项政策的强度或开关状态,在总预算约束下微调分配比例。

4.2 混合算法设计:提升求解上限

单一的禁忌搜索可能陷入“高原期”(解质量长时间停滞)。将其与其他算法结合,能取长补短:

  1. TS + 遗传算法:用遗传算法(GA)进行全局探索,维持种群多样性;用禁忌搜索对GA中的个体进行局部深度挖掘。流程可以是:GA每进化一定代数,就选取优秀个体用TS进行局部优化,再将优化结果放回种群。
  2. TS + 模拟退火:在禁忌搜索的每次迭代中,以一定概率(模拟退火的Metropolis准则)接受一个比当前解差的邻居解。这为算法提供了**偶尔“下山”**的能力,有助于跳出局部最优的吸引盆。可以设计一个随迭代次数下降的“接受差解的概率”。
  3. TS + 贪心/构造性算法:用贪心算法快速生成多个不同的高质量初始解,并行或串行地以这些解为起点运行禁忌搜索,最后取最优结果。这利用了多起点搜索的优势,增加找到全局最优的概率。
  4. 自适应禁忌搜索:让算法参数(如禁忌长度、邻域大小)根据搜索状态动态调整。例如,当解质量长期未改进时,增大禁忌长度或邻域大小以加强探索;当频繁找到新优解时,则减小它们以进行精细搜索。

论文写作技巧:在美赛论文中描述混合算法时,画一张清晰的算法融合流程图至关重要。用方框表示各个算法模块,用箭头标明数据(解)的流动方向。同时,需要设计对比实验,证明你的混合算法在求解质量或速度上优于单一算法。即使时间有限,做一个简化版的对比(比如对比TS和纯贪心算法)也能为你的论文增色不少。

5. 实战避坑指南与论文呈现要点

结合我带队和评审的经验,很多队伍在应用禁忌搜索时容易踩一些坑,或者在论文中表述不清。这里集中梳理一下。

5.1 算法实现与调试中的常见问题

  1. 问题:算法收敛太快,解质量不高。

    • 原因:禁忌长度太短,导致在几个局部最优解之间循环;邻域结构设计得太小,搜索能力弱;初始解质量太差。
    • 排查:输出每次迭代的最优解变化曲线。如果曲线很快变平,说明早熟。检查禁忌表内容是否频繁重复。
    • 解决:增加禁忌长度;设计更丰富的邻域操作(如结合2-opt和节点交换);使用更智能的初始解生成方法(如节约算法、最近邻法)。
  2. 问题:算法运行速度极慢。

    • 原因:邻域评估没有使用增量计算;每次迭代评估了整个邻域(对于大规模问题,邻域规模是O(n²)或O(n³));禁忌表检查效率低下。
    • 排查:使用性能分析工具(如Python的cProfile)找到最耗时的函数。
    • 解决
      • 必须实现增量评估,如前面代码所示。
      • 限制候选集:不必评估所有邻居,可以随机采样一部分(如50-100个)进行评估,这是大规模问题常用的策略。
      • 优化禁忌表数据结构:使用哈希表(Python字典)实现O(1)的查找。
  3. 问题:结果不稳定,每次运行差异大。

    • 原因:算法中随机因素过多(如随机初始解、邻域中随机选择),且迭代次数不足。
    • 解决:固定随机数种子(如random.seed(42))以确保结果可复现,这在论文中非常重要。适当增加最大迭代次数或设置更严格的终止条件(如连续多代无改进)。
  4. 问题:破禁准则导致搜索退化。

    • 原因:破禁准则过于宽松,导致算法频繁回到之前被禁忌的“看似好”的区域,破坏了禁忌表的引导作用。
    • 解决:谨慎使用破禁准则。通常只允许在找到历史全局最优解时破禁。也可以设置破禁条件,例如新解必须比历史最优解好出一个阈值(如1%)。

5.2 论文写作中的核心呈现要点

在美赛论文中,关于禁忌搜索的部分,评委希望看到清晰的逻辑和科学的分析,而不仅仅是代码的堆砌。

  1. 模型描述部分

    • 必须给出伪代码。伪代码应简洁明了,突出算法框架、关键步骤(初始解生成、邻域生成、禁忌表管理、破禁准则、终止条件)。
    • 解释清楚关键组件的设计:为什么选择这种邻域操作?(例如,“针对路径问题的交叉特性,我们采用2-opt操作能有效消除路径交叉。”)禁忌对象是什么?(边、解本身、还是解的特征?)为什么?
    • 给出参数设置及其理由:不要只写“禁忌长度=10”。要写“经过初步实验,我们发现当禁忌长度在7-15之间时,算法在求解质量和时间上取得较好平衡。最终我们设定为10。”如果有参数敏感性分析图(如不同禁忌长度下的收敛曲线对比),会是很大的加分项。
  2. 实验结果与分析部分

    • 收敛曲线图:绘制历史最优解随迭代次数的变化曲线。这是证明算法有效性的最直观证据。曲线应呈现初期快速下降,后期平稳的特点。
    • 对比实验:如果可能,将你的TS算法(或混合算法)与一种基准算法(如单纯形法、遗传算法、模拟退火)进行对比。对比指标包括:求得的最优解质量、达到该解所需的迭代次数或时间。
    • 解的可视化:对于路径、调度等问题,将最终解以图形方式呈现(如路径图、甘特图)。一张清晰的图胜过千言万语。
  3. 灵敏度分析部分(如果篇幅允许)

    • 选择1-2个关键参数(如禁忌长度、初始解策略),展示其在不同取值下对最终结果的影响。可以用表格或折线图表示。这体现了你对算法特性的深入理解。
  4. 代码附录

    • 将完整的、可运行的源代码放在附录中。确保代码有清晰的注释,特别是关键函数和算法主循环部分。评委可能会简要浏览代码以确认你的实现是否合理。

最后,记住禁忌搜索的本质是一种引导性的局部搜索。它的强大之处在于利用“记忆”来指导搜索方向,避免重复劳动。在美赛高压环境下,与其追求最新最复杂的算法,不如将一个像禁忌搜索这样经典、可控的算法吃透、用熟,并根据具体问题精心调整。当你能够清晰地向评委阐述你为什么选择它、如何为你的问题定制它、以及它取得了什么效果时,你就已经掌握了用智能算法解决建模问题的精髓。

← 返回列表