├── .gitignore ├── SA_VRP_Points ├── calculateCost.m ├── createNeibor.m ├── hk48.tsp ├── initModel.m ├── isFeasible.m ├── plotSolution.m ├── randomSol.m └── sa.m ├── SA_VRP_tspInstance ├── calculateCost.m ├── createNeibor.m ├── createNeibor.m~ ├── hk48.tsp ├── initModel.m ├── isFeasible.m ├── randomSol.m └── sa.m └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /SA_VRP_Points/calculateCost.m: -------------------------------------------------------------------------------- 1 | function res = calculateCost(route,model) 2 | city = model.city; 3 | veh = model.veh; 4 | maps = model.maps; 5 | 6 | route = [city+veh route city+veh]; 7 | res = 0; 8 | 9 | for i = 1:length(route)-1 10 | res = res + maps(route(i),route(i+1)); 11 | end 12 | 13 | end -------------------------------------------------------------------------------- /SA_VRP_Points/createNeibor.m: -------------------------------------------------------------------------------- 1 | function newRoute = createNeibor(route,model,mode) 2 | while(1) 3 | switch mode 4 | case 1 5 | newRoute = Swap(route); 6 | case 2 7 | % Do Reversion 8 | newRoute=Reversion(route); 9 | 10 | case 3 11 | % Do Insertion 12 | newRoute=Insertion(route); 13 | end 14 | 15 | if(isFeasible(newRoute,model)) 16 | break; 17 | end 18 | end 19 | end 20 | 21 | function newRoute = Swap(route) 22 | n = numel(route); 23 | 24 | i = randsample(n,2); 25 | i1 = i(1); 26 | i2 = i(2); 27 | newRoute = route; 28 | newRoute([i1 i2]) = route([i2 i1]); 29 | end 30 | 31 | function newRoute = Reversion(route) 32 | n=numel(route); 33 | 34 | i=randsample(n,2); 35 | i1=min(i(1),i(2)); 36 | i2=max(i(1),i(2)); 37 | 38 | newRoute=route; 39 | newRoute(i1:i2)=route(i2:-1:i1); 40 | 41 | end 42 | 43 | 44 | function newRoute = Insertion(route) 45 | n=numel(route); 46 | 47 | i=randsample(n,2); 48 | i1=i(1); 49 | i2=i(2); 50 | 51 | if i1model.city||route(len)>model.city 4 | res = 0; return 5 | end 6 | 7 | for i = 2:len 8 | if route(i)>model.city && route(i-1)>model.city % not use all vehicles 9 | res = 0; return 10 | end 11 | end 12 | 13 | res = 1; 14 | end -------------------------------------------------------------------------------- /SA_VRP_Points/plotSolution.m: -------------------------------------------------------------------------------- 1 | function plotSolution(route,model) 2 | city = model.city; 3 | veh = model.veh; 4 | x0 = model.x0; 5 | y0 = model.y0; 6 | x = model.x; 7 | y = model.y; 8 | 9 | oneRoute = find(route>city); 10 | From = [0 oneRoute]+1; 11 | To = [oneRoute city+veh]-1; 12 | routes = cell(veh,1); 13 | subplot(1,2,1); 14 | 15 | for i = 1:veh 16 | routes{i} = route(From(i):To(i)); 17 | end 18 | 19 | colors=hsv(veh); 20 | 21 | for r = 1:veh 22 | X = [x0 x(routes{r}) x0]; 23 | Y = [y0 y(routes{r}) y0]; 24 | color = 0.8*colors(r,:); 25 | 26 | plot(X,Y,'-o',... 27 | 'Color',color,... 28 | 'LineWidth',2,... 29 | 'MarkerSize',10,... 30 | 'MarkerFaceColor','white'); 31 | hold on; 32 | end 33 | 34 | plot(x0,y0,'ks',... 35 | 'LineWidth',2,... 36 | 'MarkerSize',18,... 37 | 'MarkerFaceColor','yellow'); 38 | 39 | hold off; 40 | grid on; 41 | axis equal; 42 | 43 | 44 | end -------------------------------------------------------------------------------- /SA_VRP_Points/randomSol.m: -------------------------------------------------------------------------------- 1 | function res = randomSol(model) 2 | res = randperm(model.city+model.veh-1); 3 | end -------------------------------------------------------------------------------- /SA_VRP_Points/sa.m: -------------------------------------------------------------------------------- 1 | clc; 2 | clear; 3 | close all; 4 | 5 | T0 = 500 ; % initial temperature 6 | r = 0.997 ; % temperature damping rate 7 | Ts = 1 ; % stop temperature 8 | iter = 300; 9 | 10 | model = initModel(); 11 | set(gcf,'unit','normalized','position',[0,0.35,1,0.7]); 12 | 13 | flag = 0; 14 | 15 | % initialization 16 | while(1) 17 | route = randomSol(model); 18 | if(isFeasible(route,model)) 19 | break; 20 | end 21 | end 22 | 23 | cost = calculateCost(route,model); 24 | T = T0; 25 | 26 | cnt = 1; 27 | minCost = cost; 28 | minRoute = route; 29 | 30 | maxIterate = 2100; 31 | costArray = zeros(maxIterate,1); 32 | 33 | % SA 34 | while(T > Ts) 35 | for k = 1:iter 36 | mode = randi([1 3]); 37 | newRoute = createNeibor(route,model,mode); 38 | newCost = calculateCost(newRoute,model); 39 | delta = newCost - cost; 40 | 41 | if(delta < 0) 42 | cost = newCost; 43 | route = newRoute; 44 | else 45 | p=exp(-delta/T); 46 | if rand() <= p 47 | cost = newCost; 48 | route = newRoute; 49 | 50 | end 51 | end 52 | end 53 | 54 | costArray(cnt) = cost; 55 | if costmodel.city||route(len)>model.city 4 | res = 0; return 5 | end 6 | 7 | for i = 2:len 8 | if route(i)>model.city && route(i-1)>model.city % not use all vehicles 9 | res = 0; return 10 | end 11 | end 12 | 13 | res = 1; 14 | end -------------------------------------------------------------------------------- /SA_VRP_tspInstance/randomSol.m: -------------------------------------------------------------------------------- 1 | function res = randomSol(model) 2 | res = randperm(model.city+model.veh-1); 3 | end -------------------------------------------------------------------------------- /SA_VRP_tspInstance/sa.m: -------------------------------------------------------------------------------- 1 | clc; 2 | clear; 3 | close all; 4 | 5 | T0 = 10000000 ; % initial temperature 6 | r = 0.999 ; % temperature damping rate 7 | Ts = 0.001 ; % stop temperature 8 | 9 | model = initModel(); 10 | 11 | % initialization 12 | while(1) 13 | route = randomSol(model); 14 | if(isFeasible(route,model)) 15 | break; 16 | end 17 | end 18 | 19 | cost = calculateCost(route,model); 20 | T = T0; 21 | min = cost; 22 | 23 | cnt = 1; 24 | 25 | % SA 26 | while(T > Ts) 27 | flag = '#'; 28 | mode = randi([1 3]); 29 | newRoute = createNeibor(route,model,mode); 30 | newCost = calculateCost(newRoute,model); 31 | delta = newCost - cost; 32 | 33 | if(delta < 0) 34 | cost = newCost; 35 | route = newRoute; 36 | flag = '*'; 37 | else 38 | p=exp(-delta/T); 39 | if rand <= p 40 | cost = newCost; 41 | route = newRoute; 42 | flag = '^'; 43 | end 44 | end 45 | 46 | if cost < min 47 | min = cost; 48 | end 49 | 50 | costArr(cnt) = cost; 51 | 52 | T = T*r; % annealing 53 | disp([flag 'Iteration ' num2str(cnt) ': Best Cost = ' num2str(cost) ' T = ' num2str(T)]); 54 | cnt = cnt+1; 55 | 56 | end 57 | disp(min); 58 | plot(costArr); 59 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | 模拟退火 Vehicle Routing Problem (VRP) using Simulated Annealing (SA) with Matlab 3 | 4 | > Fore more information please visit: www.lzane.com/mo-ni-tui-huo-vehicle-routing-problem-vrp-using-simulated-annealing-sa-with-matlab 5 | 6 | ### Simulation 7 | 让我们来想一个特例,80座城市,分布在四个角上,仓库在正中间,总共有四辆车。那么路程最短的解很明显可以想象出是每辆车分别去访问一个角。matlab工程在文末附件部分给出,仿真结果如下: 8 | 9 | ![](https://www.lzane.com/mo-ni-tui-huo-vehicle-routing-problem-vrp-using-simulated-annealing-sa-with-matlab/SA_VRP.gif) 10 | 11 | 12 | 观察下图,可以看出一开始温度较高的时候,容易接受一个比自己差一点的解,从而跳出局部最优解,随着时间推移,温度降下来之后,就基本上不能再接受比自己再差的解了。 13 | 14 | 15 | --------------- 16 | 17 | 使用Matlab用模拟退火(SA)解决VRP问题。首先什么是VRP问题? 18 | 19 | 大家应该都知道旅行商问题(TSP,Traveling Salesman Problem),即求一个旅行家从一个仓库出发,通过沿途所有城市,再回到仓库所需要的最短路径。TSP问题中只有一个旅行商,那我们如何去解决有多个旅行商(车辆)同时送货的问题呢? 20 | 21 | ### VRP 22 | 这就引出了VRP问题,即在TSP问题的基础上,加上两个限定条件: 23 | 24 | - 有多个旅行商(车辆)同时送货。 25 | - 每个旅行商(车辆)能携带的货物量(capacity)。 26 | 27 | 也就是说,TSP问题是VRP问题的一个特例(不考虑capacity并且只有一辆车的情况)。 28 | 29 | 现在为了简化问题,我们先不考虑汽车容量,只考虑有多个旅行商(车辆)时我们应该如何解决这个问题。 30 | 下图是一个TSP问题的邻接矩阵 (D是仓库) 31 | 32 | 33 | 34 | 我们从[ABC]随机生成一个排列组合,然后再将D接到这个序列的两头即得出了一条路线。 35 | 36 | 37 | 38 | 现在考虑VRP问题,假设现在我们有两辆汽车,其实我们需要做的只是在原来的矩阵多加一行一列,然后把一辆车当成是城市,也可以理解成有多少辆车就有多少个仓库,但他们在地图上其实是一点,然后对[A B C D1]进行排列组合,即可得到: 39 | 40 | 41 | 42 | 然后把D2加到这个序列两头,就可以生成两条路线了(1. D-B-A-D 2.D-C-D)这样就把一个VRP问题装换成TSP问题了。 43 | 44 | 不过有两个方面要注意的: 45 | 46 | - 生成的序列两头不能是D 47 | - 不能有两个D连在一起 48 | 49 | 这两种情况都相当于少了一辆车 50 | 51 | ### 模拟退火 SA 52 | 53 | 首先看这张图,如果采用一般的贪心算法求最大值,那么当搜索到达A之后,就不会继续向前了,这就陷入了局部最优解。 54 | 55 | 56 | 57 | SA模拟退火算法就是解决这个问题的一个办法,模仿金属冶炼时的退火过程,以一定概率接受一个更差一点的解,从而跳出局部最优解。 58 | 59 | 具体算法的实现请参照文末参考文献,这里只是简单带过 60 | 61 | - 假设温度比设置的最低温度高 62 | - 假如生成的解比原来的解更优,则接受生成的较优解。 63 | - 假如生成的解比原来的差,则计算 **P(dE)=exp(dE/(kT))**, 以一定的概率接受这个较差解,然后降温。 64 | - 生成一个neighbor,重复整个过程。 65 | 66 | 生成neignbor的方法也多种多样,比如说: 67 | 68 | - swap 69 | - insert 70 | - reverse 71 | 72 | 73 | ###### 参考文献: 74 | 75 | - "Improvement heuristics for the Vehicle Routing Problem based on Simulated Annealing" —— Alex Van Breedam 76 | 77 | ###### matlab工程代码: 78 | https://github.com/lzane/VRP-using-SA-with-Matlab 79 | 80 | www.lzane.com 81 | --------------------------------------------------------------------------------