本人作为学生,很没有耐性,往往我看代码都会不在乎理论部分(真想看理论部分的人基本都会直接上百度或者看论文)。当你来到此处,证明你和我一样,不需要长篇大论,想要的不过是一个能跑的代码,并且能够可视化就更加无敌了。
单TSP
废话不多说,直接上效果图(google的ortools算法)
当点的数量为9
是不是觉得这个太简单了,因为点比较少,那我尝试多增加一些点。
点的数量为50
这个路径效果尚可,实用python运行,仅需0.87s即可获得结果,不得不说这是一个非常不错的算法。效果可以说是秒杀一众智能算法(启发式算法)
首先安装ortools包,直接pip install ortools就可以安装。
代码
import time
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
import random
import math
import matplotlib.pyplot as plt
def calculate_distance_matrix(coordinates):
num_points = len(coordinates)
distance_matrix = [[0] * num_points for _ in range(num_points)]
for i in range(num_points):
for j in range(num_points):
if i != j:
x1, y1 = coordinates[i]
x2, y2 = coordinates[j]
distance = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
distance_matrix[i][j] = distance
return distance_matrix
def create_data_model(dis):
"""Stores the data for the problem."""
data = {}
data['distance_matrix'] = dis
data['num_vehicles'] = 1 # Number of salesmen/travelers
data['depots'] = 0
return data
def print_solution(data, manager, routing, solution):
rout = []
"""Prints solution on console."""
max_route_distance = 0
for vehicle_id in range(data['num_vehicles']):
curroute = []
index = routing.Start(vehicle_id)
plan_output = f'Route for vehicle {vehicle_id}:\n'
route_distance = 0
while not routing.IsEnd(index):
# print()
curroute.append(manager.IndexToNode(index))
plan_output += f' {manager.IndexToNode(index)} -> '
previous_index = index
index = solution.Value(routing.NextVar(index))
route_distance += routing.GetArcCostForVehicle(previous_index, index, vehicle_id)
rout.append(curroute)
plan_output += f'{manager.IndexToNode(index)}\n'
plan_output += f'Distance of the route: {route_distance}m\n'
print(plan_output)
max_route_distance = max(max_route_distance, route_distance)
print(f'Maximum of the route distances: {max_route_distance}m')
return rout
##进行坐标的替换
def change(rout,point):
if len(rout) == 1:
rout1 = rout[0]
result = [point[index] for index in rout1]
return result
else:
result = []
for currout in rout:
curresult = [point[index] for index in currout]
result.append(curresult)
return result
def generate_random_points(num_points, value_range):
"""
生成一个随机点的列表。
参数:
num_points : int - 生成点的数量
value_range: (int, int) - 点坐标的取值范围,形如 (最小值, 最大值)
返回:
points : list of tuples - 生成的点列表,每个点表示为 (x, y)
"""
points = []
min_val = 0
max_val = value_range # 解包取值范围为最小值和最大值
for _ in range(num_points):
x = int(random.randint(min_val, max_val)) # 随机生成 x 坐标
y = int(random.randint(min_val, max_val)) # 随机生成 y 坐标
points.append((x, y))
return points
def ortools(Point):
t1 = time.time()
dis = calculate_distance_matrix(Point)
# Instantiate the data problem.
data = create_data_model(dis)
# Create the routing index manager.
manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),
data['num_vehicles'],
data['depots']
)
# Create Routing Model.
routing = pywrapcp.RoutingModel(manager)
# Define the distance callback.
def distance_callback(from_index, to_index):
"""Returns the distance between the two nodes."""
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return data['distance_matrix'][from_node][to_node]
transit_callback_index = routing.RegisterTransitCallback(distance_callback)
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
dimension_name = 'Distance'
routing.AddDimension(
transit_callback_index,
0, # no slack
3000, # maximum distance per vehicle
True, # start cumul to zero
dimension_name)
distance_dimension = routing.GetDimensionOrDie(dimension_name)
distance_dimension.SetGlobalSpanCostCoefficient(100)
# Setting first solution heuristic.
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
# Solve the problem.
solution = routing.SolveWithParameters(search_parameters)
t2 = time.time()
print("花费了: ", t2 - t1, " s")
# Print solution on console.
if solution:
rout = print_solution(data, manager, routing, solution)
result = change(rout, Point)
print(result)
# 确保起点和终点相连
if result[0] != result[-1]:
result.append(result[0])
plt.figure()
# 绘制路径
x, y = zip(*result) # 将点的坐标转置为两个分离的列表
plt.plot(x, y, 'o-', color='blue') # 'o-' 表示以圆圈形式绘制点并连接它们
# 设置图形属性
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Connected Path')
# 显示图形
plt.show()
if __name__ == '__main__':
# Point = [(1, 1), (2, 3), (5, 2), (7, 8), (9, 5), (8, 3), (4, 7), (6, 1), (3, 6)] ##点的示例
Point = generate_random_points(50,20)
ortools(Point)
多TSP
当点的数量为30
这种效果已经很理想了(肉眼可见的理想)
下面直接放源码(直接就能跑)
import time
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
import random
import math
##计算距离矩阵
def calculate_distance_matrix(coordinates):
num_points = len(coordinates)
distance_matrix = [[0] * num_points for _ in range(num_points)]
for i in range(num_points):
for j in range(num_points):
if i != j:
x1, y1 = coordinates[i]
x2, y2 = coordinates[j]
distance = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
distance_matrix[i][j] = distance
return distance_matrix
def create_data_model(dis):
"""Stores the data for the problem."""
data = {}
data['distance_matrix'] = dis
data['num_vehicles'] = 2 # Number of salesmen/travelers
data['starts'] = [0, 0] # Starting nodes for each vehicle
data['ends'] = [0, 0] # Ending nodes for each vehicle, can be different from starts if needed
return data
def print_solution(data, manager, routing, solution):
rout = []
"""Prints solution on console."""
max_route_distance = 0
for vehicle_id in range(data['num_vehicles']):
curroute = []
index = routing.Start(vehicle_id)
plan_output = f'Route for vehicle {vehicle_id}:\n'
route_distance = 0
while not routing.IsEnd(index):
curroute.append(manager.IndexToNode(index))
plan_output += f' {manager.IndexToNode(index)} -> '
previous_index = index
index = solution.Value(routing.NextVar(index))
route_distance += routing.GetArcCostForVehicle(previous_index, index, vehicle_id)
rout.append(curroute)
plan_output += f'{manager.IndexToNode(index)}\n'
plan_output += f'Distance of the route: {route_distance}m\n'
print(plan_output)
max_route_distance = max(max_route_distance, route_distance)
print(f'Maximum of the route distances: {max_route_distance}m')
return rout
##进行坐标的替换
def change(rout,point):
if len(rout) == 1:
rout1 = rout[0]
result = [point[index] for index in rout1]
return result
else:
result = []
for currout in rout:
curresult = [point[index] for index in currout]
result.append(curresult)
return result
def generate_random_points(num_points, value_range):
"""
生成一个随机点的列表。
参数:
num_points : int - 生成点的数量
value_range: (int, int) - 点坐标的取值范围,形如 (最小值, 最大值)
返回:
points : list of tuples - 生成的点列表,每个点表示为 (x, y)
"""
points = []
min_val = 0
max_val = value_range # 解包取值范围为最小值和最大值
for _ in range(num_points):
x = int(random.randint(min_val, max_val)) # 随机生成 x 坐标
y = int(random.randint(min_val, max_val)) # 随机生成 y 坐标
points.append((x, y))
return points
def ortools(Point):
dis = calculate_distance_matrix(Point)
# Instantiate the data problem.
data = create_data_model(dis)
# Create the routing index manager.
manager = pywrapcp.RoutingIndexManager(len(data['distance_matrix']),
data['num_vehicles'],
data['starts'],
data['ends']
)
# Create Routing Model.
routing = pywrapcp.RoutingModel(manager)
# Define the distance callback.
def distance_callback(from_index, to_index):
"""Returns the distance between the two nodes."""
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return data['distance_matrix'][from_node][to_node]
transit_callback_index = routing.RegisterTransitCallback(distance_callback)
# Define cost of each arc.
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
# Add Distance constraint.
dimension_name = 'Distance'
routing.AddDimension(
transit_callback_index,
0, # no slack
3000, # maximum distance per vehicle
True, # start cumul to zero
dimension_name)
distance_dimension = routing.GetDimensionOrDie(dimension_name)
distance_dimension.SetGlobalSpanCostCoefficient(100)
# Setting first solution heuristic.
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
# Solve the problem.
solution = routing.SolveWithParameters(search_parameters)
# Print solution on console.
if solution:
rout = print_solution(data, manager, routing, solution)
result = change(rout, Point)
print(result)
import matplotlib.pyplot as plt
colors = ['blue', 'green', 'red', 'cyan', 'magenta', 'yellow', 'black', 'purple', 'orange', 'brown']
# 二维数组
plt.figure(figsize=(10, 6)) # 可以根据需要调整图形大小
# 绘制每条路径
for idx, line in enumerate(result):
# 确保起点和终点相连
if line[0] != line[-1]:
line.append(line[0])
# 绘制连接线段,从颜色列表中循环选择颜色
color = colors[idx % len(colors)]
x, y = zip(*line) # 将点的坐标转置为两个分离的列表
plt.plot(x, y, 'o-', color=color, label=f'Path {idx + 1}') # 使用 'o-' 表示点和线
# 设置图形属性
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Connected Paths')
plt.legend() # 显示图例
# 显示图形
plt.show()
if __name__ == '__main__':
# Point = [(1, 1), (2, 3), (5, 2), (7, 8), (9, 5), (8, 3), (4, 7), (6, 1), (3, 6)]
Point = generate_random_points(30,50)
ortools(Point)
茶饭闲谈
本人的研究方向为任务分配,实际上就是解决复杂约束条件下的优化问题,本人刚开始也是从TSP入手(实际上两者没多大区别),智能算法目前能够较好的解决这个问题。本人还有许多自己写的算法(包括优化),后续都会公开出来,比如对简单的遗传算法进行多种改进(纯自己创新),虽然效果还凑活,但由于是原创,网上没有,所以完全可以当做课设。