主要内容

旅行推销员问题:基于求解器

这个例子展示了如何使用二进制整数规划来解决经典的旅行商问题。这个问题涉及到找到通过一组站点(城市)的最短的封闭旅行(路径)。在这种情况下有200个站点,但是你可以很容易地换nstops.变量得到不同的问题大小。您将解决初始问题,并看到解决方案具有子图。这意味着找到的最佳解决方案不会给出贯穿所有点的连续路径,而是有几个不相连的循环。然后,您将使用一个迭代过程来确定子团,添加约束,并重新运行优化,直到子团被消除。

有关基于问题的方法,请参见旅行推销员问题:基于问题

问题制定

制定用于整数线性编程的旅行推销员问题,如下所示:

  • 生成所有可能的行程,即所有不同的站点。

  • 计算每次行程的距离。

  • 最小化的成本函数是旅游中每次旅行的旅行距离的总和。

  • 决策变量是二进制的,并且与每个旅程相关联,其中每个1表示巡回演出中存在的行程,每个0表示不在旅游时的行程。

  • 为确保游览包括每个停止,包括每个停止都在恰好两次旅行的线性约束。这意味着一个到达,一个到达停止。

生成停止

生成随机停止内的一个粗糙的多边形代表美国大陆

加载('usborder.mat'“x”“y”“xx”“yy”);RNG(3,'twister'%绘制以缅因州和佛罗里达州为终点的图,且可重现nStops = 200;你可以用任何数字,但是问题的规模是N^2stopsLon = 0 (nStops, 1);%分配NSTOPS的X坐标stopslat = stopslon;%allocate y-cocordinatesn = 1;(n <= nstops)xp = rand * 1.5;yp = rand;如果Inpolygon(XP,YP,X,Y)在边框内时%测试stopsLon (n) = xp;stopsLat (n) = yp;n = n + 1;结尾结尾

计算点间距离

因为有200个站点,所以有19,900个行程,这意味着19,900个二进制变量(# variables = 200 choose 2)。

生成所有的行程,即所有对的站点。

IDXS = NCHOOSEK(1:NSTOPS,2);

计算所有的旅行距离,假设地球是平的,以使用勾股定理。

dist = hypot(stopsLat(idxs(:,1)) - stopsLat(idxs(:,2)),......stopsLon (idx(: 1))——stopsLon (idx (:, 2)));lendist =长度(经销);

有了这个定义经销矢量,旅游的长度是

dist”* x_tsp

在哪里x_tsp是二元解向量。这是你要尽量缩短的行程。

创建图形和绘制地图

用图表表示问题。创建一个图,其中终点是节点,行程是边。

图G = (idx (: 1), idx (:, 2));

使用图形绘图显示停止。绘制没有图形边缘的节点。

figure hGraph = plot(G,'xdata'stopsLon,“YData”,stopslat,“线型”'没有任何'“NodeLabel”, {});抓住%绘制外界情节(x, y,'r-')举行

约束

创建每个停止有两个相关旅行的线性约束,因为必须有每个停止行程和行程离开每个站点。

AEQ = spalloc(nstops,长度(Idxs),nstops *(nstops-1));%分配稀疏矩阵为了i = 1:nStops which hidxs = (idxs == ii);找出包括站ii的行程哪个XS =稀疏(SUM(WHENIDX,2));%包括ii在两端的行程AEQ(II,:) = andidxs';%包含在约束矩阵中结尾BEQ = 2 *那些(NSTOPS,1);

二元界限

所有决策变量都是二进制的。现在,设置Intcon.参数到判定变量的数量,在每个界限上放置0的下限,并且上限为1。

INTCON = 1:贷款人;LB =零(贷款人,1);UB =(贷款人,1);

优化使用intlinprog

问题已准备好解决方案。要抑制迭代输出,请关闭默认显示。

opts = Optimoptions(“intlinprog”“显示”“关闭”);[X_TSP,COSTOPT,EXITFLAG,输出] = INTLINPROG(DEST,INTCON,[],[],AEQ,BEQ,LB,UB,OPTS);

创建一个以解决方案行程为边的新图形。为了做到这一点,在某些值不是精确整数的情况下,将解决方案舍入,并将结果值转换为逻辑

X_TSP =逻辑(圆形(X_TSP));GSOL = Graph(IDXS(X_TSP,1),IDXS(X_TSP,2),[],NUMNODES(G));% Gsol = graph(x_tsp,1),idxs(x_tsp,2));%也适用于大多数情况

可视化解决方案

抓住突出(hGraph Gsol,“线型”' - ')标题('与副表的解决方案'

从地图上可以看出,这个解决方案有几个分支。到目前为止指定的约束并没有阻止这些subtour发生。为了防止任何可能的subtour发生,你将需要大量的不等式约束。

子我们的限制

因为您不能添加所有的子域约束,所以采用迭代方法。检测当前解决方案中的子图,然后添加不等式约束以防止发生这些特定的子图。通过这样做,您可以在几次迭代中找到一个合适的旅行。

消除不等式约束的子学会。如果您在子台议会中有五个点,那么这是一个例子,那么你有五行连接这些点来创建子。通过实现不等式约束来说明这五点之间必须小于或等于四行,从而消除此子流。

更重要的是,找到这五个点之间的所有直线,并限制解中出现的直线不超过四条。这是一个正确的约束,因为如果一个解中存在5条或更多的直线,那么这个解就会有一个subtour(带有 N. 节点和 N. 边缘总是包含一个循环)。

通过识别连接的组件来检测子流量Gsol,用当前解决方案中的边构建的图。conncomp返回一个带有每条边所属子圈数的向量

TouriDXS = Conncomp(GSOL);numtours = max(touridxs);%子台楼数fprintf(# of subtours: %d\n', numtours);
辅导团:27

包括线性不等式约束来消除子流,并反复调用求解器,直到只需一个子电台。

a = spalloc(0,lendist,0);%分配稀疏线性不等式约束矩阵b = [];numtours> 1%重复,直到只有一个子系统%添加子大楼约束b = [b; 0 (numtours, 1)];%分配B.a = [a; spalloc(numtours,lendist,nstops)];要分配多少个非0为了numtours rowIdx = size(A,1) + 1;%用于索引的计数器subtouridx =查找(touridxs == ii);%提取当前的子大厅的关联的所有变量%特定子游,然后添加不等式约束来禁止%该支线和所有使用这些站点的支线。变化= nchoosek(1:长度(subTourIdx), 2);为了var = (sum(idxs==subTourIdx(variations(jj,1)),2)) &......(和(idx = = subTourIdx(变化(jj, 2)), 2));(rowIdx whichVar) = 1;结尾b(rowidx)=长度(subtouridx) -  1;比短途旅行少一站结尾再次尝试优化[X_TSP,COSTOPT,EXITFLAG,输出] = INTLINPROG(DEST,INTCON,A,B,AEQ,BEQ,LB,UB,OPTS);X_TSP =逻辑(圆形(X_TSP));GSOL = Graph(IDXS(X_TSP,1),IDXS(X_TSP,2),[],NUMNODES(G));% Gsol = graph(x_tsp,1),idxs(x_tsp,2));%也适用于大多数情况%可视化的结果rugk.linestyle =.'没有任何';%删除先前突出显示的路径突出(hGraph Gsol,“线型”' - ') drawnow%这次有多少子流?TouriDXS = Conncomp(GSOL);numtours = max(touridxs);%子台楼数fprintf(# of subtours: %d\n',numtours)结尾
辅导团数量:20
#副图:7
辅导团:9
辅导团:9
# of subtours: 3
# of subtours: 2
#副图:7
# of subtours: 2
# subtours: 1
标题('消除了副表的解决方案');抓住

解决方案质量

该解决方案代表了可行的巡演,因为它是单个闭环。但这是一个最低的成本之旅吗?一个方法可以检查输出结构。

disp(output.absolutegap)
0.

绝对隙的小于意味着解决方案是最佳的,或者具有接近最佳的总长度。

相关的话题