禁忌搜索算法(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()

总结

从某种角度上来看是模拟退火算法的改良,模拟退火算法有概率重新再跑一遍失败的邻域,禁忌表不会。但相比于遗传算法,禁忌搜索需要一个较好的初始解。

在我看来禁忌搜索算法在邻域空间中像一条贪吃蛇一样。不能吃到自己的身体(禁忌表),不算的寻找果子吃(最优解)。

posted @ 2025-09-10 18:10  yfceshi  阅读(41)  评论(0)    收藏  举报