TSP中消除子环路的方法callback实现,使用solumn数据集里面的'c101'数据集进行验证,选取前十个点进行验证
TSP问题一般形式
约束3是子环路消除约束
import numpy as np
from gurobipy import *
import math
import copy
import itertools
import networkx as nx
#TSP旅行商问题,callback法
def readData(path, nodeNum):
cor_X = []
cor_Y = []
with open(path, 'r') as f:
count = 0
for line in f.readlines():
count += 1
if count >= 10 and count <= 10 + nodeNum:
s = line.strip(' ').split()
cor_X.append(float(s[1]))
cor_Y.append(float(s[2]))
distance = np.zeros((nodeNum, nodeNum))
for i in range(nodeNum):
for j in range(i + 1, nodeNum):
dis = int(math.sqrt((cor_X[i] - cor_X[j]) ** 2 + (cor_Y[i] - cor_Y[j]) ** 2))
distance[i][j] = distance[j][i] = dis
return distance
def getValue(X, nodeNum):
x_value = np.zeros((nodeNum+1, nodeNum+1))
for key in X.keys():
x_value[key[0], key[1]] = X[key].x
return x_value
def getRoute(x_value):
x = copy.deepcopy(x_value)
current = 0
path = [current]
for i in range(len(x)):
for j in range(len(x[i])):
if(x[i][j] > 0):
current = j
path.append(current)
path[-1]=0
return path
def findEdges(graph):
edges = []
i, j = np.nonzero(graph > 0.5) # 返回大于阈值的元素的行列
for k in range(len(i)):
edges.append((i[k], j[k])) # 添加边
return edges
def subtour(graph):
edges = findEdges(graph)
G = nx.DiGraph(edges) # 创建有向图
cycles = list(nx.simple_cycles(G)) # 找到所有简单回路
min_cycle = min(cycles, key=len) # 找到最小的回路
return min_cycle
def subtourelim(model, where):
if(where == GRB.Callback.MIPSOL):
x_value = np.zeros((nodeNum + 1, nodeNum + 1))
x_value1 = model.cbGetSolution(model._vars)
for key,value in x_value1.items():
x_value[key[0]][key[1]] = value
tour = subtour(x_value)
if(len(tour) < nodeNum + 1):
model.cbLazy(quicksum(model._vars[i, j]
for i,j in itertools.permutations(tour, 2))
<= len(tour)-1) # 添加约束,消除子回路
path = r'C:\Users\11366\Desktop\solomn_data\c101.txt'
nodeNum = 10 # 节点数量
distance = readData(path, nodeNum) # 读取距离矩阵
model = Model()
# 创建变量
X = {}
for i in range (nodeNum+1):
for j in range(nodeNum+1):
if i!= j:
X[i, j]=model.addVar(vtype = GRB.BINARY, name = 'x_' + str(i) + '_' + str(j))
obj = LinExpr(0)
for key in X.keys():
i,j = key[0],key[1]
if(i < nodeNum and j < nodeNum):
obj.addTerms(distance[i][j], X[key])
elif(i == nodeNum):
obj.addTerms(distance[0][j], X[key])
elif(j == nodeNum):
obj.addTerms(distance[i][0], X[key])
model.setObjective(obj, GRB.MINIMIZE)
for j in range(1,nodeNum+1):
exp = LinExpr(0)
for i in range(nodeNum):
if i!= j:
exp.addTerms(1,X[i, j])
model.addConstr(exp == 1)
for i in range(nodeNum):
exp = LinExpr(0)
for j in range(1,nodeNum+1):
if i!= j:
exp.addTerms(1,X[i, j])
model.addConstr(exp == 1)
#需要添加x_{ij} + x_{ji} <=1
for i in range(0, nodeNum):
for j in range(i+1, nodeNum):
exp = LinExpr(0)
exp.addTerms(1, X[i, j])
exp.addTerms(1, X[j, i])
model.addConstr(exp <= 1)
model._vars = X
model.Params.lazyConstraints = 1
model.optimize(subtourelim)
x_value = getValue(X, nodeNum)
route = getRoute(x_value)
print('Optimal route:', route)
print('Optimal cost:', model.objVal)
运行结果:
Optimal route: [0, 7, 3, 1, 5, 2, 10, 4, 8, 9, 0]
Optimal cost: 48.0