From 661b55ace61cfa9623e72d747a3be6120b7a24bd Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Fri, 26 May 2023 23:15:11 +0300 Subject: [PATCH] finish lab4 --- Lab4/Lab4.csproj | 26 +++++++ Lab4/Program.cs | 197 +++++++++++++++++++++++++++++++++++++++++++++++ Lab4/places.tsv | Bin 0 -> 37490 bytes Labs.sln | 8 +- 4 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 Lab4/Lab4.csproj create mode 100644 Lab4/Program.cs create mode 100644 Lab4/places.tsv diff --git a/Lab4/Lab4.csproj b/Lab4/Lab4.csproj new file mode 100644 index 0000000..6a32d2b --- /dev/null +++ b/Lab4/Lab4.csproj @@ -0,0 +1,26 @@ + + + + Exe + net6.0 + enable + enable + x64 + False + + + + False + + + + False + + + + + PreserveNewest + + + + diff --git a/Lab4/Program.cs b/Lab4/Program.cs new file mode 100644 index 0000000..e3d7136 --- /dev/null +++ b/Lab4/Program.cs @@ -0,0 +1,197 @@ +using System.Diagnostics; +using System.IO; + +namespace Lab4 +{ + class Place + { + public string name; + public float x; + public float y; + public Place(string name, float x, float y) + { + this.name = name; + this.x = x; + this.y = y; + } + } + + internal class Program + { + public static double GetDistance(Place A, Place B) + { + float dx = A.x - B.x; + float dy = A.y - B.y; + return Math.Sqrt(dx * dx + dy * dy); + } + + public static double GetTravalCost(Place A, Place B) + { + return Math.Sqrt(GetDistance(A, B)); + } + + public static List ReadPlacesFromTSV(string filename) + { + List cities = new List(); + foreach (var line in File.ReadLines(filename)) + { + string[] parts = line.Split("\t"); + + string name = parts[0]; + float x = float.Parse(parts[2]); + float y = float.Parse(parts[3]); + cities.Add(new Place(name, x, y)); + } + return cities; + } + + // Warning 'PickUnqiueNode' could get stuck in an infinite loop, if there are not enough places + public static int PickUnqiueNode(Random rand, List path, int placesCount) + { + while (true) + { + int num = rand.Next(0, placesCount); + if (!path.Contains(num)) + { + return num; + } + } + } + + public static List GenerateRandomPath(Random rand, int size, int placesCount) + { + List path = new List(size); + for (int i = 0; i < size; i++) + { + path.Add(PickUnqiueNode(rand, path, placesCount)); + } + return path; + } + + public static double GetPathCost(List path, List places) + { + double cost = 0; + for (int i = 0; i < path.Count-1; i++) + { + int from = path[i]; + int to = path[i+1]; + cost += GetTravalCost(places[from], places[to]); + } + cost += GetTravalCost(places[path.First()], places[path.Last()]); + return cost; + } + + public static List SplicePaths(Random rand, List A, List B) + { + Debug.Assert(A.Count == B.Count); + + List spliced = new List(A.Count); + for (int i = 0; i < A.Count-1; i++) + { + spliced[i] = rand.Next(0, 2) == 0 ? A[i] : B[i]; + } + return spliced; + } + + public static void MutatePath(Random rand, List path, float mutationPickUniqueChance, float mutationSwapChance, int placesCount) + { + for (int i = 0; i < path.Count; i++) + { + if (rand.NextSingle() < mutationPickUniqueChance) { + path[i] = PickUnqiueNode(rand, path, placesCount); + } + if (rand.NextSingle() < mutationSwapChance) { + int idx1 = rand.Next(0, path.Count); + int idx2 = rand.Next(0, path.Count); + + (path[idx2], path[idx1]) = (path[idx1], path[idx2]); + } + } + } + + static void Main(string[] args) + { + /* + Faile places_data.xlsx pateikta informacija apie lankytinas vietas (1 lentelė). Tikslas: kaip galima pigesnio maršruto sudarymas kai, + + * priimama, kad kelionės tarp vietų kaina lygi kvadratinei šakniai iš kelionės atstumo; + * kelionės pradžios ir pabaigos vieta sutampa (su grįžimu atgal); + * ta pati vieta negali būti aplankyta daugiau nei vieną; + * Reikia aplankyti bet kurias 150 vietų iš pateikto sąrašo. + */ + + int pathLength = 150; + int batchSize = 50; + int timeLimitMs = 60 * 1000; + float mutationPickUniqueChance = 0.05f; + float mutationSwapChance = 0.05f; + + int seed = (int)DateTime.Now.Ticks; + Random rand = new Random(seed); + + var places = ReadPlacesFromTSV("places.tsv"); + + var bestPaths = new SortedList>(); + for (int i = 0; i < batchSize; i++) + { + var path = GenerateRandomPath(rand, pathLength, places.Count); + var cost = GetPathCost(path, places); + bestPaths.Add(cost, path); + } + + var stopwatch = new Stopwatch(); + int iteration = 0; + stopwatch.Start(); + while (stopwatch.ElapsedMilliseconds < timeLimitMs) + { + double bestScore = bestPaths.First().Key; + double worstScore = bestPaths.Last().Key; + + if (iteration % 200 == 0) + { + //Console.WriteLine("At {0}, best '{1}', worst '{2}'", iteration, bestScore, worstScore); + } + + var removedPaths = new List(); + var parentPool = new List>(); + foreach (var entry in bestPaths) + { + double probability = (entry.Key - bestScore) / (worstScore - bestScore); + if (rand.NextSingle() >= probability) { + parentPool.Add(entry.Value); + } else { + removedPaths.Add(entry.Key); + } + } + + foreach (var key in removedPaths) + { + bestPaths.Remove(key); + } + + while (batchSize > bestPaths.Count) + { + var parentIdx = rand.Next(0, parentPool.Count); + var parent = parentPool[parentIdx]; + var child = new List(parent); + MutatePath(rand, child, mutationPickUniqueChance, mutationSwapChance, places.Count); + var cost = GetPathCost(child, places); + if (!bestPaths.ContainsKey(cost)) { + bestPaths.Add(cost, child); + } + } + + iteration++; + } + stopwatch.Stop(); + + Console.WriteLine("Best after {0} iterations is {1}", iteration, bestPaths.First().Key); + Console.WriteLine("Time taken: {0}ms", stopwatch.ElapsedMilliseconds); + Console.WriteLine("Seed: {0}", seed); + + // Best after 484012 iterations is 30826.444176719036 + // Time taken: 60000ms + // Seed: -1477536140 + } + } +} \ No newline at end of file diff --git a/Lab4/places.tsv b/Lab4/places.tsv new file mode 100644 index 0000000000000000000000000000000000000000..3b3eacfec1320b8f03ec40655a087c8caba2a005 GIT binary patch literal 37490 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