commit bbc24df31f2694ec0c8e6e257dce6f9e4dd720b8 Author: Jan Potocki Date: Sun Nov 17 21:02:29 2019 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..259148f --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app diff --git a/ArrayGraph.cpp b/ArrayGraph.cpp new file mode 100755 index 0000000..3ab1d54 --- /dev/null +++ b/ArrayGraph.cpp @@ -0,0 +1,70 @@ +#include "ArrayGraph.h" +#include + +ArrayGraph::ArrayGraph(unsigned vertexNumber) +{ + //ctor + this->vertexNumber = vertexNumber; + graphMatrix = new unsigned*[vertexNumber]; + graphArray = new unsigned[vertexNumber*vertexNumber]; + // W ten sposob cala tablica bedzie w pamieci w jednej czesci + // (mniej chybien w odwolaniach procesora do cache) + + for(int i = 0; i < vertexNumber; i++) + { + graphMatrix[i] = graphArray + i * vertexNumber; + + for(int j = 0; j < vertexNumber; j++) + graphMatrix[i][j] = 0; + } +} + +ArrayGraph::~ArrayGraph() +{ + //dtor + delete graphArray; + delete graphMatrix; +} + +bool ArrayGraph::addEdge(unsigned v, unsigned w, unsigned weight) +{ + if(weight >= 1000) + // Waga krawedzi musi byc mniejsza od 1000 + weight = 900; + + if(graphMatrix[v][w] > 0) + return false; + else + { + graphMatrix[v][w] = weight; + return true; + } +} + +bool ArrayGraph::removeEdge(unsigned v, unsigned w) +{ + if(graphMatrix[v][w] == 0) + return false; + else + { + graphMatrix[v][w] = 0; + return true; + } +} + +unsigned ArrayGraph::getWeight(unsigned v, unsigned w) +{ + return graphMatrix[v][w]; +} + +void ArrayGraph::displayGraph() +{ + for(int i = 0; i < vertexNumber; i++) + { + for(int j = 0; j < vertexNumber; j++) + { + std::cout << graphMatrix[i][j] << '\t'; + } + std::cout << std::endl; + } +} diff --git a/ArrayGraph.h b/ArrayGraph.h new file mode 100755 index 0000000..c0a86cb --- /dev/null +++ b/ArrayGraph.h @@ -0,0 +1,24 @@ +#ifndef ARRAYGRAPH_H +#define ARRAYGRAPH_H + +#include "Graph.h" + +class ArrayGraph : public Graph +{ + public: + ArrayGraph(unsigned vertexNumber); + virtual ~ArrayGraph(); + bool addEdge(unsigned v, unsigned w, unsigned weight); + bool removeEdge(unsigned v, unsigned w); + unsigned getWeight(unsigned v, unsigned w); + void displayGraph(); + + protected: + + private: + unsigned **graphMatrix; + unsigned *graphArray; + +}; + +#endif // ARRAYGRAPH_H diff --git a/Graph.cpp b/Graph.cpp new file mode 100755 index 0000000..745098a --- /dev/null +++ b/Graph.cpp @@ -0,0 +1,681 @@ +#include "Graph.h" +#include "Stopwatch.h" +#include +#include +#include +#include +#include + +#include + +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 Graph::travellingSalesmanBruteForce(Graph &graph) +{ + // ALGORYTM przegladu zupelnego + // Implementacja: Jan Potocki 2017 + // (refactoring 2019) + std::vector 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 minCombination; + int minRoute = -1; + + // Petla przegladajaca kolejne permutacje + do + { + std::vector 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 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< std::vector >, RouteComparison> routeQueue; + std::vector 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 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 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 Graph::travellingSalesmanGreedy(Graph &graph, unsigned startVertex) +{ + // ALGORYTM zachlanny z wierzcholkiem startowym przekazanym w parametrze + // Implementacja: Jan Potocki 2017 + std::vector route; + + // std::random_device randomSrc; + // std::default_random_engine randomGen(randomSrc()); + // std::uniform_int_distribution<> vertexDist(0, graph.vertexNumber - 1); + + // Losowanie wierzcholka startowego + //route.push_back(vertexDist(randomGen)); + 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 Graph::travellingSalesmanHybrid(Graph &graph) +{ + // ALGORYTM hybrydowy losowo-zachlanny + // Losowa czesc wierzcholkow jest losowana, reszta zachlannie + // Implementacja: Jan Potocki 2019 + std::vector 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); + + 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 Graph::travellingSalesmanRandom(Graph &graph) +{ + // ALGORYTM losowy + // Implementacja: Jan Potocki 2019 + std::vector 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 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 startVertexVector; + std::vector threadsVector; + std::vector> resultsVector(threadsNumber); + std::vector resultsLength(threadsNumber); + std::vector optimalResult; + int optimalResultIndex; + int optimalResultLength; + + std::random_device randomSrc; + std::default_random_engine randomGen(randomSrc()); + std::uniform_int_distribution<> vertexDist(0, graph.vertexNumber - 1); + + for(int i = 0; i < threadsNumber; i++) + { + std::vector startRoute; + unsigned startVertex; + bool startVertexUsed; + + if(i < graph.vertexNumber) + { + 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); + + startVertexVector.push_back(startVertex); + startRoute = Graph::travellingSalesmanGreedy(graph, startVertex); + } + else + { + startRoute = Graph::travellingSalesmanRandom(graph); + } + + 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)))); + } + + for(int i = 0; i < threadsNumber; i++) + threadsVector.at(i).join(); + + 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 startRoute, std::vector &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 optimalRoute; // Tu bedziemy zapisywac optymalne (w danej chwili) rozwiazanie + int optimalRouteLength = -1; // -1 - bedziemy odtad uznawac, ze to jest nieskonczonosc ;-) + std::vector currentRoute; // Rozpatrywane rozwiazanie + + // Wyznaczenie poczatkowego rozwiazania algorytmem zachlannym + //currentRoute = Graph::travellingSalesmanGreedy(graph); + currentRoute = startRoute; + + // Inicjalizacja glownej petli... + std::vector< std::vector > 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 nextRoute; + int nextRouteLength = -1; + + std::vector 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 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; + + if(optimalRouteLength == -1) + { + optimalRouteLength = nextRouteLength; + optimalRoute = nextRoute; + + // Reset licznika + stopCounter = 0; + } + else if(optimalRouteLength > nextRouteLength) + { + optimalRouteLength = nextRouteLength; + optimalRoute = nextRoute; + + // Zaplanowanie intensyfikacji + 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 przy ostatnim minimum + currentRoute = optimalRoute; + currentTabuSteps = tabuSteps / 4; + intensification = false; + } + else + { + // Algorytm hybrydowy losowo-zachlanny + currentRoute = Graph::travellingSalesmanHybrid(graph); + currentTabuSteps = tabuSteps; + intensification = false; + } + } + + // Reset licznika iteracji przed restartem + stopCounter = 0; + } + + result = optimalRoute; + resultLength = optimalRouteLength; +} diff --git a/Graph.h b/Graph.h new file mode 100755 index 0000000..a4fd6aa --- /dev/null +++ b/Graph.h @@ -0,0 +1,40 @@ +#ifndef GRAPH_H +#define GRAPH_H +#include + + +class Graph +{ + public: + Graph(); + virtual ~Graph(); + virtual bool addEdge(unsigned v, unsigned w, unsigned weight) = 0; + virtual bool removeEdge(unsigned v, unsigned w) = 0; + virtual unsigned getWeight(unsigned v, unsigned w) = 0; + unsigned getVertexNumber(); + virtual void displayGraph() = 0; + static void randomGenerateFullGraph(Graph &graph, unsigned maxWeight); + static std::vector travellingSalesmanBruteForce(Graph &graph); + static std::vector travellingSalesmanBranchAndBound(Graph &graph); + static std::vector travellingSalesmanGreedy(Graph &graph, unsigned startVertex); + static std::vector travellingSalesmanHybrid(Graph &graph); + static std::vector travellingSalesmanRandom(Graph &graph); + static std::vector travellingSalesmanTabuSearch(Graph &graph, unsigned tabuSteps, bool diversification, int iterationsToRestart, unsigned minStopTime, unsigned threadsNumber); + + protected: + unsigned vertexNumber; + + private: + static void travellingSalesmanTabuSearchEngine(Graph &graph, unsigned tabuSteps, bool diversification, int iterationsToRestart, unsigned minStopTime, std::vector startRoute, std::vector &result, int &resultLength); + + class RouteComparison + { + public: + bool operator() (const std::vector& lhs, const std::vector& rhs) const + { + return (lhs.at(0) > rhs.at(0)); + } + }; +}; + +#endif // GRAPH_H diff --git a/ListGraph.cpp b/ListGraph.cpp new file mode 100755 index 0000000..bb5b1a5 --- /dev/null +++ b/ListGraph.cpp @@ -0,0 +1,167 @@ +#include "ListGraph.h" +#include + +ListGraph::ListGraph(unsigned vertexNumber) +{ + //ctor + this->vertexNumber = vertexNumber; + graphList = new element*[vertexNumber]; + + for(int i = 0; i < vertexNumber; i++) + graphList[i] = NULL; +} + +ListGraph::~ListGraph() +{ + //dtor + for(int i = 0; i < vertexNumber; i++) + { + if(graphList[i] != NULL) + { + element *position = graphList[i]; + do + { + element *next = position->next; + delete position; + position = next; + } + while(position != NULL); + } + } + + delete[] graphList; +} + +bool ListGraph::addEdge(unsigned v, unsigned w, unsigned weight) +{ + if(weight >= 1000) + // Waga krawedzi musi byc mniejsza od 1000 + weight = 900; + + if(graphList[v] == NULL) + { + graphList[v] = new element; + graphList[v]->vertex = w; + graphList[v]->weight = weight; + graphList[v]->next = NULL; + + return true; + } + else + { + bool isAlready = false; + element *next = graphList[v]; + element *position = NULL; + do + { + position = next; + next = next->next; + if(position->vertex == w) + { + isAlready = true; + break; + } + } + while(next != NULL); + + if(!isAlready) + { + element *newEdge = new element; + newEdge->vertex = w; + newEdge->weight = weight; + newEdge->next = NULL; + position->next = newEdge; + + return true; + } + else + return false; + } +} + +bool ListGraph::removeEdge(unsigned v, unsigned w) +{ + if(graphList[v] == NULL) + return false; + else + { + bool isAlready = false; + element *next = graphList[v]; + element *position = NULL; + element *prev = NULL; + do + { + prev = position; + position = next; + next = next->next; + if(position->vertex == w) + { + isAlready = true; + break; + } + } + while(next != NULL); + + if(!isAlready) + return false; + else + { + delete position; + if(prev != NULL) + prev->next = next; + else + graphList[v] = next; + + return true; + } + } +} + +unsigned ListGraph::getWeight(unsigned v, unsigned w) +{ + if(graphList[v] == NULL) + return 0; + else + { + bool isAlready = false; + element *next = graphList[v]; + element *position = NULL; + do + { + position = next; + next = next->next; + if(position->vertex == w) + { + isAlready = true; + break; + } + } + while(next != NULL); + + if(!isAlready) + return 0; + else + return position->weight; + } +} + +void ListGraph::displayGraph() +{ + for(int i = 0; i < vertexNumber; i++) + { + std::cout << i << " -> "; + if(graphList[i] != NULL) + { + element *next = graphList[i]; + element *position = NULL; + do + { + position = next; + next = next->next; + std::cout << position->vertex << '@' << position->weight << ' '; + } + while(next != NULL); + } + std::cout << std::endl; + } +} diff --git a/ListGraph.h b/ListGraph.h new file mode 100755 index 0000000..928c2e3 --- /dev/null +++ b/ListGraph.h @@ -0,0 +1,29 @@ +#ifndef LISTGRAPH_H +#define LISTGRAPH_H + +#include "Graph.h" + +class ListGraph : public Graph +{ + public: + ListGraph(unsigned vertexNumber); + virtual ~ListGraph(); + bool addEdge(unsigned v, unsigned w, unsigned weight); + bool removeEdge(unsigned v, unsigned w); + unsigned getWeight(unsigned v, unsigned w); + void displayGraph(); + + protected: + + private: + struct element + { + unsigned vertex; + int weight; + element *next; + }; + element **graphList; + +}; + +#endif // LISTGRAPH_H diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..537ac92 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +CXXFLAGS = -O3 -Wall -std=c++11 + +ifeq ($(OS),Windows_NT) + detected_OS := Windows +else + detected_OS := $(shell uname) +endif +ifeq ($(detected_OS),Darwin) + CXXFLAGS += -stdlib=libc++ +endif + +OBJS = pea2plus.o ArrayGraph.o Graph.o ListGraph.o Stopwatch.o + +LIBS = -pthread + +TARGET = pea2plus + +$(TARGET): $(OBJS) + $(CXX) -o $(TARGET) $(OBJS) $(LIBS) + +all: $(TARGET) + +clean: + rm -f $(OBJS) $(TARGET) diff --git a/Stopwatch.cpp b/Stopwatch.cpp new file mode 100755 index 0000000..9fe15dc --- /dev/null +++ b/Stopwatch.cpp @@ -0,0 +1,23 @@ +#include "Stopwatch.h" + +Stopwatch::Stopwatch() +{ + //ctor + // Jan Potocki 2017-2019 +} + +void Stopwatch::start() +{ + tstart = std::chrono::steady_clock::now(); +} + +void Stopwatch::stop() +{ + tstop = std::chrono::steady_clock::now(); + measurement = tstop - tstart; +} + +double Stopwatch::read() +{ + return measurement.count(); +} diff --git a/Stopwatch.h b/Stopwatch.h new file mode 100755 index 0000000..05b1272 --- /dev/null +++ b/Stopwatch.h @@ -0,0 +1,22 @@ +#ifndef STOPWATCH_H +#define STOPWATCH_H + +#include + +// Klasa do pomiaru czasu (wieloplatformowa) +// Jan Potocki 2017-2019 +class Stopwatch +{ + public: + Stopwatch(); + void start(); + void stop(); + double read(); + protected: + private: + std::chrono::duration measurement; + std::chrono::time_point tstart; + std::chrono::time_point tstop; +}; + +#endif // STOPWATCH_H diff --git a/berlin52.tsp.gz b/berlin52.tsp.gz new file mode 100755 index 0000000..23c8f2f Binary files /dev/null and b/berlin52.tsp.gz differ diff --git a/ftv33.atsp.gz b/ftv33.atsp.gz new file mode 100755 index 0000000..ee58558 Binary files /dev/null and b/ftv33.atsp.gz differ diff --git a/ftv47.atsp.gz b/ftv47.atsp.gz new file mode 100644 index 0000000..d452766 Binary files /dev/null and b/ftv47.atsp.gz differ diff --git a/ftv64.atsp.gz b/ftv64.atsp.gz new file mode 100644 index 0000000..ff7c127 Binary files /dev/null and b/ftv64.atsp.gz differ diff --git a/pea2plus.cpp b/pea2plus.cpp new file mode 100755 index 0000000..b561a88 --- /dev/null +++ b/pea2plus.cpp @@ -0,0 +1,511 @@ +#include +#include +#include +#include +#include +#include "Stopwatch.h" +#include "ArrayGraph.h" +#include "ListGraph.h" + +using namespace std; + +// USTAWIENIA +// Liczba powtorzen automatycznych pomiarow do usrednienia +const int measureIterations = 10; +// liczba automatycznych pomiarow +const int measureNumber = 4; +// Czas zatrzymania algorytmu tabu search w kazdym z automatycznych pomiarow +const int measureTabuStop[4] = {1, 5, 10, 15}; +// Maksymalna odleglosc miast przy automatycznym generowaniu +const int measureSalesmanDistance = 400; + +// Kadencja tabu search +const int tabuLength = 30; +// Kryterium dywersyfikacji, liczba iteracji bez poprawy +const int tabuIterationsToRestart = 10000; + +// Wykorzystanie reprezentacji grafu w postaci list sasiedztwa... +// ...zamiast (domyslnie) macierzy sasiedztwa +// (wolniejsze obliczenia, mniejsze uzycie pamieci) +const bool useListGraph = false; + +// Liczba watkow tabu search +const unsigned tabuThreadsNumber = 2; + +// Domyslny czas zatrzymania algorytmu tabu search [s] +int tabuStopTime = 60; + +// Domyslny stan dywersyfikacji +bool tabuDiversification = true; + +int main() +{ + Stopwatch clock; // czasomierz + Graph *graph = NULL; // <- tu bedziemy zapisywac adresy przez caly program + + cout << "PEA Projekt 2 v2.0ALPHA Plus" << endl; + cout << "Jan Potocki 2017-2019" << endl; + cout << "(beerware)" << endl; + if(useListGraph) + cout << "Uzycie listowej reprezentacji grafu" << endl; + else + cout << "Uzycie macierzowej reprezentacji grafu" << endl; + cout << endl; + + int salesmanSelection; + do + { + cout << "1 - wygeneruj losowe dane" << endl; + cout << "2 - wyswietl dane" << endl; + cout << "3 - dywersyfikacja TS" << endl; + cout << "4 - czas zatrzymania TS" << endl; + cout << "5 - tabu search" << endl; + cout << "6 - algorytm zachlanny" << endl; + cout << "7 - podzial i ograniczenia" << endl; + cout << "8 - przeglad zupelny" << endl; + cout << "9 - automatyczne pomiary (tabu search)" << endl; + cout << "10 - wczytaj dane z pliku ATSP" << endl; + cout << "11 - wczytaj dane z pliku TSP" << endl; + cout << "Aby zakonczyc - 0" << endl; + cout << "Wybierz: "; + cin >> salesmanSelection; + cout << endl; + + switch(salesmanSelection) + { + case 1: + { + int vertex; + cout << "Liczba miast: "; + cin >> vertex; + cout << endl; + + if(graph != NULL) + delete graph; + + if(useListGraph) + graph = new ListGraph(vertex); + else + graph = new ArrayGraph(vertex); + + Graph::randomGenerateFullGraph(*graph, measureSalesmanDistance); + } + break; + case 2: + { + if(graph != NULL) + graph->displayGraph(); + else + cout << "Brak wygenerowanych danych" << endl; + cout << endl; + } + break; + case 3: + { + tabuDiversification = !tabuDiversification; + + if(tabuDiversification == true) + cout << "Dywersyfikacja TS zostala wlaczona" << endl; + else + cout << "Dywersyfikacja TS zostala wylaczona" << endl; + cout << endl; + } + break; + case 4: + { + cout << "Poprzedni czas pracy TS: " << tabuStopTime << endl; + cout << "Podaj nowy czas: "; + cin >> tabuStopTime; + cout << endl; + } + break; + case 5: + { + if(graph != NULL) + { + if(tabuStopTime != 0) + { + cout << "Kadencja: " << tabuLength << endl; + cout << "Czas zatrzymania algorytmu [s]: " << tabuStopTime << endl; + + if(tabuDiversification == true) + cout << "Dywersyfikacja wlaczona, kryterium: " << tabuIterationsToRestart << " iteracji" << endl; + else + cout << "Dywersyfikacja wylaczona" << endl; + + cout << endl; + + clock.start(); + vector route = Graph::travellingSalesmanTabuSearch(*graph, tabuLength, tabuDiversification, tabuIterationsToRestart, tabuStopTime, tabuThreadsNumber); + clock.stop(); + + // Wyswietlenie trasy + unsigned distFromStart = 0; + unsigned length = 0; + cout << route.at(0) << '\t' << length << '\t' << distFromStart << endl; + for(int i = 1; i < route.size(); i++) + { + length = graph->getWeight(route.at(i - 1), route.at(i)); + distFromStart += length; + + cout << route.at(i) << '\t' << length << '\t' << distFromStart << endl; + } + + cout << "Dlugosc trasy: " << distFromStart << endl; + cout << endl; + cout << "Czas wykonania algorytmu [s]: " << clock.read() << endl; + } + else + { + // Easter egg ;-) + cout << "+++ MELON MELON MELON +++ Blad: Brak Sera! +++ !!!!! +++" << endl; + } + } + else + cout << "+++ MELON MELON MELON +++ Brak zaladowanych danych +++" << endl; + cout << endl; + } + break; + case 6: + { + if(graph != NULL) + { + clock.start(); + vector route = Graph::travellingSalesmanGreedy(*graph, 0); + clock.stop(); + + // Wyswietlenie trasy + unsigned distFromStart = 0; + unsigned length = 0; + cout << route.at(0) << '\t' << length << '\t' << distFromStart << endl; + for(int i = 1; i < route.size(); i++) + { + length = graph->getWeight(route.at(i - 1), route.at(i)); + distFromStart += length; + + cout << route.at(i) << '\t' << length << '\t' << distFromStart << endl; + } + + cout << "Dlugosc trasy: " << distFromStart << endl; + cout << endl; + cout << "Czas wykonania algorytmu [s]: " << clock.read() << endl; + } + else + cout << "+++ MELON MELON MELON +++ Brak zaladowanych danych +++" << endl; + cout << endl; + } + break; + case 7: + { + if(graph != NULL) + { + clock.start(); + vector route = Graph::travellingSalesmanBranchAndBound(*graph); + clock.stop(); + + // Wyswietlenie trasy + unsigned distFromStart = 0; + unsigned length = 0; + cout << route.at(0) << '\t' << length << '\t' << distFromStart << endl; + for(int i = 1; i < route.size(); i++) + { + length = graph->getWeight(route.at(i - 1), route.at(i)); + distFromStart += length; + + cout << route.at(i) << '\t' << length << '\t' << distFromStart << endl; + } + + cout << "Dlugosc trasy: " << distFromStart << endl; + cout << endl; + cout << "Czas wykonania algorytmu [s]: " << clock.read() << endl; + } + else + cout << "+++ MELON MELON MELON +++ Brak zaladowanych danych +++" << endl; + cout << endl; + } + break; + case 8: + { + if(graph != NULL) + { + clock.start(); + vector route = Graph::travellingSalesmanBruteForce(*graph); + clock.stop(); + + // Wyswietlenie trasy + unsigned distFromStart = 0; + unsigned length = 0; + cout << route.at(0) << '\t' << length << '\t' << distFromStart << endl; + for(int i = 1; i < route.size(); i++) + { + length = graph->getWeight(route.at(i - 1), route.at(i)); + distFromStart += length; + + cout << route.at(i) << '\t' << length << '\t' << distFromStart << endl; + } + + cout << "Dlugosc trasy: " << distFromStart << endl; + cout << endl; + cout << "Czas wykonania algorytmu [s]: " << clock.read() << endl; + } + else + cout << "+++ MELON MELON MELON +++ Brak zaladowanych danych +++" << endl; + cout << endl; + } + break; + case 9: + { + // PEA 2 + // Jan Potocki 2017 + if(graph != NULL) + { + double measureResults[measureNumber], measureResultsDiv[measureNumber]; + for(int i = 0; i < measureNumber; i++) + { + measureResults[i] = 0; + measureResultsDiv[i] = 0; + } + + cout << "Pomiary dla problemu komiwojazera, tabu search" << tabuLength << endl; + cout << "Kadencja: " << tabuLength << endl; + cout << "Kryterium dywersyfikacji: " << tabuIterationsToRestart << " iteracji" << endl; + + // Petla pomiarowa + for(int krok = 0; krok < measureIterations; krok++) + { + for(int i = 0; i < measureNumber; i++) + { + vector route; + unsigned routeLength; + + // Bez dywersyfikacji + cout << "Pomiar " << measureTabuStop[i] << " [s] (" << krok + 1 << " z " << measureIterations << " bez dywersyfikacji)..." << endl; + + route = Graph::travellingSalesmanTabuSearch(*graph, tabuLength, false, tabuIterationsToRestart, measureTabuStop[i], tabuThreadsNumber); + + routeLength = 0; + for(int j = 1; j < route.size(); j++) + routeLength += graph->getWeight(route.at(j - 1), route.at(j)); + measureResults[i] += routeLength; + + // Z dywersyfikacja + cout << "Pomiar " << measureTabuStop[i] << " [s] (" << krok + 1 << " z " << measureIterations << " z dywersyfikacja)..." << endl; + + route = Graph::travellingSalesmanTabuSearch(*graph, tabuLength, true, tabuIterationsToRestart, measureTabuStop[i], tabuThreadsNumber); + + routeLength = 0; + for(int j = 1; j < route.size(); j++) + routeLength += graph->getWeight(route.at(j - 1), route.at(j)); + measureResultsDiv[i] += routeLength; + } + } + + cout << "Opracowywanie wynikow..." << endl; + + for(int i = 0; i < measureNumber; i++) + { + measureResults[i] = nearbyint(measureResults[i] / measureIterations); + measureResultsDiv[i] = nearbyint(measureResultsDiv[i] / measureIterations); + } + + cout << "Zapis wynikow..." << endl; + + ofstream salesmanToFile; + salesmanToFile.open("wyniki-komiwojazer-ts.txt"); + salesmanToFile << "czas - bez dywersyfikacji - z dywersyfikacja" << endl; + for(int i = 0; i < measureNumber; i++) + { + salesmanToFile << measureTabuStop[i] << " [s]: " << (int)measureResults[i] << ' ' << (int)measureResultsDiv[i] << endl; + } + salesmanToFile.close(); + + cout << "Gotowe!" << endl; + cout << endl; + } + else + { + cout << "+++ MELON MELON MELON +++ Brak zaladowanych danych +++" << endl; + cout << endl; + } + } + break; + case 10: + { + // Jan Potocki 2017 + string filename, fileInput; + ifstream salesmanDataFile; + + cout << "Podaj nazwe pliku: "; + cin >> filename; + + salesmanDataFile.open(filename.c_str()); + if(salesmanDataFile.is_open()) + { + do + salesmanDataFile >> fileInput; + while(fileInput != "DIMENSION:"); + + salesmanDataFile >> fileInput; + int vertex = stoi(fileInput); + + do + salesmanDataFile >> fileInput; + while(fileInput != "EDGE_WEIGHT_FORMAT:"); + + salesmanDataFile >> fileInput; + if(fileInput == "FULL_MATRIX") + { + if(graph != NULL) + delete graph; + + if(useListGraph) + graph = new ListGraph(vertex); + else + graph = new ArrayGraph(vertex); + + do + salesmanDataFile >> fileInput; + while(fileInput != "EDGE_WEIGHT_SECTION"); + + for(int i = 0; i < vertex; i++) + { + for(int j = 0; j < vertex; j++) + { + salesmanDataFile >> fileInput; + int weight = stoi(fileInput); + + if(i != j) + graph->addEdge(i, j, weight); + } + } + + cout << "Wczytano - liczba wierzcholkow: " << vertex << endl; + cout << endl; + } + else + { + cout << "+++ MELON MELON MELON +++ Nieobslugiwany format " << fileInput << " +++" << endl; + cout << endl; + } + + salesmanDataFile.close(); + } + else + { + cout << "+++ MELON MELON MELON +++ Brak pliku " << filename << " +++" << endl; + cout << endl; + } + } + break; + case 11: + { + // Jan Potocki 2017 + string filename, fileInput; + vector xCoord, yCoord; + ifstream salesmanDataFile; + + cout << "Podaj nazwe pliku: "; + cin >> filename; + + salesmanDataFile.open(filename.c_str()); + if(salesmanDataFile.is_open()) + { + do + salesmanDataFile >> fileInput; + while(fileInput != "DIMENSION:"); + + salesmanDataFile >> fileInput; + int vertex = stoi(fileInput); + + do + salesmanDataFile >> fileInput; + while(fileInput != "EDGE_WEIGHT_TYPE:"); + + salesmanDataFile >> fileInput; + if(fileInput == "EUC_2D") + { + if(graph != NULL) + delete graph; + + if(useListGraph) + graph = new ListGraph(vertex); + else + graph = new ArrayGraph(vertex); + + do + salesmanDataFile >> fileInput; + while(fileInput != "NODE_COORD_SECTION"); + + for(int i = 0; i < vertex; i++) + { + salesmanDataFile >> fileInput; + + salesmanDataFile >> fileInput; + xCoord.push_back(stof(fileInput)); + + salesmanDataFile >> fileInput; + yCoord.push_back(stof(fileInput)); + } + + // To daloby sie zrobic optymalniej (macierz symetryczna), ale nie chce mi sie ... + // ..wole zoptymalizować czas programowania ;-) + for(int i = 0; i < vertex; i++) + { + for(int j = 0; j < vertex; j++) + { + if(i != j) + { + float xDiff = xCoord.at(i) - xCoord.at(j); + float yDiff = yCoord.at(i) - yCoord.at(j); + int weight = nearbyint(sqrt(xDiff * xDiff + yDiff * yDiff)); + + graph->addEdge(i, j, weight); + } + } + } + + cout << "Wczytano - liczba wierzcholkow: " << vertex << endl; + cout << endl; + } + else + { + cout << "+++ MELON MELON MELON +++ Nieobslugiwany format " << fileInput << " +++" << endl; + cout << endl; + } + + salesmanDataFile.close(); + } + else + { + cout << "+++ MELON MELON MELON +++ Brak pliku " << filename << " +++" << endl; + cout << endl; + } + } + break; + case 0: + { + } + break; + default: + { + cout << "Nieprawidlowy wybor" << endl; + cout << endl; + } + } + } while(salesmanSelection != 0); + + if(graph != NULL) + delete graph; + + cout << "Konczenie..." << endl; + // Easter egg :-P + cout << '"' << "Myslak Stibbons niepokoil sie HEX-em." << endl; + cout << "Nie wiedzial, jak dziala, chociaz wszyscy uwazali, ze wie." << endl; + cout << "Oczywiscie, calkiem niezle orientowal sie w niektorych elementach;" << endl; + cout << "byl tez pewien, ze HEX mysli o problemach, przeksztalcajac je" << endl; + cout << "w liczby i mielac ..." << '"' << endl; + cout << "(Terry Pratchett, " << '"' << "Wiedzmikolaj" << '"' << ", tlumaczenie Piotr Cholewa)" << endl; + cout << endl; + + return 0; +} diff --git a/swiss42.tsp.gz b/swiss42.tsp.gz new file mode 100644 index 0000000..daa1e27 Binary files /dev/null and b/swiss42.tsp.gz differ