Implementacja tabu search dla TSP
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pea2plus.cpp 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. #include <fstream>
  2. #include <iostream>
  3. #include <string>
  4. #include <vector>
  5. #include <cmath>
  6. #include <cstdlib>
  7. #include <cstring>
  8. #include "Stopwatch.h"
  9. #include "ArrayGraph.h"
  10. #include "ListGraph.h"
  11. using namespace std;
  12. // USTAWIENIA
  13. // Liczba powtorzen automatycznych pomiarow
  14. const int measureIterations = 10;
  15. // liczba automatycznych pomiarow
  16. const int measureNumber = 4;
  17. // Czas zatrzymania algorytmu tabu search w kazdym z automatycznych pomiarow
  18. const int measureTabuStop[4] = {30, 60, 90, 120};
  19. // Maksymalna odleglosc miast przy automatycznym generowaniu
  20. const int measureSalesmanDistance = 400;
  21. // Wykorzystanie reprezentacji grafu w postaci list sasiedztwa...
  22. // ...zamiast (domyslnie) macierzy sasiedztwa
  23. // (wolniejsze obliczenia, mniejsze uzycie pamieci)
  24. bool useListGraph = false;
  25. // Domyslna kadencja tabu search - wybor automatyczny
  26. unsigned tabuLength = 0;
  27. // Domyslny stan dywersyfikacji
  28. bool tabuDiversification = true;
  29. // Domyslne kryterium dywersyfikacji, liczba iteracji bez poprawy
  30. int tabuIterationsToRestart = 10000;
  31. // Domyslny czas zatrzymania algorytmu tabu search [s]
  32. unsigned tabuStopTime = 60;
  33. // Domyslna liczba watkow tabu search
  34. unsigned tabuThreadsNumber = 2;
  35. void parseTSPLIB_FULL_MATRIX(const char *filename, Graph **graph)
  36. {
  37. // Parser plikow FULL_MATRIX z TSPLIB
  38. // Implementacja: Jan Potocki 2017
  39. string fileInput;
  40. ifstream salesmanDataFile;
  41. salesmanDataFile.open(filename);
  42. if(salesmanDataFile.is_open())
  43. {
  44. do
  45. salesmanDataFile >> fileInput;
  46. while(fileInput != "DIMENSION:");
  47. salesmanDataFile >> fileInput;
  48. int vertex = stoi(fileInput);
  49. do
  50. salesmanDataFile >> fileInput;
  51. while(fileInput != "EDGE_WEIGHT_TYPE:");
  52. salesmanDataFile >> fileInput;
  53. if(fileInput != "EXPLICIT")
  54. {
  55. cout << "+++ MELON MELON MELON +++ Nieobslugiwany format " << fileInput << " +++" << endl;
  56. return;
  57. }
  58. do
  59. salesmanDataFile >> fileInput;
  60. while(fileInput != "EDGE_WEIGHT_FORMAT:");
  61. salesmanDataFile >> fileInput;
  62. if(fileInput == "FULL_MATRIX")
  63. {
  64. if(*graph != NULL)
  65. delete *graph;
  66. if(useListGraph)
  67. *graph = new ListGraph(vertex);
  68. else
  69. *graph = new ArrayGraph(vertex);
  70. do
  71. salesmanDataFile >> fileInput;
  72. while(fileInput != "EDGE_WEIGHT_SECTION");
  73. for(int i = 0; i < vertex; i++)
  74. {
  75. for(int j = 0; j < vertex; j++)
  76. {
  77. salesmanDataFile >> fileInput;
  78. int weight = stoi(fileInput);
  79. if(i != j)
  80. (*graph)->addEdge(i, j, weight);
  81. }
  82. }
  83. cout << "Liczba wczytanych wierzcholkow: " << vertex << endl;
  84. cout << endl;
  85. }
  86. else
  87. {
  88. cout << "+++ MELON MELON MELON +++ Nieobslugiwany format " << fileInput << " +++" << endl;
  89. cout << endl;
  90. }
  91. salesmanDataFile.close();
  92. }
  93. else
  94. {
  95. cout << "+++ MELON MELON MELON +++ Brak pliku " << filename << " +++" << endl;
  96. cout << endl;
  97. }
  98. return;
  99. }
  100. void parseTSPLIB_EUC_2D(const char *filename, Graph **graph)
  101. {
  102. // Parser plikow EUC_2D z TSPLIB
  103. // Implementacja: Jan Potocki 2017
  104. string fileInput;
  105. vector<float> xCoord, yCoord;
  106. ifstream salesmanDataFile;
  107. salesmanDataFile.open(filename);
  108. if(salesmanDataFile.is_open())
  109. {
  110. do
  111. salesmanDataFile >> fileInput;
  112. while(fileInput != "DIMENSION:");
  113. salesmanDataFile >> fileInput;
  114. int vertex = stoi(fileInput);
  115. do
  116. salesmanDataFile >> fileInput;
  117. while(fileInput != "EDGE_WEIGHT_TYPE:");
  118. salesmanDataFile >> fileInput;
  119. if(fileInput == "EUC_2D")
  120. {
  121. if(*graph != NULL)
  122. delete *graph;
  123. if(useListGraph)
  124. *graph = new ListGraph(vertex);
  125. else
  126. *graph = new ArrayGraph(vertex);
  127. do
  128. salesmanDataFile >> fileInput;
  129. while(fileInput != "NODE_COORD_SECTION");
  130. for(int i = 0; i < vertex; i++)
  131. {
  132. salesmanDataFile >> fileInput;
  133. salesmanDataFile >> fileInput;
  134. xCoord.push_back(stof(fileInput));
  135. salesmanDataFile >> fileInput;
  136. yCoord.push_back(stof(fileInput));
  137. }
  138. // To daloby sie zrobic optymalniej (macierz symetryczna), ale nie chce mi sie ...
  139. // ..wole zoptymalizować czas programowania ;-)
  140. for(int i = 0; i < vertex; i++)
  141. {
  142. for(int j = 0; j < vertex; j++)
  143. {
  144. if(i != j)
  145. {
  146. float xDiff = xCoord.at(i) - xCoord.at(j);
  147. float yDiff = yCoord.at(i) - yCoord.at(j);
  148. int weight = nearbyint(sqrt(xDiff * xDiff + yDiff * yDiff));
  149. (*graph)->addEdge(i, j, weight);
  150. }
  151. }
  152. }
  153. cout << "Liczba wczytanych wierzcholkow: " << vertex << endl;
  154. cout << endl;
  155. }
  156. else
  157. {
  158. cout << "+++ MELON MELON MELON +++ Nieobslugiwany format " << fileInput << " +++" << endl;
  159. cout << endl;
  160. }
  161. salesmanDataFile.close();
  162. }
  163. else
  164. {
  165. cout << "+++ MELON MELON MELON +++ Brak pliku " << filename << " +++" << endl;
  166. cout << endl;
  167. }
  168. return;
  169. }
  170. void banner()
  171. {
  172. cout << "PEA Projekt 2 Plus v2.0.1" << endl;
  173. cout << "Jan Potocki 2017-2019" << endl;
  174. cout << "(beerware)" << endl;
  175. }
  176. int main(int argc, char *argv[])
  177. {
  178. Stopwatch clock; // czasomierz
  179. Graph *graph = NULL; // <- tu bedziemy zapisywac adresy przez caly program
  180. // Parsowanie parametrow z linii komend
  181. if(argc > 1)
  182. {
  183. for(int i = 1; i < argc; i++)
  184. {
  185. if(strcmp(argv[i], "-l") == 0)
  186. {
  187. useListGraph = true;
  188. }
  189. else if(strcmp(argv[i], "-t") == 0)
  190. {
  191. i++;
  192. int input = atoi(argv[i]);
  193. if(input > 0)
  194. tabuThreadsNumber = input;
  195. else
  196. cout << "+++ MELON MELON MELON +++ Nieprawidlowa liczba watkow +++" << endl << endl;
  197. }
  198. else if(strcmp(argv[i], "-fmatrix") == 0)
  199. {
  200. i++;
  201. parseTSPLIB_FULL_MATRIX(argv[i], &graph);
  202. }
  203. else if(strcmp(argv[i], "-feuc2d") == 0)
  204. {
  205. i++;
  206. parseTSPLIB_EUC_2D(argv[i], &graph);
  207. }
  208. else if(strcmp(argv[i], "-h") == 0)
  209. {
  210. banner();
  211. cout << endl << "Parametry:" << endl;
  212. cout << "-feuc2d [plik]" << "\t\t" << "wczytanie danych z pliku w formacie TSPLIB EUC_2D" << endl;
  213. cout << endl;
  214. cout << "-fmatrix [plik]" << "\t\t" << "wczytanie danych z pliku w formacie TSPLIB FULL_MATRIX" << endl;
  215. cout << endl;
  216. cout << "-l" << "\t\t\t" << "uzycie reprezentacji grafu w pamieci w postaci listy" << endl;
  217. cout << "\t\t\t" << "sasiedztwa (domyslnie macierz sasiedztwa)" << endl;
  218. cout << endl;
  219. cout << "-t [liczba]" << "\t\t" << "liczba watkow roboczych dla algorytmu tabu search" << endl;
  220. cout << endl;
  221. cout << "-h" << "\t\t\t" << "wyswietlenie pomocy (opisu parametrow)" << endl;
  222. return 0;
  223. // PEA 2 Plus
  224. // Jan Potocki 2019
  225. }
  226. else
  227. {
  228. cout << "Nieprawidlowy parametr, uzyj -h aby uzyskac pomoc" << endl;
  229. return 0;
  230. }
  231. }
  232. }
  233. banner();
  234. if(useListGraph)
  235. cout << "Uzycie listowej reprezentacji grafu" << endl;
  236. else
  237. cout << "Uzycie macierzowej reprezentacji grafu" << endl;
  238. cout << endl;
  239. int salesmanSelection;
  240. do
  241. {
  242. cout << "1. Wygeneruj losowe dane" << endl;
  243. cout << "2. Wyswietl dane" << endl;
  244. cout << "3. Ustawienia TS" << endl;
  245. cout << "4. Tabu search" << endl;
  246. cout << "5. Podzial i ograniczenia" << endl;
  247. cout << "6. Przeglad zupelny" << endl;
  248. cout << "7. Automatyczne pomiary (tabu search)" << endl;
  249. cout << "8. Wczytaj dane z pliku TSPLIB FULL_MATRIX" << endl;
  250. cout << "9. Wczytaj dane z pliku TSPLIB EUC_2D" << endl;
  251. cout << "Aby zakonczyc - 0" << endl;
  252. cout << "Wybierz: ";
  253. cin >> salesmanSelection;
  254. cout << endl;
  255. switch(salesmanSelection)
  256. {
  257. case 1:
  258. {
  259. int vertex;
  260. cout << "Liczba miast: ";
  261. cin >> vertex;
  262. cout << endl;
  263. if(graph != NULL)
  264. delete graph;
  265. if(useListGraph)
  266. graph = new ListGraph(vertex);
  267. else
  268. graph = new ArrayGraph(vertex);
  269. Graph::randomGenerateFullGraph(*graph, measureSalesmanDistance);
  270. }
  271. break;
  272. case 2:
  273. {
  274. if(graph != NULL)
  275. graph->displayGraph();
  276. else
  277. cout << "+++ MELON MELON MELON +++ Brak zaladowanych danych +++" << endl;
  278. cout << endl;
  279. }
  280. break;
  281. case 3:
  282. {
  283. int settingsSelection;
  284. do
  285. {
  286. if(tabuDiversification == true)
  287. cout << "1. Przelacz dywersyfikacje" << "\t" << "(wlaczona)" << endl;
  288. else
  289. cout << "1. Przelacz dywersyfikacje" << "\t" << "(wylaczona)" << endl;
  290. cout << "2. Kryterium dywersyfikacji" << "\t" << "(" << tabuIterationsToRestart << " iteracji)" << endl;
  291. if(tabuLength == 0)
  292. cout << "3. Kadencja na liscie tabu" << "\t" << "(auto)" << endl;
  293. else
  294. cout << "3. Kadencja na liscie tabu" << "\t" << "(" << tabuLength << ")" << endl;
  295. cout << "4. Czas zatrzymania" << "\t\t" << "(" << tabuStopTime << " s)" << endl;
  296. cout << "Powrot - 0" << endl;
  297. cout << "Wybierz: ";
  298. cin >> settingsSelection;
  299. cout << endl;
  300. switch(settingsSelection)
  301. {
  302. case 1:
  303. {
  304. tabuDiversification = !tabuDiversification;
  305. if(tabuDiversification == true)
  306. cout << "Dywersyfikacja zostala wlaczona" << endl;
  307. else
  308. cout << "Dywersyfikacja zostala wylaczona" << endl;
  309. cout << endl;
  310. }
  311. break;
  312. case 2:
  313. {
  314. cout << "Podaj nowa liczbe iteracji bez poprawy: ";
  315. cin >> tabuIterationsToRestart;
  316. cout << endl;
  317. }
  318. break;
  319. case 3:
  320. {
  321. cout << "Podaj nowa kadencje (0 -> auto): ";
  322. cin >> tabuLength;
  323. cout << endl;
  324. }
  325. break;
  326. case 4:
  327. {
  328. cout << "Podaj nowy czas pracy [s]: ";
  329. cin >> tabuStopTime;
  330. cout << endl;
  331. }
  332. break;
  333. case 0:
  334. {
  335. }
  336. break;
  337. default:
  338. {
  339. cout << "Nieprawidlowy wybor" << endl;
  340. cout << endl;
  341. }
  342. }
  343. } while(settingsSelection != 0);
  344. }
  345. break;
  346. case 4:
  347. {
  348. if(graph != NULL)
  349. {
  350. if(tabuStopTime != 0)
  351. {
  352. unsigned effectiveTabuLength;
  353. if(tabuLength == 0)
  354. {
  355. effectiveTabuLength = (graph->getVertexNumber() / 10) * 10;
  356. if(effectiveTabuLength == 0)
  357. effectiveTabuLength = 10;
  358. // PEA 2 Plus
  359. // Jan Potocki 2019
  360. }
  361. else
  362. {
  363. effectiveTabuLength = tabuLength;
  364. }
  365. if(tabuLength == 0)
  366. cout << "Kadencja: " << effectiveTabuLength << " (auto)" << endl;
  367. else
  368. cout << "Kadencja: " << effectiveTabuLength << endl;
  369. cout << "Czas zatrzymania algorytmu [s]: " << tabuStopTime << endl;
  370. if(tabuDiversification == true)
  371. cout << "Dywersyfikacja wlaczona, kryterium: " << tabuIterationsToRestart << " iteracji bez poprawy" << endl;
  372. else
  373. cout << "Dywersyfikacja wylaczona" << endl;
  374. cout << "Liczba watkow: " << tabuThreadsNumber << endl;
  375. cout << endl;
  376. clock.start();
  377. vector<unsigned> route = Graph::travellingSalesmanTabuSearch(*graph, effectiveTabuLength, tabuDiversification, tabuIterationsToRestart, tabuStopTime, tabuThreadsNumber);
  378. clock.stop();
  379. // Wyswietlenie trasy
  380. unsigned distFromStart = 0;
  381. unsigned length = 0;
  382. cout << route.at(0) << '\t' << length << '\t' << distFromStart << endl;
  383. for(int i = 1; i < route.size(); i++)
  384. {
  385. length = graph->getWeight(route.at(i - 1), route.at(i));
  386. distFromStart += length;
  387. cout << route.at(i) << '\t' << length << '\t' << distFromStart << endl;
  388. }
  389. cout << "Dlugosc trasy: " << distFromStart << endl;
  390. cout << endl;
  391. cout << "Czas wykonania algorytmu [s]: " << clock.read() << endl;
  392. }
  393. else
  394. {
  395. // Easter egg ;-)
  396. cout << "+++ MELON MELON MELON +++ Blad: Brak Sera! +++ !!!!! +++" << endl;
  397. }
  398. }
  399. else
  400. cout << "+++ MELON MELON MELON +++ Brak zaladowanych danych +++" << endl;
  401. cout << endl;
  402. }
  403. break;
  404. case 5:
  405. {
  406. if(graph != NULL)
  407. {
  408. clock.start();
  409. vector<unsigned> route = Graph::travellingSalesmanBranchAndBound(*graph);
  410. clock.stop();
  411. // Wyswietlenie trasy
  412. unsigned distFromStart = 0;
  413. unsigned length = 0;
  414. cout << route.at(0) << '\t' << length << '\t' << distFromStart << endl;
  415. for(int i = 1; i < route.size(); i++)
  416. {
  417. length = graph->getWeight(route.at(i - 1), route.at(i));
  418. distFromStart += length;
  419. cout << route.at(i) << '\t' << length << '\t' << distFromStart << endl;
  420. }
  421. cout << "Dlugosc trasy: " << distFromStart << endl;
  422. cout << endl;
  423. cout << "Czas wykonania algorytmu [s]: " << clock.read() << endl;
  424. }
  425. else
  426. cout << "+++ MELON MELON MELON +++ Brak zaladowanych danych +++" << endl;
  427. cout << endl;
  428. }
  429. break;
  430. case 6:
  431. {
  432. if(graph != NULL)
  433. {
  434. clock.start();
  435. vector<unsigned> route = Graph::travellingSalesmanBruteForce(*graph);
  436. clock.stop();
  437. // Wyswietlenie trasy
  438. unsigned distFromStart = 0;
  439. unsigned length = 0;
  440. cout << route.at(0) << '\t' << length << '\t' << distFromStart << endl;
  441. for(int i = 1; i < route.size(); i++)
  442. {
  443. length = graph->getWeight(route.at(i - 1), route.at(i));
  444. distFromStart += length;
  445. cout << route.at(i) << '\t' << length << '\t' << distFromStart << endl;
  446. }
  447. cout << "Dlugosc trasy: " << distFromStart << endl;
  448. cout << endl;
  449. cout << "Czas wykonania algorytmu [s]: " << clock.read() << endl;
  450. }
  451. else
  452. cout << "+++ MELON MELON MELON +++ Brak zaladowanych danych +++" << endl;
  453. cout << endl;
  454. }
  455. break;
  456. case 7:
  457. {
  458. // PEA 2 Plus
  459. // Jan Potocki 2019
  460. if(graph != NULL)
  461. {
  462. unsigned effectiveTabuLength;
  463. if(tabuLength == 0)
  464. {
  465. effectiveTabuLength = (graph->getVertexNumber() / 10) * 10;
  466. if(effectiveTabuLength == 0)
  467. effectiveTabuLength = 10;
  468. }
  469. else
  470. {
  471. effectiveTabuLength = tabuLength;
  472. }
  473. int measureResult = -1;
  474. int measureResultDiv = -1;
  475. cout << "Pomiary dla problemu komiwojazera, tabu search" << tabuLength << endl;
  476. if(tabuLength == 0)
  477. cout << "Kadencja: " << effectiveTabuLength << " (auto)" << endl;
  478. else
  479. cout << "Kadencja: " << effectiveTabuLength << endl;
  480. cout << "Kryterium dywersyfikacji: " << tabuIterationsToRestart << " iteracji bez poprawy" << endl;
  481. cout << "Liczba watkow: " << tabuThreadsNumber << endl;
  482. // Petla pomiarowa
  483. for(int krok = 0; krok < measureIterations; krok++)
  484. {
  485. for(int i = 0; i < measureNumber; i++)
  486. {
  487. vector<unsigned> route;
  488. unsigned routeLength;
  489. // Bez dywersyfikacji
  490. cout << "Pomiar " << measureTabuStop[i] << " [s] (" << krok + 1 << " z " << measureIterations << " bez dywersyfikacji)..." << endl;
  491. route = Graph::travellingSalesmanTabuSearch(*graph, effectiveTabuLength, false, tabuIterationsToRestart, measureTabuStop[i], tabuThreadsNumber);
  492. routeLength = 0;
  493. for(int j = 1; j < route.size(); j++)
  494. routeLength += graph->getWeight(route.at(j - 1), route.at(j));
  495. if(measureResult == -1 || measureResult > routeLength)
  496. measureResult = routeLength;
  497. // Z dywersyfikacja
  498. cout << "Pomiar " << measureTabuStop[i] << " [s] (" << krok + 1 << " z " << measureIterations << " z dywersyfikacja)..." << endl;
  499. route = Graph::travellingSalesmanTabuSearch(*graph, effectiveTabuLength, true, tabuIterationsToRestart, measureTabuStop[i], tabuThreadsNumber);
  500. routeLength = 0;
  501. for(int j = 1; j < route.size(); j++)
  502. routeLength += graph->getWeight(route.at(j - 1), route.at(j));
  503. if(measureResultDiv == -1 || measureResultDiv > routeLength)
  504. measureResultDiv = routeLength;
  505. }
  506. }
  507. cout << "Zapis wynikow..." << endl;
  508. ofstream salesmanToFile;
  509. salesmanToFile.open("wyniki-komiwojazer-ts.txt");
  510. salesmanToFile << "czas - bez dywersyfikacji - z dywersyfikacja" << endl;
  511. for(int i = 0; i < measureNumber; i++)
  512. {
  513. salesmanToFile << measureTabuStop[i] << " [s]: " << measureResult << ' ' << measureResultDiv << endl;
  514. }
  515. salesmanToFile.close();
  516. cout << "Gotowe!" << endl;
  517. cout << endl;
  518. }
  519. else
  520. {
  521. cout << "+++ MELON MELON MELON +++ Brak zaladowanych danych +++" << endl;
  522. cout << endl;
  523. }
  524. }
  525. break;
  526. case 8:
  527. {
  528. // PEA 2 Plus
  529. // Jan Potocki 2019
  530. string filename;
  531. cout << "Podaj nazwe pliku: ";
  532. cin >> filename;
  533. parseTSPLIB_FULL_MATRIX(filename.c_str(), &graph);
  534. }
  535. break;
  536. case 9:
  537. {
  538. // PEA 2 Plus
  539. // Jan Potocki 2019
  540. string filename;
  541. cout << "Podaj nazwe pliku: ";
  542. cin >> filename;
  543. parseTSPLIB_EUC_2D(filename.c_str(), &graph);
  544. }
  545. break;
  546. case 0:
  547. {
  548. }
  549. break;
  550. default:
  551. {
  552. cout << "Nieprawidlowy wybor" << endl;
  553. cout << endl;
  554. }
  555. }
  556. } while(salesmanSelection != 0);
  557. if(graph != NULL)
  558. delete graph;
  559. cout << "Konczenie..." << endl;
  560. // Easter egg :-P
  561. cout << '"' << "Myslak Stibbons niepokoil sie HEX-em." << endl;
  562. cout << "Nie wiedzial, jak dziala, chociaz wszyscy uwazali, ze wie." << endl;
  563. cout << "Oczywiscie, calkiem niezle orientowal sie w niektorych elementach;" << endl;
  564. cout << "byl tez pewien, ze HEX mysli o problemach, przeksztalcajac je" << endl;
  565. cout << "w liczby i mielac ..." << '"' << endl;
  566. cout << "(Terry Pratchett, " << '"' << "Wiedzmikolaj" << '"' << ", tlumaczenie Piotr Cholewa)" << endl;
  567. cout << endl;
  568. return 0;
  569. }