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 int BranchAndBoundValue(BnBVertex vertex) { if (vertex.Vertices.Count == graph.Size) { return(graph.CalculateRoute(vertex.Vertices)); } else { return(int.MaxValue); } }
static void PerformAlgorithm(Func <MatrixGraph, Action <IList <int>, int, int>, long, bool, IList <int> > algorithm) { stopwatch.Restart(); solution = algorithm(graph, neighbourhoodType, timePerform, diversification); stopwatch.Stop(); Console.WriteLine($"Czas: {stopwatch.ElapsedMilliseconds / 1000.0}s"); Console.Write("Trasa: "); WriteList(solution); Console.Write("Koszt: "); Console.WriteLine(graph.CalculateRoute(solution)); }
static void PerformAlgorithm(Func <MatrixGraph, IList <int> > algorithm) { stopwatch.Restart(); bestPermutation = algorithm(graph); stopwatch.Stop(); Console.WriteLine(string.Format("Czas: {0}s", stopwatch.ElapsedMilliseconds / 1000.0)); Console.Write("Trasa: "); WriteList(bestPermutation); Console.Write("Koszt: "); Console.WriteLine(graph.CalculateRoute(bestPermutation)); }
/// <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); }
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); }