这个例子展示了如何使用二进制整数规划来解决经典的旅行商问题。这个问题涉及到找到通过一组站点(城市)的最短的封闭旅行(路径)。在这种情况下有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);
问题已准备好解决方案。要抑制迭代输出,请关闭默认显示。
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(带有 节点和 边缘总是包含一个循环)。
通过识别连接的组件来检测子流量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.
绝对隙的小于意味着解决方案是最佳的,或者具有接近最佳的总长度。