Initial commit

This commit is contained in:
Jan Potocki
2019-11-17 21:02:29 +01:00
commit bbc24df31f
16 changed files with 1623 additions and 0 deletions
+32
View File
@@ -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
Executable
+70
View File
@@ -0,0 +1,70 @@
#include "ArrayGraph.h"
#include <iostream>
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;
}
}
Executable
+24
View File
@@ -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
Executable
+681
View File
@@ -0,0 +1,681 @@
#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;
// 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<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);
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);
for(int i = 0; i < threadsNumber; i++)
{
std::vector<unsigned> 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<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;
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;
}
Executable
+40
View File
@@ -0,0 +1,40 @@
#ifndef GRAPH_H
#define GRAPH_H
#include <vector>
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<unsigned> travellingSalesmanBruteForce(Graph &graph);
static std::vector<unsigned> travellingSalesmanBranchAndBound(Graph &graph);
static std::vector<unsigned> travellingSalesmanGreedy(Graph &graph, unsigned startVertex);
static std::vector<unsigned> travellingSalesmanHybrid(Graph &graph);
static std::vector<unsigned> travellingSalesmanRandom(Graph &graph);
static std::vector<unsigned> 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<unsigned> startRoute, std::vector<unsigned> &result, int &resultLength);
class RouteComparison
{
public:
bool operator() (const std::vector<unsigned>& lhs, const std::vector<unsigned>& rhs) const
{
return (lhs.at(0) > rhs.at(0));
}
};
};
#endif // GRAPH_H
Executable
+167
View File
@@ -0,0 +1,167 @@
#include "ListGraph.h"
#include <iostream>
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;
}
}
Executable
+29
View File
@@ -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
+24
View File
@@ -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)
Executable
+23
View File
@@ -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();
}
Executable
+22
View File
@@ -0,0 +1,22 @@
#ifndef STOPWATCH_H
#define STOPWATCH_H
#include <chrono>
// Klasa do pomiaru czasu (wieloplatformowa)
// Jan Potocki 2017-2019
class Stopwatch
{
public:
Stopwatch();
void start();
void stop();
double read();
protected:
private:
std::chrono::duration<double> measurement;
std::chrono::time_point<std::chrono::steady_clock> tstart;
std::chrono::time_point<std::chrono::steady_clock> tstop;
};
#endif // STOPWATCH_H
BIN
View File
Binary file not shown.
Executable
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Executable
+511
View File
@@ -0,0 +1,511 @@
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
#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<unsigned> 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<unsigned> 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<unsigned> 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<unsigned> 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<unsigned> 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<float> 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;
}
BIN
View File
Binary file not shown.