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 0000000..3b3eacf Binary files /dev/null and b/Lab4/places.tsv differ diff --git a/Labs.sln b/Labs.sln index 0eb9884..1a2d650 100644 --- a/Labs.sln +++ b/Labs.sln @@ -7,7 +7,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lab1", "Lab1\Lab1.csproj", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lab2", "Lab2\Lab2.csproj", "{6AF5D775-08B9-4D4F-9E2E-116C551D0581}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab3", "Lab3\Lab3.csproj", "{C3128C1D-C1F7-476A-808F-1A3FE5E848FA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lab3", "Lab3\Lab3.csproj", "{C3128C1D-C1F7-476A-808F-1A3FE5E848FA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lab4", "Lab4\Lab4.csproj", "{6C74D00D-C10B-4249-99F7-E2CAD924D96B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,6 +29,10 @@ Global {C3128C1D-C1F7-476A-808F-1A3FE5E848FA}.Debug|Any CPU.Build.0 = Debug|Any CPU {C3128C1D-C1F7-476A-808F-1A3FE5E848FA}.Release|Any CPU.ActiveCfg = Release|Any CPU {C3128C1D-C1F7-476A-808F-1A3FE5E848FA}.Release|Any CPU.Build.0 = Release|Any CPU + {6C74D00D-C10B-4249-99F7-E2CAD924D96B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6C74D00D-C10B-4249-99F7-E2CAD924D96B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6C74D00D-C10B-4249-99F7-E2CAD924D96B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6C74D00D-C10B-4249-99F7-E2CAD924D96B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE