├── .vscode └── settings.json ├── README.md ├── exact_algorithms ├── DP.py └── __pycache__ │ └── DP.cpython-39.pyc ├── heuristics ├── __pycache__ │ └── iterative_improvement_procedure.cpython-39.pyc ├── exact_partitioning.py ├── greedy_partitioning.py └── iterative_improvement_procedure.py └── other_functions ├── __pycache__ ├── basic_functions.cpython-39.pyc ├── generating_functions.cpython-39.pyc ├── route_drawing.cpython-39.pyc └── solving_TSP.cpython-39.pyc ├── basic_functions.py ├── generating_functions.py ├── route_drawing.py └── solving_TSP.py /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.analysis.extraPaths": [ 3 | "c:\\東工大\\塩浦研\\修論\\計算実験\\spanning_tree_scheduling\\Pareto_algorithms", 4 | "c:\\東工大\\塩浦研\\修論\\計算実験\\spanning_tree_scheduling\\PTAS", 5 | "c:\\東工大\\塩浦研\\修論\\計算実験\\spanning_tree_scheduling", 6 | "c:\\users\\kaito\\github_programs\\TSP-D\\other_functions" 7 | ] 8 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Traveling Salesman Problem with Drone 2 | This repository contains algorithms for solving Traveling Salesman Problem with Drone (TSP-D). TSP-D is a new planning problem that combine a truck and a drone to make effective deliveries in the near future. There are several types of heuristics and exact algorithms. Algorithms are coded with Python3. You can find exact or heuristic solutions of TSP-D and also can view the delivery routes of the truck and the drone. These were used in my research for bachelor's degree at Tokyo Institute of Technology in 2020. 3 | 4 | ## Finding exact solutions 5 | DP for TSP-D was proposed by Bouman, Agatz and Schmidt. 6 | ``` 7 | └─exact_algorithms 8 | │ DP.py 9 | ``` 10 | 11 | ## Finding heuristic solutions 12 | Two kinds of heuristic algorithms for TSP-D was proposed by Agatz, Bouman, and Schmidt. Also there are some procedures to improve the outputs of the heuristics. 13 | 14 | ``` 15 | └─heuristics 16 | │ exact_partitioning.py 17 | | greedy_partitioning.py 18 | | iterative_improvement_procedure.py 19 | ``` 20 | 21 | ## Other functions 22 | 23 | There are some python files that define other functions that is needed to run the main files. 24 | 25 | ``` 26 | └─other_functions 27 | | basic_functions.py 28 | | generating_functions.py 29 | | route_drawing.py 30 | | solving_TSP.py 31 | ``` 32 | 33 | 34 | 35 | ## References 36 | P. Bouman, N. Agatz, and M. Schmidt. “Dynamic programming approaches for the traveling 37 | salesman problem with drone.” Networks 72(2018), 528-542. 38 | 39 | N. Agatz, P. Bouman, and M. Schmidt. “Optimization approaches for the traveling salesman 40 | problem with drone.” Transportation Science 52(2018), 965-981 41 | -------------------------------------------------------------------------------- /exact_algorithms/DP.py: -------------------------------------------------------------------------------- 1 | # you can change pass that fit to your environment 2 | import sys 3 | sys.path.append('c:\\users\\kaito\\github_programs\\TSP-D\\other_functions') 4 | sys.path.append("../") 5 | from generating_functions import * 6 | from route_drawing import * 7 | from basic_functions import t_cost,d_cost 8 | import itertools 9 | import time 10 | 11 | 12 | # V : coordinates of customers 13 | # depot : truck and drone start from depot and get back to depot 14 | 15 | def DP_for_TSPD(V,depot,alpha): 16 | n=len(V) 17 | Dt={} 18 | truck={} 19 | for i in range(1,n+1): 20 | for a in itertools.combinations(range(n),i): 21 | S=frozenset(a) 22 | for v in range(n):#始点 23 | for w in S:#終点 24 | Dt[(S,v,w)]=float('inf')#無限大に初期化 25 | if i==1: 26 | Dt[(S,v,w)]=t_cost(V[v],V[w]) 27 | truck[(S,v,w)]=[w] 28 | else: 29 | Sw=S-set([w]) 30 | for u in Sw: 31 | z=Dt[(Sw,v,u)]+t_cost(V[u],V[w]) 32 | p=truck[(Sw,v,u)]+[w] 33 | if zT[i,k,j]: 42 | M[i,j]=T[i,k,j] 43 | Mdrone[tsp[i],tsp[j]]=[tsp[k]] 44 | else: 45 | for k in range(i+1,n): 46 | if M[i,j]>T[i,k,j]: 47 | M[i,j]=T[i,k,j] 48 | Mdrone[tsp[i],tsp[j]]=[tsp[k]] 49 | for k in range(0,j): 50 | if M[i,j]>T[i,k,j]: 51 | M[i,j]=T[i,k,j] 52 | Mdrone[tsp[i],tsp[j]]=[tsp[k]] 53 | M[n-1,0]=t_cost(V[tsp[n-1]],V[tsp[0]]) 54 | 55 | for i in range(tsp.index(depot)+1,tsp.index(depot)+n+1): 56 | D[depot,tsp[i%n]]=float('inf') 57 | Dres=float('inf') 58 | D[depot,depot]=0 59 | route[depot,depot]=[[depot]] 60 | for j in range(tsp.index(depot),i): 61 | if j%n!=i%n: 62 | d=M[j%n,i%n]+D[depot,tsp[j%n]] 63 | r=route[depot,tsp[j%n]]+[[tsp[j%n],tsp[i%n]]] 64 | if i==tsp.index(depot)+n: 65 | if Dres>d: 66 | Dres=d 67 | route[depot,depot]=r 68 | 69 | else: 70 | if D[depot,tsp[i%n]]>d: 71 | D[depot,tsp[i%n]]=d 72 | route[depot,tsp[i%n]]=r 73 | 74 | pathlen=len(route[depot,depot]) 75 | path=[] 76 | for i in range(pathlen-1): 77 | path=path+route[depot,depot][i+1] 78 | if (route[depot,depot][i+1][0],route[depot,depot][i+1][1]) in Mdrone: 79 | path=path+[Mdrone[route[depot,depot][i+1][0],route[depot,depot][i+1][1]]] 80 | else: 81 | path=path+[[]] 82 | 83 | label={i:"truck" for i in tsp} 84 | for i in path: 85 | if type(i) == int: 86 | label[i] = "combined" 87 | else: 88 | if len(i) > 0: 89 | label[i[0]] = "drone" 90 | 91 | dset=set() 92 | for i in range(len(path)): 93 | if type(path[i])==list and len(path[i])==1: 94 | dset.add(path[i][0]) 95 | return Dres,label,dset 96 | 97 | # initial route is calculated by using mst 2-approximation algorithm for TSP. 98 | def MST_exact_partitioning(V,depot,alpha): 99 | tsp=two_approximation_for_TSP(V) 100 | return exact_partitioning(V,tsp,depot,alpha) 101 | 102 | # initial route is calculated by using 2-opt algorithm for TSP 103 | def two_opt_exact_partitioning(V,depot,alpha): 104 | tsp = two_opt_for_TSP(V) 105 | return exact_partitioning(V,tsp,depot,alpha) 106 | 107 | # initial route is calculated by using DP for TSP. 108 | def DP_exact_partitioning(V,depot,alpha): 109 | tsp=DP_for_TSP(V) 110 | return exact_partitioning(V,tsp,depot,alpha) 111 | 112 | 113 | ## using iterative improvement procedure 114 | def improve_twopmove_exact(alg,V,depot,alpha): 115 | tsp=improve(two_points_move,exact_partitioning,alg(V),depot,V,alpha) 116 | return exact_partitioning(V,tsp,depot,alpha) 117 | 118 | def improve_twooptmove_exact(alg,V,depot,alpha): 119 | tsp=improve(two_opt_move,exact_partitioning,alg(V),depot,V,alpha) 120 | return exact_partitioning(V,tsp,depot,alpha) 121 | 122 | def improve_onepmove_exact(alg,V,depot,alpha): 123 | tsp=improve(one_point_move,exact_partitioning,alg(V),depot,V,alpha) 124 | return exact_partitioning(V,tsp,depot,alpha) 125 | 126 | def improve_all_exact(alg,V,depot,alpha): 127 | tsp=improve_all(exact_partitioning,alg(V),depot,V,alpha) 128 | return exact_partitioning(V,tsp,depot,alpha) 129 | 130 | def MST_exact_partitioning_all_improved(V,depot,alpha): 131 | tsp=improve_all(exact_partitioning,two_approximation_for_TSP(V),depot,V,alpha) 132 | return exact_partitioning(V,tsp,depot,alpha) 133 | 134 | def two_opt_exact_partitioning_all_improved(V,depot,alpha): 135 | tsp = improve_all(exact_partitioning,two_opt_for_TSP(V),depot,V,alpha) 136 | return exact_partitioning(V,tsp,depot,alpha) 137 | 138 | def DP_exact_partitioning_all_improved(V,depot,alpha): 139 | tsp=improve_all(exact_partitioning,DP_for_TSP(V),depot,V,alpha) 140 | return exact_partitioning(V,tsp,depot,alpha) 141 | 142 | def main(): 143 | # you can change the size of the problems here 144 | n = 7 145 | # you can change the character of the testcases here 146 | V = testcase_uniform(n) 147 | # you can change the spped rate (alpha) between truck and drone here 148 | alpha = 2 149 | 150 | print("------------------------------------------------------------------------------------------------------------------------------------------------") 151 | start = time.time() 152 | total_cost,label,drone_nodes = two_opt_exact_partitioning(V,0,alpha) 153 | end = time.time() 154 | print(f"running time of two_opt_exact_partitioning : {round(end-start,4)} sec") 155 | print(f"total cost (time) to deliver all of the customers : {round(total_cost,4)}") 156 | drawing_routes_using_labels(V,label,0,drone_nodes) 157 | # print("------------------------------------------------------------------------------------------------------------------------------------------------") 158 | # start = time.time() 159 | # total_cost,path,drone_nodes = DP_exact_partitioning(V,0,alpha) 160 | # end = time.time() 161 | # print(f"running time of DP_exact_partitioning : {round(end-start,4)} sec") 162 | # print(f"total cost (time) to deliver all of the customers : {round(total_cost,4)}") 163 | 164 | print("------------------------------------------------------------------------------------------------------------------------------------------------") 165 | start = time.time() 166 | total_cost,path,drone_nodes = two_opt_exact_partitioning_all_improved(V,0,alpha) 167 | end = time.time() 168 | print(f"running time of two_opt_exact_partitioning_all_improved : {round(end-start,4)} sec") 169 | print(f"total cost (time) to deliver all of the customers : {round(total_cost,4)}") 170 | drawing_routes_using_labels(V,label,0,drone_nodes) 171 | 172 | # print("------------------------------------------------------------------------------------------------------------------------------------------------") 173 | # start = time.time() 174 | # total_cost,path,drone_nodes = DP_exact_partitioning_all_improved(V,0,alpha) 175 | # end = time.time() 176 | # print(f"running time of DP_exact_partitioning_all_improved : {round(end-start,4)} sec") 177 | # print(f"total cost (time) to deliver all of the customers : {round(total_cost,4)}") 178 | 179 | print("------------------------------------------------------------------------------------------------------------------------------------------------") 180 | 181 | if __name__ == '__main__': 182 | main() -------------------------------------------------------------------------------- /heuristics/greedy_partitioning.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import sys 3 | # you can change pass that fit to your environment 4 | sys.path.append('c:\\users\\kaito\\github_programs\\TSP-D\\other_functions') 5 | sys.path.append("../") 6 | from generating_functions import * 7 | from route_drawing import * 8 | from basic_functions import t_cost,d_cost,t_pathcost 9 | from solving_TSP import * 10 | from iterative_improvement_procedure import * 11 | import time 12 | 13 | def greedy_partitioning(V,tsp,depot,alpha):#depotはindex指定ではない 14 | label={} 15 | operation={} 16 | flyop={} 17 | driveop={} 18 | tsptotal=0 19 | for i in tsp: 20 | label[i]='simple' 21 | tsptotal=tsptotal+t_cost(V[tsp[i]],V[tsp[(i+1)%len(tsp)]]) 22 | n=len(label) 23 | tspdtotal=tsptotal 24 | while 'simple' in label.values(): 25 | ms=[] 26 | for i in range(n): 27 | if label[tsp[i]]!='simple' or tsp[i]==depot: 28 | ms.append(-float('inf')) 29 | else: 30 | mfsi=t_cost(V[tsp[i-1]],V[tsp[i]])+t_cost(V[tsp[i]],V[tsp[(i+1)%n]])-max(d_cost(V[tsp[i-1]],V[tsp[i]],alpha)+d_cost(V[tsp[i]],V[tsp[(i+1)%n]],alpha),t_cost(V[tsp[i-1]],V[tsp[(i+1)%n]])) 31 | ms.append(mfsi) 32 | for i in range(n): 33 | if label[tsp[i-1]]=='combined' and tsp[(i-1)]!=depot: 34 | if label[tsp[i]]=='simple': 35 | j=i-2 36 | while label[tsp[j]]!='combined': 37 | if label[tsp[j]]=='drone': 38 | dronenode=j 39 | if label[tsp[(j-1)]]=='combined': 40 | pls=t_cost(V[tsp[i-1]],V[tsp[i]])+operation[(tsp[dronenode],tsp[j-1],tsp[i-1])]-max(d_cost(V[tsp[j-1]],V[tsp[dronenode]],alpha)+d_cost(V[tsp[dronenode]],V[tsp[i]],alpha),driveop[(tsp[dronenode],tsp[j-1],tsp[i-1])]+t_cost(V[tsp[i-1]],V[tsp[i]])) 41 | ms.append(pls) 42 | break 43 | j=j-1 44 | else: ms.append(-float('inf')) 45 | else: ms.append(-float('inf')) 46 | for i in range(n): 47 | if label[tsp[(i+1)%n]]=='combined' and tsp[(i+1)%n]!=depot: 48 | if label[tsp[i]]=='simple': 49 | j=(i+2)%n 50 | while label[tsp[j]]!='combined': 51 | if label[tsp[j]]=='drone': 52 | dronenode=j 53 | if label[tsp[(j+1)%n]]=='combined': 54 | prs=t_cost(V[tsp[i]],V[tsp[(i+1)%n]])+operation[(tsp[dronenode],tsp[(i+1)%n],tsp[(j+1)%n])]-max(d_cost(V[tsp[i]],V[tsp[dronenode]],alpha)+d_cost(V[tsp[dronenode]],V[tsp[(j+1)%n]],alpha),driveop[(tsp[dronenode],tsp[(i+1)%n],tsp[(j+1)%n])]+t_cost(V[tsp[i]],V[tsp[(i+1)%n]])) 55 | ms.append(prs) 56 | break 57 | j=(j+1)%n 58 | else: ms.append(-float('inf')) 59 | else: ms.append(-float('inf')) 60 | i = numpy.argmax(ms) 61 | if ms[i]>0: 62 | tspdtotal=tspdtotal-ms[i] 63 | if i//n==0:#makeflysaving 64 | label[tsp[i]]='drone' 65 | label[tsp[i-1]]='combined' 66 | label[tsp[(i+1)%n]]='combined' 67 | flyop[(tsp[i],tsp[i-1],tsp[(i+1)%n])]=d_cost(V[tsp[i-1]],V[tsp[i]],alpha)+d_cost(V[tsp[i]],V[tsp[(i+1)%n]],alpha) 68 | driveop[(tsp[i],tsp[i-1],tsp[(i+1)%n])]=t_cost(V[tsp[i-1]],V[tsp[(i+1)%n]]) 69 | operation[(tsp[i],tsp[i-1],tsp[(i+1)%n])]=max(flyop[(tsp[i],tsp[i-1],tsp[(i+1)%n])],driveop[(tsp[i],tsp[i-1],tsp[(i+1)%n])]) 70 | elif i//n==1:#pushleftsaving 71 | label[tsp[i%n]]='combined' 72 | label[tsp[(i-1)%n]]='truck' 73 | j=(i-2)%n 74 | while label[tsp[j]]!='combined': 75 | if label[tsp[j]]=='drone': 76 | dronenode=j 77 | if label[tsp[j-1]]=='combined': 78 | flyop[(tsp[dronenode],tsp[j-1],tsp[i%n])]=d_cost(V[tsp[j-1]],V[tsp[dronenode]],alpha)+d_cost(V[tsp[dronenode]],V[tsp[i%n]],alpha) 79 | driveop[(tsp[dronenode],tsp[j-1],tsp[i%n])]=driveop[(tsp[dronenode],tsp[j-1],tsp[(i-1)%n])]+t_cost(V[tsp[(i-1)%n]],V[tsp[i%n]]) 80 | operation[(tsp[dronenode],tsp[j-1],tsp[i%n])]=max(flyop[(tsp[dronenode],tsp[j-1],tsp[i%n])],driveop[(tsp[dronenode],tsp[j-1],tsp[i%n])]) 81 | break 82 | j=j-1 83 | elif i//n==2:#pushrightsaving 84 | label[tsp[i%n]]='combined' 85 | label[tsp[(i+1)%n]]='truck' 86 | j=(i+2)%n 87 | while label[tsp[j]]!='combined': 88 | if label[tsp[j]]=='drone': 89 | dronenode=j 90 | if label[tsp[(j+1)%n]]=='combined': 91 | flyop[(tsp[dronenode],tsp[i%n],tsp[(j+1)%n])]=d_cost(V[tsp[i%n]],V[tsp[dronenode]],alpha)+d_cost(V[tsp[dronenode]],V[tsp[(j+1)%n]],alpha) 92 | driveop[(tsp[dronenode],tsp[i%n],tsp[(j+1)%n])]=driveop[(tsp[dronenode],tsp[(i+1)%n],tsp[(j+1)%n])]+t_cost(V[tsp[i%n]],V[tsp[(i+1)%n]]) 93 | operation[(tsp[dronenode],tsp[i%n],tsp[(j+1)%n])]=max(flyop[(tsp[dronenode],tsp[i%n],tsp[(j+1)%n])],driveop[(tsp[dronenode],tsp[i%n],tsp[(j+1)%n])]) 94 | break 95 | j=(j+1)%n 96 | else: 97 | for l in label: 98 | if label[l]=='simple': 99 | label[l]='combined' 100 | 101 | 102 | dset=set() 103 | for i in label: 104 | if label[i]=='drone': 105 | dset.add(i) 106 | return tspdtotal,label,dset 107 | 108 | # initial route is calculated by using mst 2-approximation algorithm for TSP. 109 | def MST_greedy_partitioning(V,depot,alpha): 110 | tsp=two_approximation_for_TSP(V) 111 | return greedy_partitioning(V,tsp,depot,alpha) 112 | 113 | # initial route is calculated by using 2-opt algorithm for TSP 114 | def two_opt_greedy_partitioning(V,depot,alpha): 115 | tsp = two_opt_for_TSP(V) 116 | return greedy_partitioning(V,tsp,depot,alpha) 117 | 118 | # initial route is calculated by using DP for TSP. 119 | def DP_greedy_partitioning(V,depot,alpha): 120 | tsp=DP_for_TSP(V) 121 | return greedy_partitioning(V,tsp,depot,alpha) 122 | 123 | ## using iterative improvement procedure 124 | def improve_twopmove_greedy(alg,V,depot,alpha): 125 | tsp=improve(two_points_move,greedy_partitioning,alg(V),depot,V,alpha) 126 | return greedy_partitioning(V,tsp,depot,alpha) 127 | 128 | def improve_twooptmove_greedy(alg,V,depot,alpha): 129 | tsp=improve(two_opt_move,greedy_partitioning,alg(V),depot,V,alpha) 130 | return greedy_partitioning(V,tsp,depot,alpha) 131 | 132 | def improve_onepmove_greedy(alg,V,depot,alpha): 133 | tsp=improve(one_point_move,greedy_partitioning,alg(V),depot,V,alpha) 134 | return greedy_partitioning(V,tsp,depot,alpha) 135 | 136 | def improve_all_greedy(alg,V,depot,alpha): 137 | tsp=improve_all(greedy_partitioning,alg(V),depot,V,alpha) 138 | return greedy_partitioning(V,tsp,depot,alpha) 139 | 140 | def MST_greedy_partitioning_all_improved(V,depot,alpha): 141 | tsp=improve_all(greedy_partitioning,two_approximation_for_TSP(V),depot,V,alpha) 142 | return greedy_partitioning(V,tsp,depot,alpha) 143 | 144 | def two_opt_greedy_partitioning_all_improved(V,depot,alpha): 145 | tsp = improve_all(greedy_partitioning,two_opt_for_TSP(V),depot,V,alpha) 146 | return greedy_partitioning(V,tsp,depot,alpha) 147 | 148 | def DP_greedy_partitioning_all_improved(V,depot,alpha): 149 | tsp=improve_all(greedy_partitioning,DP_for_TSP(V),depot,V,alpha) 150 | return greedy_partitioning(V,tsp,depot,alpha) 151 | 152 | def main(): 153 | # you can change the size of the problems here 154 | n = 15 155 | # you can change the character of the testcases here 156 | V = testcase_donuts_center(n) 157 | # you can change the spped rate between truck and drone here 158 | alpha = 2 159 | 160 | print("------------------------------------------------------------------------------------------------------------------------------------------------") 161 | start = time.time() 162 | total_cost,label,drone_nodes = two_opt_greedy_partitioning(V,0,alpha) 163 | end = time.time() 164 | print(f"running time of two_opt_greedy_partitioning : {round(end-start,4)} sec") 165 | print(f"total cost (time) to deliver all of the customers : {round(total_cost,4)}") 166 | drawing_routes_using_labels(V,label,0,drone_nodes) 167 | 168 | # print("------------------------------------------------------------------------------------------------------------------------------------------------") 169 | # start = time.time() 170 | # total_cost,path,drone_nodes = DP_greedy_partitioning(V,0,alpha) 171 | # end = time.time() 172 | # print(f"running time of DP_greedy_partitioning : {round(end-start,4)} sec") 173 | # print(f"total cost (time) to deliver all of the customers : {round(total_cost,4)}") 174 | 175 | print("------------------------------------------------------------------------------------------------------------------------------------------------") 176 | start = time.time() 177 | total_cost,label,drone_nodes = two_opt_greedy_partitioning_all_improved(V,0,alpha) 178 | end = time.time() 179 | print(f"running time of two_opt_greedy_partitioning_all_improved : {round(end-start,4)} sec") 180 | print(f"total cost (time) to deliver all of the customers : {round(total_cost,4)}") 181 | drawing_routes_using_labels(V,label,0,drone_nodes) 182 | 183 | # print("------------------------------------------------------------------------------------------------------------------------------------------------") 184 | # start = time.time() 185 | # total_cost,path,drone_nodes = DP_greedy_partitioning_all_improved(V,0,alpha) 186 | # end = time.time() 187 | # print(f"running time of DP_greedy_partitioning_all_improved : {round(end-start,4)} sec") 188 | # print(f"total cost (time) to deliver all of the customers : {round(total_cost,4)}") 189 | 190 | print("------------------------------------------------------------------------------------------------------------------------------------------------") 191 | 192 | # drawing_routes(V,route,0,drone_nodes) 193 | 194 | if __name__ == '__main__': 195 | main() -------------------------------------------------------------------------------- /heuristics/iterative_improvement_procedure.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | 3 | # iとjの順番を入れ替える 4 | # swap index i and j 5 | def two_points_move(lis,i,j): 6 | if iheuristic(V,newroute,depot,alpha)[0]: 41 | update=True 42 | initial=newroute 43 | f=heuristic(V,newroute,depot,alpha)[0] 44 | if update: 45 | lis=initial 46 | return initial 47 | 48 | # improving procedure using all of the moves 49 | def improve_all(heuristic,lis,depot,V,alpha): 50 | n=len(lis) 51 | initial=lis 52 | f=heuristic(V,initial,depot,alpha)[0] 53 | update=True 54 | while update: 55 | update=False 56 | for (i,j) in itertools.permutations(range(n),2): 57 | moves=[two_points_move(lis,i,j),two_opt_move(lis,i,j),one_point_move(lis,i,j)] 58 | for newroute in moves: 59 | if f>heuristic(V,newroute,depot,alpha)[0]: 60 | update=True 61 | initial=newroute 62 | f=heuristic(V,newroute,depot,alpha)[0] 63 | if update: 64 | lis=initial 65 | return initial 66 | 67 | -------------------------------------------------------------------------------- /other_functions/__pycache__/basic_functions.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaito2399/TSP-D-algorithms/3cfbb2adf30dbe4aa3137d4ec79898a614552aec/other_functions/__pycache__/basic_functions.cpython-39.pyc -------------------------------------------------------------------------------- /other_functions/__pycache__/generating_functions.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaito2399/TSP-D-algorithms/3cfbb2adf30dbe4aa3137d4ec79898a614552aec/other_functions/__pycache__/generating_functions.cpython-39.pyc -------------------------------------------------------------------------------- /other_functions/__pycache__/route_drawing.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaito2399/TSP-D-algorithms/3cfbb2adf30dbe4aa3137d4ec79898a614552aec/other_functions/__pycache__/route_drawing.cpython-39.pyc -------------------------------------------------------------------------------- /other_functions/__pycache__/solving_TSP.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaito2399/TSP-D-algorithms/3cfbb2adf30dbe4aa3137d4ec79898a614552aec/other_functions/__pycache__/solving_TSP.cpython-39.pyc -------------------------------------------------------------------------------- /other_functions/basic_functions.py: -------------------------------------------------------------------------------- 1 | # cost(time) of trucks 2 | def t_cost(s,t): 3 | return ((s[0]-t[0])**2+(s[1]-t[1])**2)**(0.5) 4 | 5 | # cost(time) of drones 6 | def d_cost(s,t,alpha): 7 | return (1/alpha)*(((s[0]-t[0])**2+(s[1]-t[1])**2)**(0.5)) 8 | 9 | ## t_cost > d_cost holds 10 | 11 | 12 | def t_pathcost(a,b,route,V): 13 | pathcost=0 14 | if ab: 18 | for i in range(a,len(route)): 19 | pathcost=pathcost+t_cost(V[route[i]],V[route[(i+1)%len(route)]]) 20 | for i in range(b): 21 | pathcost=pathcost+t_cost(V[route[i]],V[route[(i+1)%len(route)]]) 22 | if a==b: 23 | pathcost=0 24 | 25 | return pathcost -------------------------------------------------------------------------------- /other_functions/generating_functions.py: -------------------------------------------------------------------------------- 1 | import random 2 | import math 3 | import numpy 4 | random.seed(0) 5 | 6 | # customers are located uniformly 7 | def testcase_uniform(n): 8 | V=[(random.uniform(0,100),random.uniform(0,100)) for i in range(n)] 9 | return V 10 | 11 | # customers are located in the shape of donut 12 | def testcase_donuts(n): 13 | V=[] 14 | for i in range(n): 15 | angle=random.uniform(0,2*math.pi) 16 | V.append((25*random.uniform(0.9,1.1)*math.cos(angle)+50,25*random.uniform(0.9,1.1)*math.sin(angle)+50)) 17 | return V 18 | 19 | # customers are located in the shape of donuts but depot is in the center 20 | def testcase_donuts_center(n): 21 | V=[(50,50)] 22 | for i in range(n-1): 23 | angle=random.uniform(0,2*math.pi) 24 | V.append((25*random.uniform(0.9,1.1)*math.cos(angle)+50,25*random.uniform(0.9,1.1)*math.sin(angle)+50)) 25 | return V 26 | 27 | # customers are located in more in center but less in outsides 28 | def testcase_center(n): 29 | V=[] 30 | for i in range(n): 31 | angle=random.uniform(0,2*math.pi) 32 | r=numpy.random.normal(0,25) 33 | V.append((r*math.cos(angle)+50,r*math.sin(angle)+50)) 34 | return V -------------------------------------------------------------------------------- /other_functions/route_drawing.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | 3 | # red for truck, blue for drone 4 | def drawing_routes_for_DP(V,path,depot,drone_nodes): 5 | n = len(V) 6 | text_dict = dict(boxstyle = "round",fc = "silver", ec = "mediumblue") 7 | for i in range(n): 8 | plt.scatter(V[depot][0],V[depot][1],c='k') 9 | plt.annotate("DEPOT",size = 6, xy = (V[depot][0],V[depot][1]), bbox = text_dict) 10 | plt.scatter(V[i][0],V[i][1],c='k') 11 | if i in drone_nodes: 12 | plt.annotate(f"d{i}",size = 6, xy = (V[i][0],V[i][1]),bbox = text_dict) 13 | elif i != 0: 14 | plt.annotate(f"t{i}",size = 6, xy = (V[i][0],V[i][1]),bbox = text_dict) 15 | for i in range(len(path)): 16 | if len(path[i])>=3: 17 | for j in range(len(path[i])-2): 18 | truck_x=[V[path[i][j]][0],V[path[i][j+1]][0]] 19 | truck_y=[V[path[i][j]][1],V[path[i][j+1]][1]] 20 | plt.plot(truck_x, truck_y,c='red') 21 | truck_x=[V[path[i][-2]][0],V[path[(i+1)%len(path)][0]][0]] 22 | truck_y=[V[path[i][-2]][1],V[path[(i+1)%len(path)][0]][1]] 23 | drone_x1=[V[path[i][0]][0],V[path[i][-1][0]][0]] 24 | drone_y1=[V[path[i][0]][1],V[path[i][-1][0]][1]] 25 | drone_x2=[V[path[i][-1][0]][0],V[path[i][-2]][0]] 26 | drone_y2=[V[path[i][-1][0]][1],V[path[i][-2]][1]] 27 | plt.plot(truck_x,truck_y,c='red') 28 | plt.plot(drone_x1,drone_y1, c='blue') 29 | plt.plot(drone_x2,drone_y2, c='blue') 30 | elif len(path[i])==1: 31 | truck_x=[V[path[i][0]][0],V[path[i+1][0]][0]] 32 | truck_y=[V[path[i][0]][1],V[path[i+1][0]][1]] 33 | plt.plot(truck_x,truck_y, c='red') 34 | else: 35 | truck_x=[V[path[i][-2]][0],V[path[(i+1)%len(path)][0]][0]] 36 | truck_y=[V[path[i][-2]][1],V[path[(i+1)%len(path)][0]][1]] 37 | drone_x1=[V[path[i-1][0]][0],V[path[i][-1][0]][0]] 38 | drone_y1=[V[path[i-1][0]][1],V[path[i][-1][0]][1]] 39 | drone_x2=[V[path[i][-1][0]][0],V[path[i][-2]][0]] 40 | drone_y2=[V[path[i][-1][0]][1],V[path[i][-2]][1]] 41 | plt.plot(truck_x,truck_y,c='red') 42 | plt.plot(drone_x1,drone_y1, c='blue') 43 | plt.plot(drone_x2,drone_y2, c='blue') 44 | plt.show() 45 | 46 | def drawing_routes_using_labels(V,label,depot,drone_nodes): 47 | tsp_route = [i for i in label.keys()] 48 | n = len(V) 49 | text_dict = dict(boxstyle = "round",fc = "silver", ec = "mediumblue") 50 | for i in range(n): 51 | plt.scatter(V[depot][0],V[depot][1],c='k') 52 | plt.annotate("DEPOT",size = 6, xy = (V[depot][0],V[depot][1]), bbox = text_dict) 53 | plt.scatter(V[i][0],V[i][1],c='k') 54 | if i in drone_nodes: 55 | plt.annotate(f"d{i}",size = 6, xy = (V[i][0],V[i][1]),bbox = text_dict) 56 | elif i != 0: 57 | plt.annotate(f"t{i}",size = 6, xy = (V[i][0],V[i][1]),bbox = text_dict) 58 | for i in range(n): 59 | if label[tsp_route[i]] == "combined": 60 | if label[tsp_route[(i+1)%n]] == "combined": 61 | truck_x = (V[tsp_route[i]][0],V[tsp_route[(i+1)%n]][0]) 62 | truck_y = (V[tsp_route[i]][1],V[tsp_route[(i+1)%n]][1]) 63 | plt.plot(truck_x,truck_y,c="red") 64 | else: 65 | cnt = 1 66 | while label[tsp_route[(i+cnt)%n]] != "drone": 67 | cnt += 1 68 | drone_x = (V[tsp_route[i]][0],V[tsp_route[(i+cnt)%n]][0]) 69 | drone_y = (V[tsp_route[i]][1],V[tsp_route[(i+cnt)%n]][1]) 70 | plt.plot(drone_x,drone_y,c="blue") 71 | cnt = 1 72 | while label[tsp_route[(i+cnt)%n]] == "drone": 73 | cnt += 1 74 | truck_x = (V[tsp_route[i]][0],V[tsp_route[(i+cnt)%n]][0]) 75 | truck_y = (V[tsp_route[i]][1],V[tsp_route[(i+cnt)%n]][1]) 76 | plt.plot(truck_x,truck_y,c="red") 77 | elif label[tsp_route[i]] == "drone": 78 | cnt = 1 79 | while label[tsp_route[(i+cnt)%n]] != "combined": 80 | cnt += 1 81 | drone_x = (V[tsp_route[i]][0],V[tsp_route[(i+cnt)%n]][0]) 82 | drone_y = (V[tsp_route[i]][1],V[tsp_route[(i+cnt)%n]][1]) 83 | plt.plot(drone_x,drone_y,c="blue") 84 | else: 85 | cnt = 1 86 | while label[tsp_route[(i+cnt)%n]] == "drone": 87 | cnt += 1 88 | truck_x = (V[tsp_route[i]][0],V[tsp_route[(i+cnt)%n]][0]) 89 | truck_y = (V[tsp_route[i]][1],V[tsp_route[(i+cnt)%n]][1]) 90 | plt.plot(truck_x,truck_y,c="red") 91 | plt.show() -------------------------------------------------------------------------------- /other_functions/solving_TSP.py: -------------------------------------------------------------------------------- 1 | import heapq 2 | from basic_functions import t_cost 3 | import itertools 4 | 5 | ## these algorithms return the routes 6 | 7 | # 2-approximation algorithm using minimum spanning tree 8 | def two_approximation_for_TSP(V): 9 | n = len(V) 10 | #prim 11 | mst = {i:[] for i in range(n)} # MST の隣接リスト 12 | X = set() 13 | heap = [] 14 | for i in range(1,n): 15 | heapq.heappush(heap,(t_cost(V[0],V[i]),0,i)) 16 | while len(heap)>0: 17 | (d,u,i)=heapq.heappop(heap) 18 | if i not in X: 19 | X.add(i) 20 | mst[u].append(i) 21 | mst[i].append(u) 22 | for w in range(n): 23 | if w not in X: heapq.heappush(heap,(t_cost(V[i],V[w]),i,w)) 24 | #dfs 25 | res = [] 26 | stack = [0] 27 | visited = [False]*n 28 | while len(stack)>0: 29 | i = stack.pop() 30 | if visited[i]: continue 31 | visited[i]=True 32 | res.append(i) 33 | for u in mst[i]: stack.append(u) 34 | return res 35 | 36 | # 2-opt algorithm using edge swap 37 | def two_opt_for_TSP(V): 38 | n = len(V) 39 | res = list(range(n)) 40 | update = True 41 | while update: 42 | update = False 43 | for (i,j) in itertools.combinations(range(n),2): 44 | if ((t_cost(V[res[i]],V[res[i+1]])+t_cost(V[res[j]],V[res[(j+1)%n]]))> 45 | (t_cost(V[res[i]],V[res[j]])+t_cost(V[res[i+1]],V[res[(j+1)%n]]))): 46 | res[i+1:j+1] = res[j:i:-1] 47 | update = True 48 | return res 49 | 50 | # dynamic programming 51 | def DP_for_TSP(V): 52 | length = {} # length[(u,S)]: u を始点とし S の点すべてを回る最小経路長、ディクショナリ 53 | route = {} # route[(u,S)]: 最小経路長を達成するためのルート、ディクショナリ 54 | v = V[0] 55 | n = len(V) 56 | for i in range(1,n+1): 57 | for a in itertools.combinations(range(n),i): 58 | S = frozenset(a) 59 | for j in S: 60 | u = V[j] 61 | if i==1: 62 | length[(j,S)] = t_cost(v,u) 63 | route[(j,S)] = [j] 64 | else: 65 | Sj = S-set([j]) # S から j を除いたもの 66 | k=min(Sj,key=lambda k: length[(k,Sj)]+t_cost(V[k],u))#Sjの中でlength[(k,Sj)]+dist(V[k],u)が最小のものを返している 67 | length[(j,S)] = length[(k,Sj)]+t_cost(V[k],u) 68 | route[(j,S)] = route[(k,Sj)]+[j] 69 | return route[(0,frozenset(range(n)))] --------------------------------------------------------------------------------