123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695 |
- #include "Graph.h"
- #include "Stopwatch.h"
- #include <algorithm>
- #include <chrono>
- #include <queue>
- #include <random>
- #include <thread>
-
- #include <iostream>
-
- Graph::Graph()
- {
- //ctor
- }
-
- Graph::~Graph()
- {
- //dtor
- }
-
- unsigned Graph::getVertexNumber()
- {
- return vertexNumber;
- }
-
- void Graph::randomGenerateFullGraph(Graph &graph, unsigned maxWeight)
- {
- std::random_device randomSrc;
- std::default_random_engine randomGen(randomSrc());
- std::uniform_int_distribution<> weightDist(1, maxWeight);
-
- for(int i = 0; i < graph.vertexNumber; i++)
- {
- for(int j = 0; j < graph.vertexNumber; j++)
- {
- if(i != j)
- {
- // Bez warunku na krawedzie juz wygenerowane...
- // ...z tym radzi sobie juz metoda addEdge
- int randomWeight = weightDist(randomGen);
- graph.addEdge(i, j, randomWeight);
- }
- }
- }
- }
-
- std::vector<unsigned> Graph::travellingSalesmanBruteForce(Graph &graph)
- {
- // ALGORYTM przegladu zupelnego
- // Implementacja: Jan Potocki 2017
- // (refactoring 2019)
- std::vector<unsigned> vertexArray;
-
- // Generowanie "spisu" wierzcholkow
- // (od razu w odpowiedniej kolejnosci dla next_permutation)
- for(int i = 1; i < graph.vertexNumber; i++)
- vertexArray.push_back(i);
-
- std::vector<unsigned> minCombination;
- int minRoute = -1;
-
- // Petla przegladajaca kolejne permutacje
- do
- {
- std::vector<unsigned> combination;
-
- // Dodanie wierzcholka startowego i pierwszego na trasie
- combination.push_back(0);
- combination.push_back(vertexArray.front());
-
- // W petli reszta wiercholkow
- for(int i = 1; i < vertexArray.size(); i++)
- combination.push_back(vertexArray.at(i));
-
- // Powrot do wierzcholka startowego
- combination.push_back(0);
- // PEA 2
- // Jan Potocki 2017
-
- int route = 0;
- for(int i = 1; i < combination.size(); i++)
- route += graph.getWeight(combination.at(i - 1), combination.at(i));
-
- if(minRoute == -1 || route < minRoute)
- {
- minRoute = route;
- minCombination = combination;
- }
- }
- while(next_permutation(vertexArray.begin(), vertexArray.end()));
-
- return minCombination;
- }
-
- std::vector<unsigned> Graph::travellingSalesmanBranchAndBound(Graph &graph)
- {
- // ALGORYTM pracujacy w oparciu o kolejke priorytetowa i niejawnie utworzone drzewo
- // Zrodlo: www.ii.uni.wroc.pl/~prz/2011lato/ah/opracowania/met_podz_ogr.opr.pdf
- // Autor: Mateusz Lyczek 2011
- // Implementacja: Jan Potocki 2017
- std::priority_queue<std::vector<unsigned>, std::vector< std::vector<unsigned> >, RouteComparison> routeQueue;
- std::vector<unsigned> optimalRoute; // Tu bedziemy zapisywac optymalne (w danej chwili) rozwiazanie
- int optimalRouteLength = -1; // -1 - bedziemy odtad uznawac, ze to jest nieskonczonosc ;-)
-
- // UMOWA
- // Pierwszy element wektora to dlugosc trasy (trzeba ustawic "z palca"!)
- // Kolejne to wierzcholki na trasie
- std::vector<unsigned> currentRoute; // Niejawne tworzenie drzewa, tu bedzie korzen
- currentRoute.push_back(0); // Poczatkowe oszacowanie nie ma znaczenia
- currentRoute.push_back(0); // Wierzcholek startowy (korzen drzewa rozwiazan)
- routeQueue.push(currentRoute); // Dodanie do kolejki korzenia
-
- while(!routeQueue.empty())
- {
- // Przypisanie korzenia do dalszej roboty
- currentRoute = routeQueue.top();
- routeQueue.pop();
-
- // Sprawdzenie, czy rozwiazanie jest warte rozwijania, czy odrzucic
- if(optimalRouteLength == -1 || currentRoute.at(0) < optimalRouteLength)
- {
- for(int i = 0; i < graph.vertexNumber; i++)
- {
- // Petla wykonywana dla kazdego potomka rozpatrywanego wlasnie rozwiazania w drzewie
- // Ustalenie, czy dany wierzcholek mozna jeszcze wykorzystac, czy juz zostal uzyty
- bool vertexUsed = false;
- for(int j = 1; j < currentRoute.size(); j++)
- {
- if(currentRoute.at(j) == i)
- {
- vertexUsed = true;
- break;
- }
- }
- if(vertexUsed)
- continue;
-
- // Niejawne utworzenie nowego wezla reprezuntujacego rozpatrywane rozwiazanie...
- std::vector<unsigned> nextRoute = currentRoute;
- //unsigned nextLength = graph.getWeight(nextRoute.back(), i);
- nextRoute.push_back(i);
-
- // Dalej bedziemy postepowac roznie...
- if(nextRoute.size() > graph.vertexNumber)
- {
- // Doszlismy wlasnie do liscia
- // Dodajemy droge powrotna, nie musimy nic szacowac
- // (wszystko juz wiemy)
- nextRoute.push_back(0);
-
- nextRoute.at(0) = 0;
-
- for(int j = 1; j < nextRoute.size() - 1; j++)
- {
- // Liczymy dystans od poczatku do konca
- nextRoute.at(0) += graph.getWeight(nextRoute.at(j), nextRoute.at(j+ 1));
- }
- if(optimalRouteLength == -1 || nextRoute.at(0) < optimalRouteLength)
- {
- optimalRouteLength = nextRoute.at(0);
- nextRoute.erase(nextRoute.begin());
- optimalRoute = nextRoute;
- }
- }
- else
- {
- // Liczenie tego, co juz wiemy, od nowa...
- // (dystans od poczatku)
- nextRoute.at(0) = 0;
- for(int j = 1; j < nextRoute.size() - 1; j++)
- {
- nextRoute.at(0) += graph.getWeight(nextRoute.at(j), nextRoute.at(j + 1));
- }
-
- // Reszte szacujemy...
- // Pomijamy od razu wierzcholek startowy
- for(int j = 1; j < graph.vertexNumber; j++)
- {
- // Odrzucenie wierzcholkow juz umieszczonych na trasie
- bool vertexUsed = false;
- for(int k = 1; k < currentRoute.size(); k++)
- {
- if(j == currentRoute.at(k))
- {
- vertexUsed = true;
- break;
- }
- }
- if(vertexUsed)
- continue;
-
- int minEdge = -1;
- for(int k = 0; k < graph.vertexNumber; k++)
- {
- // Odrzucenie krawedzi do wierzcholka 0 przy ostatnim wierzcholku w czesciowym rozwiazaniu
- // Wyjatkiem jest ostatnia mozliwa krawedz
- if(j == i && k == 0)
- continue;
-
- // Odrzucenie krawedzi do wierzcholka umieszczonego juz na rozwazanej trasie
- bool vertexUsed = false;
- for(int l = 2; l < nextRoute.size(); l++)
- {
- if(k == nextRoute.at(l))
- {
- vertexUsed = true;
- break;
- }
- }
- if(vertexUsed)
- continue;
-
- // Odrzucenie samego siebie
- if(k == j)
- continue;
-
- // Znalezienie najkrotszej mozliwej jeszcze do uzycia krawedzi
- unsigned consideredLength = graph.getWeight(j, k);
-
- if(minEdge == -1)
- minEdge = consideredLength;
- else if(minEdge > consideredLength)
- minEdge = consideredLength;
- }
- nextRoute.at(0) += minEdge;
- }
-
- // ...i teraz zastanawiamy sie co dalej
- if(optimalRouteLength == -1 || nextRoute.at(0) < optimalRouteLength)
- {
- routeQueue.push(nextRoute);
- }
- }
- }
- }
- else
- {
- // Jezeli jedno rozwiazanie odrzucilismy, to wszystkie inne tez mozemy
- // (kolejka priorytetowa, inne nie moga byc lepsze)
- break;
- }
- }
-
- return optimalRoute;
- }
-
- std::vector<unsigned> Graph::travellingSalesmanGreedy(Graph &graph, unsigned startVertex)
- {
- // ALGORYTM zachlanny z wierzcholkiem startowym przekazanym w parametrze
- // Implementacja: Jan Potocki 2017
- std::vector<unsigned> route;
-
- // Przypisanie wierzcholka startowego
- route.push_back(startVertex);
-
- for(int i = 0; i < graph.vertexNumber - 1; i++)
- {
- int minEdge = -1;
- unsigned nextVertex;
- for(int j = 0; j < graph.vertexNumber; j++)
- {
- // Odrzucenie samego siebie lub wierzcholka startowego
- // (zeby bylo szybciej)
- if(route.back() == j || route.front() == j)
- continue;
-
- // Odrzucenie krawedzi do wierzcholka umieszczonego juz na trasie
- bool vertexUsed = false;
- for(int k = 0; k < route.size(); k++)
- {
- if(j == route.at(k))
- {
- vertexUsed = true;
- break;
- }
- }
- if(vertexUsed)
- continue;
-
- // Znalezienie najkrotszej mozliwej jeszcze do uzycia krawedzi
- unsigned consideredLength = graph.getWeight(route.back(), j);
-
- if(minEdge == -1)
- {
- minEdge = consideredLength;
- nextVertex = j;
- }
- else if(minEdge > consideredLength)
- {
- minEdge = consideredLength;
- nextVertex = j;
- }
- }
- route.push_back(nextVertex);
- }
-
- route.push_back(startVertex);
- return route;
- }
-
- std::vector<unsigned> Graph::travellingSalesmanHybrid(Graph &graph)
- {
- // ALGORYTM hybrydowy losowo-zachlanny
- // Losowa czesc wierzcholkow jest losowana, reszta zachlannie
- // Implementacja: Jan Potocki 2019
- std::vector<unsigned> route;
-
- std::random_device randomSrc;
- std::default_random_engine randomGen(randomSrc());
- std::uniform_int_distribution<> vertexNumberDist(1, graph.vertexNumber);
- std::uniform_int_distribution<> vertexDist(0, graph.vertexNumber - 1);
-
- // Liczba losowanych wierzcholkow
- unsigned randomVertexNumber = vertexNumberDist(randomGen);
-
- // Czesc losowa
- for(int i = 0; i < randomVertexNumber; i++)
- {
- unsigned randomVertex;
- bool vertexUsed;
-
- do
- {
- randomVertex = vertexDist(randomGen);
- vertexUsed = false;
-
- for(int j = 0; j < route.size(); j++)
- {
- if(route.at(j) == randomVertex)
- {
- vertexUsed = true;
- break;
- }
- }
- } while(vertexUsed == true);
-
- route.push_back(randomVertex);
- }
-
- // Czesc zachlanna
- for(int i = 0; i < graph.vertexNumber - randomVertexNumber; i++)
- {
- int minEdge = -1;
- unsigned nextVertex;
- for(int j = 0; j < graph.vertexNumber; j++)
- {
- // Odrzucenie samego siebie lub wierzcholka startowego
- // (zeby bylo szybciej)
- if(route.back() == j || route.front() == j)
- continue;
-
- // Odrzucenie krawedzi do wierzcholka umieszczonego juz na trasie
- bool vertexUsed = false;
- for(int k = 0; k < route.size(); k++)
- {
- if(j == route.at(k))
- {
- vertexUsed = true;
- break;
- }
- }
- if(vertexUsed)
- continue;
-
- // Znalezienie najkrotszej mozliwej jeszcze do uzycia krawedzi
- unsigned consideredLength = graph.getWeight(route.back(), j);
-
- // PEA 2 Plus
- // Jan Potocki 2019
- if(minEdge == -1)
- {
- minEdge = consideredLength;
- nextVertex = j;
- }
- else if(minEdge > consideredLength)
- {
- minEdge = consideredLength;
- nextVertex = j;
- }
- }
- route.push_back(nextVertex);
- }
-
- route.push_back(route.front());
- return route;
- }
-
- std::vector<unsigned> Graph::travellingSalesmanRandom(Graph &graph)
- {
- // ALGORYTM losowy
- // Implementacja: Jan Potocki 2019
- std::vector<unsigned> route;
-
- std::random_device randomSrc;
- std::default_random_engine randomGen(randomSrc());
- std::uniform_int_distribution<> vertexDist(0, graph.vertexNumber - 1);
-
- for(int i = 0; i < graph.vertexNumber; i++)
- {
- unsigned randomVertex;
- bool vertexUsed;
-
- do
- {
- randomVertex = vertexDist(randomGen);
- vertexUsed = false;
-
- for(int j = 0; j < route.size(); j++)
- {
- if(route.at(j) == randomVertex)
- {
- vertexUsed = true;
- break;
- }
- }
- } while(vertexUsed == true);
-
- route.push_back(randomVertex);
- }
-
- route.push_back(route.front());
- return route;
- }
-
- std::vector<unsigned> Graph::travellingSalesmanTabuSearch(Graph &graph, unsigned tabuSteps, bool diversification, int iterationsToRestart, unsigned minStopTime, unsigned threadsNumber)
- {
- // ALGORYTM wielawotkowy oparty na metaheurystyce tabu search
- // Pomocniczy kod uruchamiajacy watki wlasciwego algorytmu w najbardziej optymalny sposob
- // Implementacja: Jan Potocki 2019
- std::vector<unsigned> startVertexVector;
- std::vector<std::thread> threadsVector;
- std::vector<std::vector<unsigned>> resultsVector(threadsNumber);
-
- std::vector<int> resultsLength(threadsNumber);
- std::vector<unsigned> optimalResult;
- int optimalResultIndex;
- int optimalResultLength;
-
- std::random_device randomSrc;
- std::default_random_engine randomGen(randomSrc());
- std::uniform_int_distribution<> vertexDist(0, graph.vertexNumber - 1);
-
- // Petla uruchamiajaca watki
- for(int i = 0; i < threadsNumber; i++)
- {
- // Generowanie startowego rozwiazania...
- std::vector<unsigned> startRoute;
- unsigned startVertex;
- bool startVertexUsed;
-
- if(i < graph.vertexNumber)
- {
- // ...dopoki ma to sens - algorytmem zachlannym z innym wierzcholkiem startowym
- // (dla kazdego watku)
- do
- {
- startVertex = vertexDist(randomGen);
- startVertexUsed = false;
-
- for(int j = 0; j < startVertexVector.size(); j++)
- {
- if(startVertexVector.at(j) == startVertex)
- {
- startVertexUsed = true;
- break;
- }
- }
- } while(startVertexUsed == true);
-
- // PEA 2 Plus
- // Jan Potocki 2019
- startVertexVector.push_back(startVertex);
- startRoute = Graph::travellingSalesmanGreedy(graph, startVertex);
- }
- else
- {
- // ...jezeli wszystkie wierzcholki sa juz wykorzystane - w pelni losowo
- startRoute = Graph::travellingSalesmanRandom(graph);
- }
-
- // Uruchomienie watku
- threadsVector.push_back(std::thread(Graph::travellingSalesmanTabuSearchEngine, std::ref(graph), tabuSteps, diversification, iterationsToRestart, minStopTime, startRoute, std::ref(resultsVector.at(i)), std::ref(resultsLength.at(i))));
- }
-
- // Petla potwierdzajaca zakonczenie watkow
- for(int i = 0; i < threadsNumber; i++)
- threadsVector.at(i).join();
-
- // Przegladanie wszystkich rozwiazan i wybor optymalnego
- optimalResultIndex = 0;
- optimalResultLength = resultsLength.at(0);
-
- for(int i = 0; i < threadsNumber; i++)
- {
- if(resultsLength.at(i) < optimalResultLength)
- {
- optimalResultIndex = i;
- optimalResultLength = resultsLength.at(i);
- }
- }
-
- optimalResult = resultsVector.at(optimalResultIndex);
-
- return optimalResult;
- }
-
- void Graph::travellingSalesmanTabuSearchEngine(Graph &graph, unsigned tabuSteps, bool diversification, int iterationsToRestart, unsigned minStopTime, std::vector<unsigned> startRoute, std::vector<unsigned> &result, int &resultLength)
- {
- // ALGORYTM oparty na metaheurystyce tabu search z dywersyfikacja i sasiedztwem typu swap
- // Rdzen przeznaczony do uruchamiania jako jeden watek
- // Projekt i implementacja: Jan Potocki 2017
- // (refactoring 2019)
- Stopwatch onboardClock;
-
- std::vector<unsigned> optimalRoute; // Tu bedziemy zapisywac optymalne (w danej chwili) rozwiazanie
- int optimalRouteLength = -1; // -1 - bedziemy odtad uznawac, ze to jest nieskonczonosc ;-)
- std::vector<unsigned> currentRoute; // Rozpatrywane rozwiazanie
-
- // Wyznaczenie poczatkowego rozwiazania algorytmem zachlannym
- //currentRoute = Graph::travellingSalesmanGreedy(graph);
- currentRoute = startRoute;
-
- // Inicjalizacja glownej petli...
- std::vector< std::vector<unsigned> > tabuArray;
- unsigned currentTabuSteps = tabuSteps;
- int stopCounter = 0;
- bool timeNotExceeded = true;
- onboardClock.start();
-
- // Rdzen algorytmu
- while(timeNotExceeded == true)
- {
- bool cheeseSupplied = true;
- bool intensification = false;
-
- while(cheeseSupplied == true)
- {
- std::vector<unsigned> nextRoute;
- int nextRouteLength = -1;
-
- std::vector<unsigned> nextTabu(3, 0);
- nextTabu.at(0) = currentTabuSteps;
-
- // Generowanie sasiedztwa typu swap przez zamiane wierzcholkow
- // (wierzcholka startowego i zarazem ostatniego nie ruszamy,
- // pomijamy tez od razu aktualny wierzcholek)
- for(int i = 1; i < graph.vertexNumber - 1; i++)
- {
- for(int j = i + 1; j < graph.vertexNumber; j++)
- {
- std::vector<unsigned> neighbourRoute = currentRoute;
-
- // Zamiana
- unsigned buffer = neighbourRoute.at(j);
- neighbourRoute.at(j) = neighbourRoute.at(i);
- neighbourRoute.at(i) = buffer;
-
- unsigned neighbourRouteLength = 0;
- for(int i = 1; i < neighbourRoute.size(); i++)
- neighbourRouteLength += graph.getWeight(neighbourRoute.at(i - 1), neighbourRoute.at(i));
-
- // Sprawdzenie, czy dany ruch nie jest na liscie tabu
- // (dwa wierzcholki)
- bool tabu = false;
- for(int k = 0; k < tabuArray.size(); k++)
- {
- if(tabuArray.at(k).at(1) == i && tabuArray.at(k).at(2) == j)
- {
- tabu = true;
- break;
- }
-
- if(tabuArray.at(k).at(1) == j && tabuArray.at(k).at(2) == i)
- {
- tabu = true;
- break;
- }
- }
-
- // Kryterium aspiracji...
- if(tabu == true && neighbourRouteLength >= optimalRouteLength)
- // ...jezeli niespelnione - pomijamy ruch
- continue;
-
- if(nextRouteLength == -1)
- {
- nextRouteLength = neighbourRouteLength;
- nextRoute = neighbourRoute;
- nextTabu.at(1) = i;
- nextTabu.at(2) = j;
- }
- else if(nextRouteLength > neighbourRouteLength)
- {
- nextRouteLength = neighbourRouteLength;
- nextRoute = neighbourRoute;
- nextTabu.at(1) = i;
- nextTabu.at(2) = j;
- }
- }
- }
-
- currentRoute = nextRoute;
- // PEA 2 Plus
- // Jan Potocki 2019
-
- if(optimalRouteLength == -1)
- {
- optimalRouteLength = nextRouteLength;
- optimalRoute = nextRoute;
-
- // Reset licznika
- stopCounter = 0;
- }
- else if(optimalRouteLength > nextRouteLength)
- {
- optimalRouteLength = nextRouteLength;
- optimalRoute = nextRoute;
-
- // Zaplanowanie intensyfikacji przy znalezieniu nowego optimum
- intensification = true;
-
- // Reset licznika
- stopCounter = 0;
- }
-
- // Weryfikacja listy tabu...
- // ...aktualizacja kadencji na liscie tabu
- for(int i = 0; i < tabuArray.size(); i++)
- {
- tabuArray.at(i).at(0)--;
- }
-
- //...usuniecie zerowych kadencji
- for(int i = 0; i < tabuArray.size(); i++)
- {
- if(tabuArray.at(i).at(0) == 0)
- tabuArray.erase(tabuArray.begin() + i);
- }
-
- // ...dopisanie ostatniego ruchu do listy tabu
- tabuArray.push_back(nextTabu);
-
- // Zliczenie iteracji
- stopCounter++;
-
- // Zmierzenie czasu
- onboardClock.stop();
- if(onboardClock.read() > minStopTime)
- timeNotExceeded = false;
-
- // Sprawdzenie warunku zatrzymania
- if(diversification == true)
- {
- // Przy aktywowanej dywersyfikacji - po zadanej liczbie iteracji bez poprawy
- if(stopCounter >= iterationsToRestart || timeNotExceeded == false)
- cheeseSupplied = false;
- }
- else
- {
- // Przy nieaktywowanej dywersyfikacji - po uplynieciu okreslonego czasu
- if(timeNotExceeded == false)
- cheeseSupplied = false;
- }
- }
-
- // Dywersyfikacja
- if(diversification == true)
- {
- if(intensification == true)
- {
- // Intensyfikacja przeszukiwania przez skrócenie kadencji
- // (jezeli w ostatnim przebiegu znaleziono nowe minimum)
- currentRoute = optimalRoute;
- currentTabuSteps = tabuSteps / 4;
- intensification = false;
- // PEA 2 Plus
- // Jan Potocki 2019
- }
- else
- {
- // W innym przypadku wlasciwa dywersyfikacja przez wygenerowanie nowego
- // rozwiazania startowego algorytmem hybrydowym losowo-zachlannym
- currentRoute = Graph::travellingSalesmanHybrid(graph);
- currentTabuSteps = tabuSteps;
- intensification = false;
- }
- }
-
- // Reset licznika iteracji przed restartem
- stopCounter = 0;
- }
-
- result = optimalRoute;
- resultLength = optimalRouteLength;
- }
|