旅行推销员问题:Solver-Based
这个例子展示了如何使用二进制整数规划来解决经典的旅行商问题。这个问题涉及找到最短的封闭之旅(路径)通过一组停止(城市)。在这种情况下,有200停止,但你可以很容易地改变nStops
变量大小不同的问题。你会解决最初的问题,看到解决方案subtours。这意味着最优解发现没有给一个连续路径通过所有的点,而是有几个断开连接的循环。然后您将使用一个迭代过程确定subtours,添加约束和运行优化直到subtours消除。
具体问题具体分析的方法,请参阅旅行推销员问题:具体问题具体分析。
问题公式化
制定的旅行推销员问题整数线性规划如下:
生成所有可能的旅行,这意味着所有的成对的停止。
计算每个旅行的距离。
成本函数最小化旅行距离之和为每个访问之旅。
决策变量是二进制,与每一个旅行,其中每个1表示存在于旅游的行程,和每个0代表一个旅行而不是旅游。
确保旅游包括每一停止,包括线性约束,每一站到底两次。这意味着一个到来,一个离开停止。
生成停止
生成随机停止在一个原油的多边形表示美国大陆
负载(“usborder.mat”,“x”,“y”,“xx”,“yy”);rng (3“旋风”)%情节与停在缅因州、佛罗里达州,并且是可再生的nStops = 200;%你可以用任意数量,但问题规模的N ^ 2stopsLon = 0 (nStops, 1);%分配nStops x坐标stopsLat = stopsLon;%分配坐标n = 1;而(n < = nStops) xp =兰特* 1.5;yp =兰德;如果inpolygon (xp, yp, x, y)%在边界测试stopsLon (n) = xp;stopsLat (n) = yp;n = n + 1;结束结束
计算点之间的距离
因为有200停止,19900年有19900次,这意味着二进制变量(变量= 200号选择2)。
生成所有的旅行,这意味着所有成对的停止。
idx = nchoosek (1: nStops, 2);
计算所有旅行的距离,假设地球是平为了使用毕达哥拉斯规则。
dist =函数(stopsLat (idx (: 1))——stopsLat (idx (:, 2)),…stopsLon (idx (: 1))——stopsLon (idx (:, 2)));lendist =长度(经销);
这个定义的经销
向量,参观的长度
dist”* x_tsp
在哪里x_tsp
是二进制解向量。这是旅行的距离,你尽量减少。
创建图表,画地图
代表图的问题。创建一个图形,停止节点和边旅行。
图G = (idx (: 1), idx (:, 2));
用图表显示停止阴谋。阴谋没有图形边缘节点。
图hGraph =情节(G,“XData”stopsLon,“YData”stopsLat,“线型”,“没有”,“NodeLabel”,{});持有在%画出边界之外情节(x, y,的r -)举行从
约束
创建线性约束,每一站有两个相关的旅行,因为必须有一个去每一站和离开每一站的旅程。
Aeq = spalloc (nStops、长度(idx) nStops * (nStops-1));%分配一个稀疏矩阵为2 = 1:nStops whichIdxs = (idx = = 2);%的旅行,包括停止二世whichIdxs =稀疏(sum (whichIdxs, 2));%包括旅行二世的两端Aeq (ii):) = whichIdxs ';%包含在约束矩阵结束说真的= 2 * 1 (nStops 1);
二进制界限
所有的决策变量都是二进制的。现在,设置intcon
参数决策变量的数量,给每一个下界(0),和1的一个上界。
intcon = 1: lendist;1磅= 0 (lendist);乌兰巴托= 1 (lendist, 1);
优化使用intlinprog
问题是准备解决方案。抑制迭代输出,关闭默认显示。
选择= optimoptions (“intlinprog”,“显示”,“关闭”);[x_tsp, costopt exitflag、输出]= intlinprog(经销、intcon [], [], Aeq,说真的,磅,乌兰巴托,选择);
创建一个新的旅行作为边缘图的解决方案。为此,在解决方案中一些值不是整数,并转换生成的值逻辑
。
x_tsp =逻辑(圆(x_tsp));Gsol =图(idx (x_tsp, 1), idx (x_tsp, 2), [], numnodes (G));% Gsol =图(idx (x_tsp, 1), idx (x_tsp 2));%在大多数情况下同样适用
可视化解决方案
持有在突出(hGraph Gsol,“线型”,“- - -”)标题(“解决方案与Subtours”)
在地图上可以看到,解决方案有几个subtours。到目前为止没有指定的约束阻止这些subtours发生。为了防止任何可能的subtour情况发生,你将需要一个令人难以置信的大量的不等式约束。
Subtour约束
因为你不能添加的所有subtour约束,采用迭代的方法。检测subtours在当前的解决方案,然后添加不等式约束,以防止这些特定的subtours发生。通过这样做,你会找到一个合适的旅游在一些迭代。
消除subtours不等式约束。这是如何工作的一个例子是如果你有5分subtour,然后是五行连接这些点来创建subtour。消除这种subtour通过实现一个不等式约束说必须小于或等于四行这5分之间。
更多,找到所有这些5分之间的界限,限制的解决方案没有超过四行。这是一个正确的约束,因为如果5或更多的行中存在一个解决方案,解决方案会有subtour(图表 节点和 边缘总是包含一个周期)。
检测subtours通过确定连接组件Gsol
与边缘图,建立在当前的解决方案。conncomp
返回一个向量的数量每条边所属subtour。
tourIdxs = conncomp (Gsol);numtours = max (tourIdxs);%的subtours数量流(“# subtours: % d \ n”,numtours);
# subtours: 27
包括线性不等式约束来消除subtours,反复调用解算器,直到只剩下一个subtour。
lendist = spalloc (0, 0);%分配一个稀疏矩阵线性不等式约束b = [];而numtours > 1直到只有一个subtour %重复%添加subtour约束b = [b; 0 (numtours, 1)];%分配bA = [; spalloc (numtours、lendist nStops)];%猜测有多少零来分配为2 = 1:numtours rowIdx =大小(A, 1) + 1;%为索引计数器subTourIdx =找到(tourIdxs = = 2);当前subtour %提取%的下一行找到所有相关的变量%特定subtour,然后添加一个不等式约束禁止%,subtour和所有使用这些subtours停止。变化= nchoosek(1:长度(subTourIdx), 2);为jj = 1:长度(变化)whichVar =(总和(idx = = subTourIdx(变化(jj, 1), 2)) &…(和(idx = = subTourIdx(变化(jj, 2)), 2));(rowIdx whichVar) = 1;结束b (rowIdx) =长度(subTourIdx) - 1;%不如subtour停止旅行结束%再次优化[x_tsp, costopt exitflag、输出]= intlinprog(经销、intcon A、b Aeq,说真的,磅,乌兰巴托,选择);x_tsp =逻辑(圆(x_tsp));Gsol =图(idx (x_tsp, 1), idx (x_tsp, 2), [], numnodes (G));% Gsol =图(idx (x_tsp, 1), idx (x_tsp 2));%在大多数情况下同样适用%可视化的结果hGraph。线型=“没有”;%删除之前的突出显示的路径突出(hGraph Gsol,“线型”,“- - -”)drawnow%多少subtours呢?tourIdxs = conncomp (Gsol);numtours = max (tourIdxs);%的subtours数量流(“# subtours: % d \ n”numtours)结束
# subtours: 20 # subtours: 7 # subtours: 9 # subtours: 9 # subtours: 3 # subtours: 2 # subtours: 7 # subtours: 2 # subtours: 1
标题(“解决方案与Subtours消除”);持有从
解决方案质量
解决方案是一个可行的旅游,因为它是一个闭环。但这是最小成本之旅吗?发现的一个方法是检查输出结构。
disp (output.absolutegap)
0
渺小的绝对差距意味着解决方案是最优或接近最优的总长度。