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