public static IList <int> GeneticAlgorithm( MatrixGraph graph, int populationSize, long timeToPerform, Func <IList <int>, IList <int>, IList <int> > crossover, // rodzic1, rodzic2, zwraca dziecko double crossoverCoefficient, Func <IList <int>, IList <int> > mutation, //dziecko, zwraca zmutowane dziecko double mutationCoefficient ) { numberOfCities = graph.Size; Algorithms.populationSize = populationSize; Algorithms.crossover = crossover; Algorithms.crossoverCoefficient = crossoverCoefficient; Algorithms.mutation = mutation; Algorithms.mutationCoefficient = mutationCoefficient; int bestSolutionValue = int.MaxValue; var stopwatch = new Stopwatch(); stopwatch.Start(); #region stworzenie populacji startowej population = new List <IList <int> >(populationSize); populationSolutionValues = new int[populationSize]; for (int i = 0; i < populationSize; i++) { population.Add(GenerateRandomPermutation()); populationSolutionValues[i] = graph.CalculateRoute(population[i]); } #endregion bestSolution = population[0]; // defaultowe przypisanie while (stopwatch.ElapsedMilliseconds <= timeToPerform) { // selekcja rodziców (indeksy rodziców) List <int> parents = Selection(); // tworzenie dzieci - krzyżowanie, mutacja List <IList <int> > newPopulation = CreateNewGeneration(parents); population = newPopulation; // nowa populacja for (int i = 0; i < populationSize; i++) { populationSolutionValues[i] = graph.CalculateRoute(population[i]); if (populationSolutionValues[i] < bestSolutionValue) { bestSolution = population[i]; bestSolutionValue = populationSolutionValues[i]; } } } return(bestSolution); }
static MatrixGraph ReadGraphFromFile(string filePath) { string[] lines; int cities; try { lines = System.IO.File.ReadAllLines(filePath); cities = int.Parse(lines[0]); if (cities < 1) { throw new Exception("Liczba miast musi być większa od 0"); } } catch (Exception e) { Console.WriteLine(e.Message); return(null); } var readGraph = new MatrixGraph(cities); for (int i = 0; i < cities; i++) { char[] ss = { ' ', '\t' }; var strNumbers = lines[i + 1].Split(ss, StringSplitOptions.RemoveEmptyEntries); var intNumbers = new List <int>(); try { for (int j = 0; j < cities; j++) { int a = int.Parse(strNumbers[j]); if (i == j) { a = int.MaxValue; } intNumbers.Add(a); } } catch (Exception e) { Console.WriteLine(e.Message); return(null); } readGraph.SetMatrixRow(i, intNumbers); } return(readGraph); }
public static MatrixGraph GenerateRandomGraph(int size) { var graph = new MatrixGraph(size); var rand = new Random(); for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { if (i != j) { graph.Matrix[i, j] = rand.Next(1, 100); } else { graph.Matrix[i, j] = int.MaxValue; } } } return(graph); }
/// <summary> /// Przegląd zupełny za pomocą NextPermutation /// </summary> public static IList <int> BruteForce(MatrixGraph graph) { int[] permutation = new int[graph.Size - 1]; int[] bestPermutation = new int[graph.Size]; bestPermutation[0] = 0; for (int i = 1; i < graph.Size; i++) { permutation[i - 1] = i; } int bestSum = int.MaxValue, newSum; do { newSum = graph.Matrix[0, permutation[0]] + graph.CalculateRoute(permutation); if (newSum < bestSum) { bestSum = newSum; Array.Copy(permutation, 0, bestPermutation, 1, graph.Size - 1); } } while (NextPermutation(permutation)); return(bestPermutation); }
public static IList <int> BranchAndBoundBestFirst(MatrixGraph givenGraph) { graph = givenGraph; var queue = new PriorityQueue <BnBVertex>(); queue.Enqueue(new BnBVertex(0, 0)); int higherBound = int.MaxValue; List <int> bestPermutation = null; int iPow2, logicAnd; while (queue.Count != 0) { var vertexLowestBound = queue.Dequeue(); if (BranchAndBoundLowerBound(vertexLowestBound) < higherBound) { for (int i = 1; i < graph.Size; i++) { iPow2 = 1 << i; logicAnd = iPow2 & vertexLowestBound.BitMask; if (logicAnd == 0) { var childVertex = new BnBVertex(i, vertexLowestBound.BitMask + iPow2, vertexLowestBound); int value = BranchAndBoundValue(childVertex); if (value < higherBound) { higherBound = value; bestPermutation = childVertex.Vertices; } BranchAndBoundLowerBound(childVertex); if (childVertex.LowerBound < higherBound) { queue.Enqueue(childVertex); } } } } } return(bestPermutation); }
public static IList <int> DynamicProgramming(MatrixGraph givenGraph) { // 1 << n == de facto 2 do potęgi n int pow2 = 1 << givenGraph.Size; // pow2-1, ponieważ miasto 0 jest zawsze pierwsze, więc nigdy nie będzie w zbiorze pozostałych miast do przejścia solutionsMatrix = new int[pow2 - 1, givenGraph.Size]; verticesMatrix = new int[pow2 - 1, givenGraph.Size]; // problemy trywialne (trasa wierzchołek 'i' -> wierzchołek 1) for (int i = 1; i < givenGraph.Size; i++) { solutionsMatrix[0, i] = givenGraph.Matrix[i, 0]; } graph = givenGraph; int bestSum = DynamicProgrammingGetMinimum(pow2 - 2, 0); dynamicProgrammingPermutationList = new List <int>(graph.Size); dynamicProgrammingPermutationList.Add(0); DynamicProgrammingReturnPath(pow2 - 2, 0); return(dynamicProgrammingPermutationList); }
static long TestAlgorithm(Func <MatrixGraph, IList <int> > algorithm, int problemSize) { int tests = 100; MatrixGraph graph; Stopwatch stopwatch = new Stopwatch(); long nanosecondsPerTick = 1000000000L / Stopwatch.Frequency; long time = 0; // w nanosekundach for (int i = 0; i < tests + 1; ++i) { graph = MatrixGraph.GenerateRandomGraph(problemSize); stopwatch.Reset(); stopwatch.Start(); var bestPermutation = algorithm(graph); stopwatch.Stop(); // wyrzucenie pierwszego testu, który może zepsuć średnią if (i != 0) { time += stopwatch.ElapsedTicks * nanosecondsPerTick; } } return(time /= tests); }
static void ConsoleMenu() { while (true) { Console.WriteLine(graph == null ? "\nNIE WCZYTANO MIAST." : "\nWCZYTANO MIASTA."); switch (neighbourhoodTypeInt) { case 1: Console.Write("Sąsiedztwo: Swap, "); break; case 2: Console.Write("Sąsiedztwo: Reverse, "); break; } Console.Write(timePerform != long.MaxValue / 1000 ? $"Maksymalny czas: {timePerform}s, " : "Brak limitu czasu, "); Console.Write($"Dywersyfikacja włączona: {diversification}\n"); Console.WriteLine( @"1. Wczytanie danych z pliku 2. Wprowadzenie kryterium stopu 3. Włączenie/Wyłączenie dywersyfikacji 4. Wybór sąsiedztwa 5. Algorytm symulowanego wyżarzania 6. Algorytm tabu search 0. Wyjdź"); var strInput = Console.ReadLine(); int intInput; try { intInput = int.Parse(strInput); } catch (Exception e) { Console.WriteLine(e.Message); continue; } switch (intInput) { case 1: Console.WriteLine("Podaj ścieżkę do pliku: "); strInput = Console.ReadLine(); graph = ReadGraphFromFile(strInput); break; case 2: Console.WriteLine("Podaj maksymalny czas w sekundach (0 - brak limitu)"); strInput = Console.ReadLine(); try { intInput = int.Parse(strInput); } catch (Exception e) { Console.WriteLine(e.Message); break; } timePerform = intInput > 0 ? intInput : long.MaxValue / 1000; break; case 3: Console.WriteLine("Dywersyfikacja włączona? Y/N"); strInput = Console.ReadLine(); switch (strInput) { case "Y": case "y": diversification = true; break; case "N": case "n": diversification = false; break; } break; case 4: Console.WriteLine("Wybór sąsiedztwa: 1. Swap, 2. Reverse"); strInput = Console.ReadLine(); try { intInput = int.Parse(strInput); } catch (Exception e) { Console.WriteLine(e.Message); break; } switch (intInput) { case 1: neighbourhoodTypeInt = 1; neighbourhoodType = Algorithms.Swap; break; case 2: neighbourhoodTypeInt = 2; neighbourhoodType = Algorithms.Reverse; break; } break; case 5: if (graph != null) { PerformAlgorithm(Algorithms.SimulatedAnnealing); } break; case 6: if (graph != null) { PerformAlgorithm(Algorithms.TabuSearch); } break; case 0: Environment.Exit(0); break; default: Console.WriteLine("Niepoprawna opcja."); break; } } }
static void ConsoleMenu() { while (true) { Console.WriteLine(graph == null ? "\nNIE WCZYTANO MIAST." : "\nWCZYTANO MIASTA."); Console.Write( timeToPerform != long.MaxValue / 1000 ? $"Maksymalny czas: {timeToPerform / 1000}s, " : "Brak limitu czasu, " ); switch (mutationType) { case 1: Console.Write("Mutacja: Swap, "); break; case 2: Console.Write("Mutacja: Reverse, "); break; } switch (crossoverType) { case 1: Console.Write("Krzyżowanie: Partially Matched Crossover\n"); break; case 2: Console.Write("Krzyżowanie: Order Crossover\n"); break; } Console.Write( $"Wielkość populacji: {populationSize}, Współczynnik mutacji: {mutationCoefficient}, Współczynnik krzyżowania: {crossoverCoefficient}"); Console.WriteLine(); Console.WriteLine(); Console.WriteLine( @"1. Wczytanie danych z pliku 2. Wprowadzenie kryterium stopu 3. Ustawienie wielkości populacji początkowej 4. Ustawienie współczynnika mutacji 5. Ustawienie współczynnika krzyżowania 6. Wybór metody mutacji 7. Wybór metody krzyżowania 8. Uruchomienie algorytmu genetycznego 0. Wyjdź"); var strInput = Console.ReadLine(); int intInput; double doubleInput; try { intInput = int.Parse(strInput); } catch (Exception e) { Console.WriteLine(e.Message); continue; } switch (intInput) { case 1: Console.WriteLine("Podaj ścieżkę do pliku: "); strInput = Console.ReadLine(); graph = ReadGraphFromFile(strInput); break; case 2: Console.WriteLine("Podaj maksymalny czas w sekundach (0 - brak limitu)"); strInput = Console.ReadLine(); try { intInput = int.Parse(strInput); } catch (Exception e) { Console.WriteLine(e.Message); break; } timeToPerform = intInput > 0 ? intInput * 1000 : long.MaxValue / 1000; break; case 3: Console.WriteLine("Podaj nową wielkość populacji, musi być parzysta"); strInput = Console.ReadLine(); try { intInput = int.Parse(strInput); if (intInput <= 0) { throw new Exception("Wielkość populacji musi być większa od 0"); } if (intInput % 2 == 1) { throw new Exception("Wielkość populacji w założeniu jest parzysta"); } } catch (Exception e) { Console.WriteLine(e.Message); break; } populationSize = intInput; break; case 4: Console.WriteLine("Podaj nowy współczynnik mutacji"); strInput = Console.ReadLine(); try { doubleInput = double.Parse(strInput); if (doubleInput < 0.0 || doubleInput > 1.0) { throw new Exception("Współczynnik mutacji musi być w przedziale [0, 1]"); } } catch (Exception e) { Console.WriteLine(e.Message); break; } mutationCoefficient = doubleInput; break; case 5: Console.WriteLine("Podaj nowy współczynnik krzyżowania"); strInput = Console.ReadLine(); try { doubleInput = double.Parse(strInput); if (doubleInput < 0.0 || doubleInput > 1.0) { throw new Exception("Współczynnik krzyżowania musi być w przedziale [0, 1]"); } } catch (Exception e) { Console.WriteLine(e.Message); break; } crossoverCoefficient = doubleInput; break; case 6: Console.WriteLine("Wybór mutacji: 1. Swap, 2. Reverse"); strInput = Console.ReadLine(); try { intInput = int.Parse(strInput); } catch (Exception e) { Console.WriteLine(e.Message); break; } switch (intInput) { case 1: mutationType = Mutation1; mutation = Algorithms.MutationSwap; break; case 2: mutationType = Mutation2; mutation = Algorithms.MutationReverse; break; } break; case 7: Console.WriteLine("Wybór krzyżowania: 1. Partially Matched Crossover, 2. Order Crossover"); strInput = Console.ReadLine(); try { intInput = int.Parse(strInput); } catch (Exception e) { Console.WriteLine(e.Message); break; } switch (intInput) { case 1: crossoverType = Crossover1; crossover = Algorithms.PartiallyMatchedCrossover; break; case 2: crossoverType = Crossover2; crossover = Algorithms.OrderCrossover; break; } break; case 8: if (graph != null) { // tworzenie wątku odpowiadającego za wyświetlanie pośrednich wyników var timesThread = new Thread(() => { const int n = 5; // liczba próbek czasu for (int i = 0; i < n; i++) { Thread.Sleep((int)(timeToPerform / n)); Console.WriteLine( $"{stopwatch.ElapsedMilliseconds / 1000.0}s - {graph.CalculateRoute(Algorithms.bestSolution)}"); } }); Console.WriteLine("Czas - koszt uzyskanej trasy"); timesThread.Start(); stopwatch.Restart(); solution = Algorithms.GeneticAlgorithm( graph, populationSize, timeToPerform, crossover, crossoverCoefficient, mutation, mutationCoefficient ); timesThread.Join(); stopwatch.Stop(); Console.WriteLine($"Czas: {stopwatch.ElapsedMilliseconds / 1000.0}s"); Console.Write("Trasa: "); WriteList(solution); Console.Write("Koszt: "); Console.WriteLine(graph.CalculateRoute(solution)); } break; case 0: Environment.Exit(0); break; default: Console.WriteLine("Niepoprawna opcja."); break; } } }
/// <summary> /// https://cs.pwr.edu.pl/zielinski/lectures/om/localsearch.pdf - strona 13 /// http://www.pi.zarz.agh.edu.pl/intObl/notes/IntObl_w2.pdf - strona 21 /// </summary> /// <param name="graph"></param> /// <param name="neighbourhood">typ sąsiedztwa</param> /// <param name="time">czas W SEKUNDACH</param> /// <param name="notImportant">bool jako parametr tylko po to by parametry zgadzały się z tabu search</param> /// <returns></returns> public static IList <int> SimulatedAnnealing(MatrixGraph graph, Action <IList <int>, int, int> neighbourhood, long time, bool notImportant) { int numberOfCities = graph.Size; int bestSolutionValue; int currentSolutionValue; int[] bestSolution = new int[numberOfCities]; int[] currentSolution = new int[numberOfCities]; int[] neighbourSolution = new int[numberOfCities]; #region generowanie pierwszego, losowego rozwiązania for (int i = 0; i < numberOfCities; i++) { currentSolution[i] = i; } Shuffle(currentSolution); bestSolutionValue = currentSolutionValue = graph.CalculateRoute(currentSolution); Array.Copy(currentSolution, bestSolution, numberOfCities); #endregion #region temperatura początkowa, współczynnik skalowania, końcowa temperatura double temperature = bestSolutionValue * numberOfCities; const double alpha = 0.99; const double endTemperature = 0.000000001; #endregion var stopwatch = new Stopwatch(); time *= 1000; //zamiana sekund na milisekundy stopwatch.Start(); while (stopwatch.ElapsedMilliseconds <= time && temperature > endTemperature) { bool foundBetterSolution; do { foundBetterSolution = false; for (int i = 0; i < numberOfCities * 5; i++) { #region generowanie losowej permutacji z sąsiedztwa obecnego rozwiązania Array.Copy(currentSolution, neighbourSolution, numberOfCities); neighbourhood(neighbourSolution, random.Next(1, numberOfCities), random.Next(1, numberOfCities)); int neighbourSolutionValue = graph.CalculateRoute(neighbourSolution); #endregion int delta = neighbourSolutionValue - currentSolutionValue; #region jeśli nowa permutacja jest najlepsza, ustaw ją jako obecną i najlepszą permutację if (neighbourSolutionValue < bestSolutionValue) { foundBetterSolution = true; Array.Copy(neighbourSolution, currentSolution, numberOfCities); currentSolutionValue = neighbourSolutionValue; Array.Copy(neighbourSolution, bestSolution, numberOfCities); bestSolutionValue = neighbourSolutionValue; } #endregion #region w przeciwnym razie, jeśli nowa permutacja jest lepsza niż obecna, ustaw ją jako obecną else if (delta < 0) { Array.Copy(neighbourSolution, currentSolution, numberOfCities); currentSolutionValue = neighbourSolutionValue; } #endregion #region w przeciwnym razie, jeśli nowa permutacja jest gorsza od obecnej, ustaw ją jako obecną z prawdopodobieństwem exp(-delta/T) else if (random.NextDouble() < Math.Exp(-delta / temperature)) { Array.Copy(neighbourSolution, currentSolution, numberOfCities); currentSolutionValue = neighbourSolutionValue; } #endregion } } while (foundBetterSolution); temperature *= alpha; } return(bestSolution); }
/// <summary> /// http://www.zio.iiar.pwr.wroc.pl/pea/w5_ts.pdf - strona 20 /// http://www.pi.zarz.agh.edu.pl/intObl/notes/IntObl_w2.pdf - strona 38 /// </summary> /// <param name="graph"></param> /// <param name="neighbourhood">typ sąsiedztwa</param> /// <param name="time">czas W SEKUNDACH</param> /// <param name="diversification"></param> /// <returns></returns> public static IList <int> TabuSearch(MatrixGraph graph, Action <IList <int>, int, int> neighbourhood, long time, bool diversification) { int numberOfCities = graph.Size; int bestSolutionValue; int currentSolutionValue; int[] bestSolution = new int[numberOfCities]; int[] currentSolution = new int[numberOfCities]; int[] neighbourSolution = new int[numberOfCities]; // int lifetime = numberOfCities; int lifetime = 10; int criticalEvents = 0; // w C# tablice domyślnie wypełnione są zerami int[,] tabuList = new int[numberOfCities, numberOfCities]; #region generowanie pierwszego, losowego rozwiązania for (int i = 0; i < numberOfCities; i++) { currentSolution[i] = i; } Shuffle(currentSolution); bestSolutionValue = currentSolutionValue = graph.CalculateRoute(currentSolution); Array.Copy(currentSolution, bestSolution, numberOfCities); #endregion var stopwatch = new Stopwatch(); stopwatch.Start(); time *= 1000; // zamiana sekund na milisekundy while (stopwatch.ElapsedMilliseconds <= time) { int previousCurrentSolutionValue = currentSolutionValue; #region znalezienie najlepszego rozwiązania w sąsiedztwie int bestI = 0, bestJ = 0; for (int i = 1; i < numberOfCities; i++) { for (int j = i + 1; j < numberOfCities; j++) { Array.Copy(currentSolution, neighbourSolution, numberOfCities); neighbourhood(neighbourSolution, i, j); int neighbourSolutionValue = graph.CalculateRoute(neighbourSolution); // jeżeli rozpatrywane rozwiązanie jest lepsze od najlepszego dotychczas znalezionego, to przyjmujemy je jako obecne nawet jeżeli jest zakazane // w przeciwnym wypadku przyjmujemy tylko jeśli jest lepsze od obecnego rozwiązania i nie ma go na liście tabu if (neighbourSolutionValue < bestSolutionValue || (neighbourSolutionValue < currentSolutionValue && tabuList[i, j] == 0)) { currentSolutionValue = neighbourSolutionValue; bestI = i; bestJ = j; } } } neighbourhood(currentSolution, bestI, bestJ); tabuList[bestI, bestJ] = lifetime; #endregion TabuSearchDecrementTabuList(tabuList); if (currentSolutionValue < bestSolutionValue) { Array.Copy(currentSolution, bestSolution, numberOfCities); bestSolutionValue = currentSolutionValue; criticalEvents = 0; } else if (diversification && currentSolutionValue >= previousCurrentSolutionValue) { criticalEvents++; // zbyt dużo krytycznych momentów, dywersyfikacja (jeśli włączona) if (criticalEvents >= 20 /*10*//*lifetime*/) { Shuffle(currentSolution); currentSolutionValue = graph.CalculateRoute(currentSolution); TabuSearchEmptyTabuList(tabuList); } } } return(bestSolution); }
static void ConsoleMenu() { bool running = true; string strInput; int intInput; while (running) { if (graph == null) { Console.WriteLine("\nNIE WCZYTANO MIAST."); } else { Console.WriteLine("\nWCZYTANO MIASTA."); } Console.WriteLine("1. Przegląd zupełny\n2. Programowanie dynamiczne\n3. Podział i ograniczenia - przeszukiwanie wszerz\n4. Podział i ograniczenia - najpierw najlepszy\n8. Kompletny test\n9. Wczytaj zadania z pliku\n0. Wyjdź"); strInput = Console.ReadLine(); try { intInput = int.Parse(strInput); } catch (Exception e) { Console.WriteLine(e.Message); continue; } switch (intInput) { case 1: if (graph != null) { PerformAlgorithm(Algorithms.BruteForce); } break; case 2: if (graph != null) { PerformAlgorithm(Algorithms.DynamicProgramming); } break; case 3: if (graph != null) { PerformAlgorithm(Algorithms.BranchAndBoundBreadthSearch); } break; case 4: if (graph != null) { PerformAlgorithm(Algorithms.BranchAndBoundBestFirst); } break; case 8: Console.WriteLine("Przeprowadzanie kompletnego testu."); CompleteTest(); break; case 9: Console.WriteLine("Podaj ścieżkę do pliku: "); strInput = Console.ReadLine(); graph = ReadGraphFromFile(strInput); break; case 0: Environment.Exit(0); break; default: Console.WriteLine("Niepoprawna opcja."); break; } } }