使用C++编写蚁群算法+2-opt局部搜索算法的结合版,并调用python的matplotlib库显示结果。TSPlib中ch150的数据仅相差3.1%。(150城)
ch150.txt的格式为:第一行是已知最优路径,第二行开始每行都是城市编号与坐标,城市编号顺序递增。
如果有误,请多指教。
分为两个文件"main.cpp"存放main函数,"Head.h"存放其他一切。
//main.cpp
#include"Head.h"
int main() {
//更新随机种子
srand((unsigned)time(NULL));
Graph* graph = new Graph;
graph->readCity();//启用文件读取,注销掉则为随机构造城市
graph->innitial_Graph();
AntColony* ant_colony = new AntColony;
ant_colony->innitial_AntColony();
for (int NC_now = 0; NC_now < NC_MAX; ++NC_now) {
for (int i = 1; i < N; ++i) {
ant_colony->ChooseNextCity(graph, i);
}
ant_colony->Update(graph,NC_now);
ant_colony->innitial_AntColony();
}
cout << "The BestLength=" << BestLength << endl;
ShowByPython(graph);
int last, next;
double sum = 0;
for (int i = 1; i < N; ++i) {
last = BestPath[i-1];
next = BestPath[i];
cout << "Travel " << i << " : " << last << " \t-> " << next << " \tdistante: " << graph->Length[last][next] << endl;
sum += graph->Length[last][next];
}
last = BestPath[N - 1];
next = BestPath[0];
cout << "Travel " << N << " : " << last << " \t-> " << next << " \tdistante: " << graph->Length[last][next] << endl;
sum += graph->Length[last][next];
cout << "最优解路程和 = " << sum << endl;
cout << "TSPlib上的已知最优解 :" << graph->KnowBest << endl;
cout << "与已知最优解的偏差为 :" << ((sum - graph->KnowBest) / graph->KnowBest) * 100 << "%" << endl;//最后两句必须启用文件读取才有效
delete graph;
delete ant_colony;
return 0;
}
//Head.h
#include<iostream>
#include<cstdlib>
#include<string>
#include<cmath>
#include<fstream>
#include<Python.h>
using namespace std;
#define PATH "E:\\TSPlib\\ch150.txt"//TSPlib数据文件的路径
#define N 150 //城市数量
#define M 100 //蚂蚁数量
#define NC_MAX 1200 //迭代次数
#define α 1 //信息素浓度重要性
#define β 1 //启发式重要性
#define Q 100 //蚂蚁所携带信息素
#define ρ 0.2 //蒸发系数
//注意:如果从文件中读取City,目前需要手动调整城市数量N,要与文件中保持一致
//并且修改 PATH 路径与启用main函数中的readCity函数
//以下数据结构用于最后分析
int BestPath_PerRound[NC_MAX][N] = { 0 };//记录每轮的最优路径
double BestLength_PerRound[NC_MAX] = { 0 };//记录每轮最优路径的长度
int BestPath[N] = { 0 };//全局最短路径(后一轮迭代未必一定比前一轮更优,只是总体是这样,不绝对),不保存每轮的信息
double BestLength = 1000000;//全局最短路径长度
double AverageLength_PerRound[NC_MAX] = { 0 };//记录每轮所有蚂蚁所走路径的平均长度
class City {
public:
//无参构造函数,创建城市时默认在[0,999]内随机获得x,y坐标
City() {
x = rand() % 1000;
y = rand() % 1000;
}
double x;
double y;
};
class Graph {
public:
City citys[N];//无参构造函数创建N个城市
double Length[N][N] = { 0 };//路径长度表
double tao[N][N] = { 0 };//信息素浓度表
double Heuristic[N][N] = { 0 };//启发式表,将城市之间距离的倒数作为启发函数
public:
//使用fstream读取文件中的城市坐标,并创建城市
int KnowBest;//读取文件时存放已知最优解
void readCity() {
ifstream CityFile;
CityFile.open(PATH, ios::in);
if (!CityFile.is_open()) {
cout << "Open file failure" << endl;
exit(0);
}
CityFile >> KnowBest;
cout << "The known best result is :" << KnowBest << endl;
int a;double b[2];
while (!CityFile.eof()) {
CityFile >> a >> b[0] >> b[1];
citys[a - 1].x = b[0];
citys[a - 1].y = b[1];
}
CityFile.close();
}
//初始化城市图,并计算各城市之间的距离
void innitial_Graph() {
double x = 0, y = 0;
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
x = citys[i].x - citys[j].x;
y = citys[i].y - citys[j].y;
Length[i][j] = sqrt(pow(x, 2) + pow(y, 2));//就是两点间距离公式
if (i == j) {
Length[i][j] = 10000;//默认城市到自己的距离为10000,防止启发式表出现无穷大,并不会计算城市自己到自己的概率
}
else {
if (x == 0 && y == 0) {
//这种情况是城市坐标完全重合,如果遇到这种情况则不能继续计算了,否则i与j之间的距离为0,启发式就会为无穷大
//随之p的分子和分母都会变成无穷大,无穷大除无穷大...
cout << "innitial_Graph ERROR!!! City overlapping, please retry." << endl;
cout << "City :" << i << " and City :" << j << endl;
exit(0);
}
}
Heuristic[i][j] = 1 / Length[i][j];//将距离的倒数作为启发式
tao[i][j] = 1;//信息素tao不能初始化为0
}
}
}
};
class AntColony {
public:
int tabu[M][N] = { 0 };//禁忌列表,同时记录了蚂蚁的路径,每个元素取值为[0,N-1]
int allow[M][N] = { 0 };//允许列表,完全依赖于禁忌列表,只是为了算法方便一些而设置,无法记录路径,每个元素取值为[0,1]
double probability[M][N] = { 0 };//概率表,存放各蚂蚁选择下一作城市的概率
double cumulative_probability[M][N] = { 0 };//累积概率表,用于轮盘赌算法随机选择城市
public:
//每轮迭代前都应该被调用一次
void innitial_AntColony() {
//初始化各列表
for (int i = 0; i < M; ++i) {
for (int j = 0; j < N; ++j) {
tabu[i][j] = -1;
allow[i][j] = 1;
probability[i][j] = 0;
cumulative_probability[i][j] = 0;
}
}
//为每一只蚂蚁随机一个初始城市,并将其加入禁忌列表
for (int i = 0; i < M; i++) {
tabu[i][0] = rand() % N;
allow[i][tabu[i][0]] = 0;
}
}
//求取概率表和累积概率表,然后根据轮盘赌算法选择下一个城市,每轮迭代应该被调用N-1次(初始化时已经有一个城市了)
void ChooseNextCity(Graph* graph, int times) {//times表示此轮迭代已经是第几次调用这个函数了,times应该从1开始,到N-1次结束
//求概率表
double sum = 0;
int city_now = -1;
for (int i = 0; i < M; ++i) {
sum = 0;
city_now = tabu[i][times - 1];//蚂蚁i目前在城市city_now
for (int j = 0; j < N; ++j) {
if (allow[i][j] == 1) {
probability[i][j] = pow(graph->tao[city_now][j], α) * pow(graph->Heuristic[city_now][j], β);
sum += probability[i][j];
}
else {
probability[i][j] = 0;
sum += 0;
}
}
//上面求出的probability表并不是真正的概率表,而只是概率的分子,下面求出真正的概率表
for (int j = 0; j < N; ++j) {
probability[i][j] = probability[i][j] / sum;
}
}
//用概率表求累积概率表
for (int i = 0; i < M; ++i) {
cumulative_probability[i][0] = probability[i][0];
for (int j = 1; j < N; ++j) {
cumulative_probability[i][j] = cumulative_probability[i][j - 1] + probability[i][j];
}
}
//依据累积概率表,用轮盘赌算法为每只蚂蚁选择下一个城市
double p = 0;
for (int i = 0; i < M; ++i) {
p = rand() % 1000;
p /= 1000;
for (int j = 0; j < N; ++j) {
if (p <= cumulative_probability[i][j]) {
//如果满足此式,则让蚂蚁i前往城市j(下面更新tabu表的操作就是从逻辑上把蚂蚁移动到城市j)
tabu[i][times] = j;
allow[i][j] = 0;
break;
}
}
}
}
//更新函数每次迭代后都应该被调用一次,用于更新信息素表graph->tabu,并记录本轮迭代的相关信息(最优路径,最优路径的长度,所有蚂蚁所走路径的平均长度)
//NC_now表示目前的迭代次数,应该从0开始,到NC_MAX-1结束,依次调用此函数
void Update(Graph* graph, int NC_now) {
double delta_ktao = 0; //用于保存当前蚂蚁留在所经边的信息素
double delta_tao[N][N] = { 0 }; //此轮迭代的信息素增量表,表示本轮信息素的增量,配合蒸发系数更新信息素表
double sum_Length[M] = { 0 }; //保存此轮每只蚂蚁所经过的路径长度
int last_city, next_city;
//遍历tabu表,计算每只蚂蚁的路径长度,存放在sum_Length[]中
for (int i = 0; i < M; ++i) {
for (int j = 1; j < N; ++j) {
last_city = tabu[i][j - 1];
next_city = tabu[i][j];
sum_Length[i] += graph->Length[last_city][next_city];
}
//还要再加上终点到起点的距离
last_city = tabu[i][N - 1];
next_city = tabu[i][0];
sum_Length[i] += graph->Length[last_city][next_city];
}
//遍历tabu表,计算信息素增量表delta_tao[][]
for (int i = 0; i < M; ++i) {
delta_ktao = Q / sum_Length[i];
for (int j = 1; j < N; ++j) {
last_city = tabu[i][j - 1];
next_city = tabu[i][j];
delta_tao[last_city][next_city] += delta_ktao;
}
}
//利用信息素增量表和蒸发系数更新信息素表tao[][]
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
graph->tao[i][j] = graph->tao[i][j] * (1 - ρ) + delta_tao[i][j];
}
}
//计算本轮最优路径,最优路径的长度,所有蚂蚁所走路径的平均长度
int flag = 0;
double min = 1000000, sum = 0; //min的初始值必须是一个足够大的值
for (int i = 0; i < M; ++i) {
sum += sum_Length[i];
if (min > sum_Length[i]) {
min = sum_Length[i];
flag = i;//标记最好的蚂蚁
}
}
//记录本轮信息至全局变量中
for (int i = 0; i < N; ++i) {
BestPath_PerRound[NC_now][i] = tabu[flag][i];
}
BestLength_PerRound[NC_now] = min;
AverageLength_PerRound[NC_now] = sum / M;
//更新全局最优路径和其长度
if (BestLength > min) {
for (int i = 0; i < N; ++i) {
BestPath[i] = BestPath_PerRound[NC_now][i];
}
BestLength = min;
}
//用2-opt局部搜索优化本轮最优路径并用信息素强化
{
int TempPath[N];
int flag = false;
int t;
for (int i = 0; i < N; ++i) {
TempPath[i] = BestPath_PerRound[NC_now][i];
}
for (int a = 0; a < N-1; ++a) {
//如果路径被优化为新路径,则再对新路径从头来一次局部搜索
if (flag == true) {
a = 0;
flag = false;
}
for (int b = a + 1; b < N; ++b) {
//逆序a~b的路径
for (int i = a, j = b; i < j; ++i, --j) {
t = TempPath[i];
TempPath[i] = TempPath[j];
TempPath[j] = t;
}
//重新求优化后的路径长度,加上终点到起点的距离
sum = 0;
for (int i = 1; i < N; ++i) {
last_city = TempPath[i - 1];
next_city = TempPath[i];
sum += graph->Length[last_city][next_city];
}
last_city = TempPath[N - 1];
next_city = TempPath[0];
sum += graph->Length[last_city][next_city];
//如果此解更优则更新本轮最优解
if (sum < BestLength_PerRound[NC_now]) {
flag = true;//如果路径被更新,则局部搜索从0开始
//先更新平均路径再更新最优路径
AverageLength_PerRound[NC_now] = (AverageLength_PerRound[NC_now] * M - BestLength_PerRound[NC_now] + sum) / M;
BestLength_PerRound[NC_now] = sum;
for (int i = 0; i < N; ++i) {
BestPath_PerRound[NC_now][i] = TempPath[i];
}
//如果比全局最优解还优,则更新全局最优解
if (sum < BestLength) {
for (int i = 0; i < N; ++i) {
BestPath[i] = TempPath[i];
}
BestLength = sum;
}
}
}
}
//使用2opt局部搜索获得更强的解之后,给最优路径追加信息素奖励
double reward = Q / sum;
for (int i = 1; i < N; ++i) {
last_city = BestPath[i-1];
next_city = BestPath[i];
graph->tao[last_city][next_city] += reward;
}
}
}
};
//此函数的目的是为了用图展示出蚁群算法的结果,用C++调用python的matplotlib库
bool ShowByPython(Graph* graph) {
//C++中初始化python环境
Py_Initialize();
if (!Py_IsInitialized()) {
std::cout << "Py_Initialize Error!" << std::endl;
return false;
}
try {
//执行python语句
PyRun_SimpleString("import sys");
PyRun_SimpleString("import matplotlib.pyplot as plt");
string importPy = "sys.argv = ['suibiantian']";
PyRun_SimpleString(importPy.c_str());
double x, y;
PyRun_SimpleString("plt.figure(num=1)");
PyRun_SimpleString("plt.title('Ant Colony algorithm for solving TSP')");//蚁群算法解决TSP问题
PyRun_SimpleString("plt.xlabel('x')");
PyRun_SimpleString("plt.ylabel('y')");
importPy= "plt.xlabel('Shortest=" + to_string(BestLength) + "')";
PyRun_SimpleString(importPy.c_str());
for (int i = 0; i < N; ++i) {
x = graph->citys[i].x;//Graph[i].get_x();
y = graph->citys[i].y;
importPy = "plt.scatter(" + to_string(x) + "," + to_string(y) + ")";
PyRun_SimpleString(importPy.c_str());
}
int last_city, next_city;
double X1, X2, Y1, Y2;
string str;
//把全局最优路径按顺序画出来,画在第一副图上
PyRun_SimpleString("plt.figure(num=1)");
for (int i = 1; i < N; ++i) {
last_city = BestPath[i - 1];
next_city = BestPath[i];
X1 = graph->citys[last_city].x;
Y1 = graph->citys[last_city].y;
X2 = graph->citys[next_city].x;
Y2 = graph->citys[next_city].y;
str = "plt.plot([" + to_string(X1) + "," + to_string(X2) + "],[" + to_string(Y1) + "," + to_string(Y2) + "])";
PyRun_SimpleString(str.c_str());
}
//把终点到起点的线也画上
last_city = BestPath[N - 1];
next_city = BestPath[0];
X1 = graph->citys[last_city].x;
Y1 = graph->citys[last_city].y;
X2 = graph->citys[next_city].x;
Y2 = graph->citys[next_city].y;
str = "plt.plot([" + to_string(X1) + "," + to_string(X2) + "],[" + to_string(Y1) + "," + to_string(Y2) + "])";
PyRun_SimpleString(str.c_str());
//下面展示随着迭代进行,每轮最优路径长度的变化,这是第二幅图
double last = 0, next = 0;
//string str;
PyRun_SimpleString("plt.figure(num=2)");
PyRun_SimpleString("plt.title('ShortestLength Convergence graph')");//最优路径收敛图
PyRun_SimpleString("plt.xlabel('times of iterations')");
PyRun_SimpleString("plt.ylabel('BestLength')");
for (int i = 1; i < NC_MAX; ++i) {
last = BestLength_PerRound[i - 1];
next = BestLength_PerRound[i];
str = "plt.plot([" + to_string(i - 1) + "," + to_string(i) + "],[" + to_string(last) + "," + to_string(next) + "])";
PyRun_SimpleString(str.c_str());
}
//下面展示随着迭代进行,每轮平均路径长度的变化,这是第三幅图
PyRun_SimpleString("plt.figure(num=3)");
PyRun_SimpleString("plt.title('AverageLength Convergence graph')");//平均路径收敛图
PyRun_SimpleString("plt.xlabel('times of iterations')");
PyRun_SimpleString("plt.ylabel('AverageLength')");
for (int i = 1; i < NC_MAX; ++i) {
last = AverageLength_PerRound[i - 1];
next = AverageLength_PerRound[i];
str = "plt.plot([" + to_string(i - 1) + "," + to_string(i) + "],[" + to_string(last) + "," + to_string(next) + "])";
PyRun_SimpleString(str.c_str());
}
//展示上面描绘的三幅图
PyRun_SimpleString("plt.show()");
}
catch (...) {
PyErr_Print();
PyErr_Clear();
Py_Finalize();
return false;
}
Py_Finalize();
return true;
}
下面是搜索出的最优路径
下面是每轮最短路径的收敛图
每轮平均路径的收敛图
详细路径信息,格式为:Travel 路径编号:起点->中点 distante: 路径距离
贴在下面了,欢迎核验,如有错误请务必指出。
Travel 1 : 117 -> 126 distante: 55.1443
Travel 2 : 126 -> 68 distante: 23.8115
Travel 3 : 68 -> 35 distante: 29.2264
Travel 4 : 35 -> 60 distante: 9.72314
Travel 5 : 60 -> 10 distante: 51.2535
Travel 6 : 10 -> 147 distante: 46.2648
Travel 7 : 147 -> 129 distante: 55.4831
Travel 8 : 129 -> 59 distante: 65.0094
Travel 9 : 59 -> 65 distante: 30.1797
Travel 10 : 65 -> 16 distante: 27.3557
Travel 11 : 16 -> 139 distante: 110.504
Travel 12 : 139 -> 116 distante: 86.7004
Travel 13 : 116 -> 56 distante: 78.7559
Travel 14 : 56 -> 38 distante: 29.3313
Travel 15 : 38 -> 40 distante: 54.1271
Travel 16 : 40 -> 100 distante: 50.9215
Travel 17 : 100 -> 115 distante: 34.8224
Travel 18 : 115 -> 11 distante: 61.9085
Travel 19 : 11 -> 23 distante: 29.6934
Travel 20 : 23 -> 52 distante: 40.9306
Travel 21 : 52 -> 39 distante: 42.3001
Travel 22 : 39 -> 138 distante: 40.6155
Travel 23 : 138 -> 119 distante: 44.4553
Travel 24 : 119 -> 41 distante: 36.8047
Travel 25 : 41 -> 8 distante: 33.4683
Travel 26 : 8 -> 27 distante: 17.6044
Travel 27 : 27 -> 5 distante: 39.5344
Travel 28 : 5 -> 36 distante: 50.9311
Travel 29 : 36 -> 1 distante: 38.1426
Travel 30 : 1 -> 18 distante: 45.8299
Travel 31 : 18 -> 98 distante: 32.8616
Travel 32 : 98 -> 113 distante: 25.9416
Travel 33 : 113 -> 101 distante: 37.9277
Travel 34 : 101 -> 136 distante: 91.5024
Travel 35 : 136 -> 131 distante: 78.4181
Travel 36 : 131 -> 64 distante: 70.9377
Travel 37 : 64 -> 84 distante: 79.0921
Travel 38 : 84 -> 141 distante: 43.3364
Travel 39 : 141 -> 17 distante: 18.7001
Travel 40 : 17 -> 74 distante: 68.229
Travel 41 : 74 -> 25 distante: 52.9664
Travel 42 : 25 -> 145 distante: 30.1759
Travel 43 : 145 -> 55 distante: 48.4976
Travel 44 : 55 -> 82 distante: 60.9404
Travel 45 : 82 -> 89 distante: 78.7118
Travel 46 : 89 -> 45 distante: 37.2769
Travel 47 : 45 -> 137 distante: 32.2974
Travel 48 : 137 -> 133 distante: 36.4663
Travel 49 : 133 -> 53 distante: 49.6458
Travel 50 : 53 -> 91 distante: 18.2787
Travel 51 : 91 -> 32 distante: 68.4011
Travel 52 : 32 -> 125 distante: 34.6496
Travel 53 : 125 -> 92 distante: 42.6625
Travel 54 : 92 -> 123 distante: 49.6092
Travel 55 : 123 -> 96 distante: 83.7926
Travel 56 : 96 -> 99 distante: 64.4461
Travel 57 : 99 -> 142 distante: 27.2312
Travel 58 : 142 -> 4 distante: 63.2943
Travel 59 : 4 -> 106 distante: 41.3304
Travel 60 : 106 -> 94 distante: 31.1275
Travel 61 : 94 -> 81 distante: 31.9049
Travel 62 : 81 -> 102 distante: 106.139
Travel 63 : 102 -> 97 distante: 14.1672
Travel 64 : 97 -> 0 distante: 12.4814
Travel 65 : 0 -> 86 distante: 21.0398
Travel 66 : 86 -> 75 distante: 39.3925
Travel 67 : 75 -> 72 distante: 35.7439
Travel 68 : 72 -> 47 distante: 36.0159
Travel 69 : 47 -> 62 distante: 26.0187
Travel 70 : 62 -> 33 distante: 64.609
Travel 71 : 33 -> 29 distante: 51.9375
Travel 72 : 29 -> 83 distante: 29.438
Travel 73 : 83 -> 6 distante: 21.5333
Travel 74 : 6 -> 7 distante: 37.8298
Travel 75 : 7 -> 88 distante: 19.5707
Travel 76 : 88 -> 95 distante: 28.0031
Travel 77 : 95 -> 34 distante: 45.2472
Travel 78 : 34 -> 51 distante: 63.4108
Travel 79 : 51 -> 110 distante: 59.0715
Travel 80 : 110 -> 104 distante: 16.3226
Travel 81 : 104 -> 15 distante: 64.9284
Travel 82 : 15 -> 58 distante: 24.72
Travel 83 : 58 -> 78 distante: 32.2454
Travel 84 : 78 -> 120 distante: 36.1277
Travel 85 : 120 -> 87 distante: 41.0983
Travel 86 : 87 -> 93 distante: 25.7435
Travel 87 : 93 -> 9 distante: 46.3794
Travel 88 : 9 -> 112 distante: 10.3364
Travel 89 : 112 -> 2 distante: 59.0653
Travel 90 : 2 -> 61 distante: 47.9991
Travel 91 : 61 -> 148 distante: 34.7824
Travel 92 : 148 -> 124 distante: 29.0095
Travel 93 : 124 -> 21 distante: 38.7598
Travel 94 : 21 -> 103 distante: 97.2203
Travel 95 : 103 -> 3 distante: 39.9367
Travel 96 : 3 -> 44 distante: 43.7677
Travel 97 : 44 -> 127 distante: 38.5289
Travel 98 : 127 -> 67 distante: 39.3229
Travel 99 : 67 -> 118 distante: 17.3873
Travel 100 : 118 -> 90 distante: 21.9871
Travel 101 : 90 -> 105 distante: 62.0361
Travel 102 : 105 -> 12 distante: 49.6411
Travel 103 : 12 -> 73 distante: 68.0154
Travel 104 : 73 -> 122 distante: 45.3449
Travel 105 : 122 -> 135 distante: 78.0568
Travel 106 : 135 -> 111 distante: 27.5365
Travel 107 : 111 -> 63 distante: 40.7227
Travel 108 : 63 -> 43 distante: 72.4602
Travel 109 : 43 -> 70 distante: 31.6304
Travel 110 : 70 -> 114 distante: 48.8181
Travel 111 : 114 -> 149 distante: 10.7822
Travel 112 : 149 -> 20 distante: 79.5582
Travel 113 : 20 -> 77 distante: 89.5726
Travel 114 : 77 -> 14 distante: 23.1417
Travel 115 : 14 -> 132 distante: 42.269
Travel 116 : 132 -> 76 distante: 45.1619
Travel 117 : 76 -> 121 distante: 26.6059
Travel 118 : 121 -> 13 distante: 27.6407
Travel 119 : 13 -> 79 distante: 12.1495
Travel 120 : 79 -> 71 distante: 64.5719
Travel 121 : 71 -> 48 distante: 38.6427
Travel 122 : 48 -> 146 distante: 1.64922
Travel 123 : 146 -> 143 distante: 21.5679
Travel 124 : 143 -> 128 distante: 50.3949
Travel 125 : 128 -> 26 distante: 44.8038
Travel 126 : 26 -> 30 distante: 41.4632
Travel 127 : 30 -> 144 distante: 67.7661
Travel 128 : 144 -> 22 distante: 146.406
Travel 129 : 22 -> 37 distante: 17.5752
Travel 130 : 37 -> 31 distante: 25.0768
Travel 131 : 31 -> 130 distante: 28.8611
Travel 132 : 130 -> 66 distante: 59.137
Travel 133 : 66 -> 42 distante: 46.6341
Travel 134 : 42 -> 108 distante: 38.3764
Travel 135 : 108 -> 50 distante: 40.2562
Travel 136 : 50 -> 19 distante: 53.2103
Travel 137 : 19 -> 24 distante: 33.4419
Travel 138 : 24 -> 140 distante: 63.6564
Travel 139 : 140 -> 57 distante: 59.038
Travel 140 : 57 -> 54 distante: 20.8724
Travel 141 : 54 -> 49 distante: 48.5817
Travel 142 : 49 -> 134 distante: 31.587
Travel 143 : 134 -> 69 distante: 22.4594
Travel 144 : 69 -> 107 distante: 22.2829
Travel 145 : 107 -> 85 distante: 35.2128
Travel 146 : 85 -> 28 distante: 32.9668
Travel 147 : 28 -> 80 distante: 48.2219
Travel 148 : 80 -> 109 distante: 32.195
Travel 149 : 109 -> 46 distante: 51.5045
Travel 150 : 46 -> 117 distante: 156.05