禁忌搜索算法(python、C++代码) - 教程
0.引言
禁忌搜索(tabu search)的思想源自对人类智力过程的搜索,并通过禁忌准则来避免迂回搜索,并通过藐视准则来赦免一些被禁忌的优良状态,同时保证有效搜索和多样化。
:每次只往比当前位置更高的地方走。但这样很容易走到一个小山丘(局部最优解)就卡住了,因为四周都比它低,你无法再移动。就是想象一下,你在一个巨大的迷宫里寻找宝藏(最优解)。最简单的“爬山法”
禁忌搜索的核心思想就是:允许你暂时走下坡路(接受较差的解),从而有机会跳出局部最优,去寻找更高的山峰(全局最优)。但它不是盲目地向下走,它有一个“记忆”系统(禁忌表)来避免重复和循环。
核心在于禁忌表(记忆走过的路)和藐视准则(“赦免”误放入禁忌表中的资料)。
1.流程

2.更换模块
- 禁忌表长度
决定了算法的记忆长度,太小容易陷入循环(鱼的记忆),太大可能会挡住通往最优解的路。
- 邻域搜索策略
所有启发式算法共同问题
- 藐视准则
该算法中最重要的一个点。一般藐视比历史最优更好的解,但一些困难禁忌搜索藐视准则更加巧妙。
3.代码
3.1 问题及解决手段描述
定义了五个城市,求解如何遍历五个城市得到最短路径。(TSP)
初始解:随机顺序
邻域生成:两点交换
藐视准则:比当前全局最优解更好
3.2 python
import numpy as np
import matplotlib.pyplot as plt
import random
# 城市坐标
cities = {
0: (0, 0),
1: (1, 2),
2: (3, 1),
3: (4, 3),
4: (2, 4)
}
# 计算两个城市之间的距离
def distance(city1, city2):
x1, y1 = cities[city1]
x2, y2 = cities[city2]
return np.sqrt((x1 - x2)**2 + (y1 - y2)**2)
# 计算路径总长度
def total_distance(path):
total = 0
for i in range(len(path)):
total += distance(path[i], path[(i+1) % len(path)])
return total
# 生成初始解
def initial_solution():
path = list(cities.keys())
random.shuffle(path)
return path
# 定义邻域操作:交换两个城市
def get_neighbors(path):
neighbors = []
for i in range(len(path)):
for j in range(i+1, len(path)):
new_path = path.copy()
new_path[i], new_path[j] = new_path[j], new_path[i] # 交换两个城市
neighbors.append(new_path)
return neighbors
# 禁忌搜索算法
def tabu_search(max_iter=100, tabu_size=5):
# 初始化
current_solution = initial_solution()
best_solution = current_solution.copy()
best_distance = total_distance(best_solution)
# 禁忌表
tabu_list = []
# 记录迭代过程
history = []
# 主循环
for iteration in range(max_iter):
# 生成所有邻居
neighbors = get_neighbors(current_solution)
# 评估邻居
best_candidate = None
best_candidate_distance = float('inf')
for neighbor in neighbors:
# 获取移动(交换的城市对,排序以便比较)
move = tuple(sorted((neighbor.index(current_solution[i]), neighbor.index(current_solution[j]))
for i, j in [(i, j) for i in range(len(neighbor)) for j in range(i+1, len(neighbor))
if neighbor[i] != current_solution[i] or neighbor[j] != current_solution[j]])[0])
neighbor_distance = total_distance(neighbor)
# 如果移动不在禁忌表中,或者满足渴望水平(比全局最优解更好)
if move not in tabu_list or neighbor_distance tabu_size:
tabu_list.pop(0)
# 更新全局最优解
if current_distance < best_distance:
best_solution = current_solution.copy()
best_distance = current_distance
print(f"迭代 {iteration}: 找到新最优解,距离 = {best_distance:.4f}")
# 记录历史
history.append(best_distance)
return best_solution, best_distance, history
# 运行禁忌搜索
best_path, best_dist, history = tabu_search(max_iter=50, tabu_size=7)
print(f"\n最优路径: {best_path}")
print(f"最短距离: {best_dist:.4f}")
# 可视化结果
plt.figure(figsize=(12, 5))
# 绘制城市和路径
plt.subplot(1, 2, 1)
for city, coord in cities.items():
plt.plot(coord[0], coord[1], 'o')
plt.text(coord[0]+0.1, coord[1]+0.1, str(city), fontsize=12)
# 绘制路径
path_coords = [cities[city] for city in best_path]
path_coords.append(path_coords[0]) # 回到起点
x, y = zip(*path_coords)
plt.plot(x, y, 'r-')
plt.title(f"最优路径 (距离 = {best_dist:.4f})")
plt.xlabel("X坐标")
plt.ylabel("Y坐标")
# 绘制收敛曲线
plt.subplot(1, 2, 2)
plt.plot(history)
plt.title("收敛曲线")
plt.xlabel("迭代次数")
plt.ylabel("路径长度")
plt.grid(True)
plt.tight_layout()
plt.show()
总结
从某种角度上来看是模拟退火算法的改良,模拟退火算法有概率重新再跑一遍失败的邻域,禁忌表不会。但相比于遗传算法,禁忌搜索需要一个较好的初始解。
在我看来禁忌搜索算法在邻域空间中像一条贪吃蛇一样。不能吃到自己的身体(禁忌表),不算的寻找果子吃(最优解)。

浙公网安备 33010602011771号