Bläddra i källkod

feat: add generate Traveling Salesman Problem answer

ArrayKat 5 månader sedan
förälder
incheckning
a51a3be4f7

+ 412 - 0
MathModelingSimulator/Function/TravelingSalesmanProblem.cs

@@ -0,0 +1,412 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MathModelingSimulator.Function
+{
+    internal struct BranchMatrix
+    {
+        public int id; //уникальный код, по которому будем находить обратный путь
+        public int objFunction; //целевая функция
+        public (int start, int end, bool isNotBranch) path; // картедж, с значениями начала и конца ветки и то, включена ли эта ветка в решение
+        public int[,] matrix; //матрица, которая соответствует решению
+        public int previosId; //целевая функция предыдущего элемента
+    }
+    internal class TravelingSalesmanProblem
+    {
+        static int id = 0;
+        static int[,] Mass;
+        static int M = int.MaxValue;
+
+
+        static List<BranchMatrix> Matrix = new List<BranchMatrix>(); //общий лист со всеми матрицами
+        static List<BranchMatrix> minBranch = new List<BranchMatrix>(); //лист с матрицами, которые не ветвились
+
+        static int[,] cloneArr(int[,] mas)
+        {
+            int[,] newMas = new int[mas.GetLength(0), mas.GetLength(1)];
+            for (int i = 0; i < mas.GetLength(0); i++)
+            {
+                for (int j = 0; j < mas.GetLength(1); j++)
+                {
+                    newMas[i, j] = mas[i, j];
+                }
+            }
+            return newMas;
+        }
+        public int MainTravelingSalesmanProblem(int[,] mas)
+        {
+            InputMas(mas); 
+
+
+            BranchMatrix previous; //структура , которая хранит в себе значение предыдущего элемента
+            previous.id = id;
+            previous.previosId = id - 1;
+            previous.matrix = Mass;
+            previous.objFunction = Reduction(ref Mass);
+            previous.path = (0, 0, false);
+            Matrix.Add(previous); //добавляем 1 элемент (h0)
+            id++;
+
+            while (!isEndIteration(previous.matrix)) //пока не останется матрица 2х2
+            {
+                BranchMatrix notParth; //с путем
+                BranchMatrix withParth; //буз путя
+                int MaxZeroCell = 0;
+
+                //оцениваем свободные ячейки, выбираем из этой матрицы макимальное значение (пути: "с = true" или "без = false")
+                notParth.path = MaxMas(ZeroCellEvaluation(cloneArr(previous.matrix)), ref MaxZeroCell, false);
+                withParth.path = MaxMas(ZeroCellEvaluation(cloneArr(previous.matrix)), ref MaxZeroCell, true);
+
+
+                notParth.matrix = WithoutAPath(cloneArr(previous.matrix), withParth.path.start, withParth.path.end);
+
+                //подсчитываем значение целефой функции
+                notParth.objFunction = previous.objFunction + MaxZeroCell;
+                //если у нас остается матрица 2х2:
+                if (WithAPath(cloneArr(previous.matrix), withParth.path.start, withParth.path.end, out withParth.matrix) == -1)
+                {
+                    //высчитываем значение для предпоследнего значения
+                    withParth.objFunction = previous.objFunction + WithAPath(cloneArr(previous.matrix), withParth.path.start, withParth.path.end, out withParth.matrix);
+                    withParth.previosId = previous.id;
+                    withParth.id = id + 1;
+                    id++;
+
+                    //высчитываем значение для последнего значения
+                    BranchMatrix endParth;
+                    endParth.path = EndPath(cloneArr(withParth.matrix), true);
+                    endParth.matrix = cloneArr(withParth.matrix);
+                    endParth.objFunction = previous.objFunction + WithAPath(cloneArr(previous.matrix), withParth.path.start, withParth.path.end, out withParth.matrix) + 1;
+                    endParth.id = id + 1;
+                    id++;
+                    endParth.previosId = withParth.id;
+
+
+
+                    //добавляем последнее и предпоследнее значение
+                    Matrix.Add(withParth);
+                    Matrix.Add(endParth);
+
+                    break;
+                }
+                else
+                {
+                    withParth.objFunction = previous.objFunction + WithAPath(cloneArr(previous.matrix), withParth.path.start, withParth.path.end, out withParth.matrix);
+                }
+
+                //присвоение айди для веток с путем или без
+                notParth.previosId = previous.id;
+                notParth.id = id + 1;
+                id++;
+                withParth.previosId = previous.id;
+                withParth.id = id + 1;
+                id++;
+
+
+
+                //удляем тут ветку, от которой ветвились
+                minBranch.Remove(previous);
+                //добавляем ветки, которые получились после ветвления
+                minBranch.Add(withParth);
+                minBranch.Add(notParth);
+                Matrix.Add(withParth);
+                Matrix.Add(notParth);
+                //сортируем список матриц, от которых еще не ветвились по значению целевой функции
+                minBranch = minBranch.OrderBy(x => x.objFunction).ToList();
+
+                //присваиваем переменной предыдущего элемента - первому элементу отсортированного листа (с минимальной целевой функцией). 
+                previous.matrix = minBranch[0].matrix;
+                previous.objFunction = minBranch[0].objFunction;
+                previous.id = minBranch[0].id;
+                previous.previosId = minBranch[0].id - 1;
+                minBranch.Remove(minBranch[0]);
+
+            }
+
+            //составление листа ответов
+            int OptimZnach = Matrix.Last().objFunction;
+            int previousId = Matrix.Last().id;
+            List<(int s, int e)> otvet = new List<(int s, int e)>(); //лист кортежей (s - начало,e - конец)
+            int j = 0;
+            while (previousId > 0) //покеа предыдущий id не станет меньше 0
+            {
+                for (int i = 0; i < Matrix.Count(); i++) //перебираем значения из общей матрицы
+                {
+                    if (Matrix[i].id == previousId) //если значение id совпало с предыдущим - значит ветка найдена
+                    {
+                        if (Matrix[i].path.isNotBranch == true) //если включаем путь
+                        {
+                            previousId = Matrix[i].previosId;
+                            (int, int) temp = (Matrix[i].path.start, Matrix[i].path.end);
+                            otvet.Add(temp);
+                            break;
+                        }
+                        else
+                        {//если не включаем путь
+                            previousId = Matrix[i].previosId;
+                            break;
+                        }
+                    }
+                }
+            }
+            return OptimZnach;
+        }
+        /// <summary>
+        /// Это конец итерации?
+        /// </summary>
+        /// <param name="mas"></param>
+        /// <returns></returns>
+        static bool isEndIteration(int[,] mas)
+        {
+            if (mas.GetLength(0) == 2 || mas.GetLength(1) == 2)
+            {
+                return true;
+            }
+            else
+            {
+                return false;
+            }
+        }
+        static int Reduction(ref int[,] mas)
+        {
+            int min = M;
+            int sumMin = 0;
+            //редукция строк:
+            for (int i = 1; i < mas.GetLength(0); i++)
+            {
+                min = M;
+                for (int j = 1; j < mas.GetLength(1); j++)
+                {
+                    if (min > mas[i, j])
+                    {
+                        min = mas[i, j];
+                    }
+                }
+                for (int j = 1; j < mas.GetLength(1); j++)
+                {
+                    mas[i, j] -= min;
+                }
+                sumMin += min;
+            }
+
+            //редукция столбцов:
+            for (int j = 1; j < mas.GetLength(1); j++)
+            {
+                min = M;
+                for (int i = 1; i < mas.GetLength(0); i++)
+                {
+                    if (min > mas[i, j])
+                    {
+                        min = mas[i, j];
+                    }
+                }
+                for (int i = 1; i < mas.GetLength(0); i++)
+                {
+                    mas[i, j] -= min;
+                }
+                sumMin += min;
+            }
+
+            return sumMin;
+
+        }
+        /// <summary>
+        /// Высчитывает максимум из передаваемого массива
+        /// </summary>
+        /// <param name="mas"></param>
+        /// <param name="Max"></param>
+        /// <param name="isNotBranch"></param>
+        /// <returns></returns>
+        static  (int Start, int End, bool isNotBranch) MaxMas(int[,] mas, ref int Max, bool isNotBranch)
+        {
+            int s = 0, e = 0;
+            Max = int.MinValue;
+            for (int i = 1; i < mas.GetLength(0); i++)
+            {
+                for (int j = 1; j < mas.GetLength(1); j++)
+                {
+                    if (Max < mas[i, j])
+                    {
+                        Max = mas[i, j];
+                        s = mas[i, 0];
+                        e = mas[0, j];
+                    }
+                }
+            }
+
+            return (s, e, isNotBranch);
+        }
+        static  (int Start, int End, bool isNotBranch) EndPath(int[,] mas, bool isNotBranch)
+        {
+            return (mas[1, 0], mas[0, 1], isNotBranch);
+        }
+        /// <summary>
+        /// оценка нулевых ячеек
+        /// </summary>
+        /// <param name="mas"></param>
+        /// <param name="Start"></param>
+        /// <param name="End"></param>
+        /// <returns></returns>
+        static int[,] ZeroCellEvaluation(int[,] mas)
+        {
+            int red = Reduction(ref mas);
+            int[,] ZeroCell = new int[mas.GetLength(0), mas.GetLength(1)];
+
+            for (int i = 0; i < mas.GetLength(0); i++)
+            {
+                ZeroCell[i, 0] = mas[i, 0];
+            }
+            for (int j = 0; j < mas.GetLength(1); j++)
+            {
+                ZeroCell[0, j] = mas[0, j];
+            }
+
+            for (int i = 1; i < mas.GetLength(0); i++)
+            {
+                for (int j = 1; j < mas.GetLength(1); j++)
+                {
+                    if (mas[i, j] == 0)
+                    {
+
+                        int minRow = M;
+                        for (int a = 1; a < mas.GetLength(0); a++)
+                        { //i - минимум по строкам
+                            if ((minRow > mas[a, j]) && (a != i))
+                            {
+                                minRow = mas[a, j];
+                            }
+                        }
+                        int minCol = M;
+                        for (int b = 1; b < mas.GetLength(1); b++)
+                        {
+                            if (minCol > mas[i, b] && (b != j))
+                            {
+                                minCol = mas[i, b];
+                            }
+                        }
+                        ZeroCell[i, j] = minRow + minCol;
+                    }
+                }
+            }
+
+            return ZeroCell;
+        }
+        /// <summary>
+        /// При учитывании пути - вычеркиваем его и возвращаем редукцию от нового массива
+        /// </summary>
+        /// <param name="mas">массив</param>
+        /// <param name="pathStart">индекс начала функции</param>
+        /// <param name="pathEnd">индекс конца функции</param>
+        /// <param name="PreviousEl">Целевая функция предыдущего элемента</param>
+        /// <returns>значение целефой функции при условии включения пути в итог</returns>
+        static int WithAPath(int[,] mas, int pathStart, int pathEnd, out int[,] newMas)
+        {
+            newMas = new int[mas.GetLength(0) - 1, mas.GetLength(1) - 1];
+
+            //находим индексы начала и конца массива по элементу
+            int indS = 0, indE = 0;
+            for (int i = 0; i < mas.GetLength(0); i++)
+            {
+                if (mas[i, 0] == pathStart) { indS = i; }
+                if (mas[0, i] == pathEnd) { indE = i; }
+            }
+
+            //ставим М на обратном пути 
+            for (int i = 0; i < mas.GetLength(0); i++)
+            {
+                for (int j = 0; j < mas.GetLength(1); j++)
+                {
+                    if ((mas[i, 0] == pathEnd) && (mas[0, j] == pathStart))
+                    {
+                        mas[i, j] = M;
+                    }
+                }
+            }
+
+            //удаляем строку и столбец, на пересечении которых находится элемент выбранного маршрута
+            int newRow = 0, newCol = 0;
+            for (int i = 0; i < mas.GetLength(0); i++)
+            {
+                if (i == indS) { continue; }
+                for (int j = 0; j < mas.GetLength(1); j++)
+                {
+                    if (j == indE) { continue; }
+
+                    if (i > indS)
+                    {
+                        newRow = i - 1;
+                    }
+                    else
+                    {
+                        newRow = i;
+                    }
+                    if (j > indE)
+                    {
+                        newCol = j - 1;
+                    }
+                    else
+                    {
+                        newCol = j;
+                    }
+
+                    newMas[newRow, newCol] = mas[i, j];
+                }
+            }
+            //если получившаяся таблица 2х2 (учет того, что получившаяся матрица - последняя)
+            if (newMas.GetLength(0) == 2 && newMas.GetLength(1) == 2)
+            {
+                return -1;
+            }
+
+            return Reduction(ref newMas);
+        }
+        /// <summary>
+        /// Возвращает массив с М на месте найденного пути
+        /// </summary>
+        static int[,] WithoutAPath(int[,] mas, int pathStart, int pathEnd)
+        {
+            for (int i = 0; i < mas.GetLength(0); i++)
+            {
+                for (int j = 0; j < mas.GetLength(1); j++)
+                {
+                    if ((mas[i, 0] == pathStart) && (mas[0, j] == pathEnd))
+                    {
+                        mas[i, j] = M;
+                    }
+                }
+            }
+
+            return mas;
+        }
+        /// <summary>
+        /// Красивый вывод массива
+        /// </summary>
+        /// <param name="arr"></param>
+        static void InputMas(int[,] mas)
+        {
+            Mass = new int[mas.GetLength(0)+1, mas.GetLength(0)+1];
+            for (int i = 0; i < Mass.GetLength(0); i++)
+            {
+                Mass[i, 0] = i;
+            }
+            for (int j = 0; j < Mass.GetLength(1); j++)
+            {
+                Mass[0, j] = j;
+            }
+            for (int i = 0, k=1; i < mas.GetLength(0); i++, k++) {
+                for (int j = 0, n=1; j < mas.GetLength(1); j++,n++) {
+                    if (i == j)
+                    {
+                        Mass[k, n] = M;
+                    }
+                    else {
+                        Mass[k, n] = mas[i,j];
+                    }
+                }
+            }
+        }
+
+    }
+}

+ 25 - 1
MathModelingSimulator/ViewModels/CreateSimulatorViewModel.cs

@@ -95,7 +95,7 @@ namespace MathModelingSimulator.ViewModels
                 {
                     case "Ńčěďëĺęń ěĺňîä íŕ ěčíčěóě": GetMinSimplexMetod(); break;
                     case "Ńčěďëĺęń ěĺňîä íŕ ěŕęńčěóě": GetMaxSimplexMetod(); break;
-                    case "Çŕäŕ÷ŕ Ęîěěčâî˙ćĺđŕ": MessageRezult = "ß ďîęŕ íĺ óěĺţ ňŕęîĺ đĺřŕňü"; break;
+                    case "Çŕäŕ÷ŕ Ęîěěčâî˙ćĺđŕ": GetTravelingSalesmanProblem(); break;
                     case "Ňđŕíńďîđňíűĺ çŕäŕ÷č. Ěĺňîä ŕďďđîęńčěŕöčč Ôîăĺë˙": GetAnswerZadFogel(); break;
                     case "Çŕäŕ÷ŕ Äćîíńîíŕ": GetAnswerZadDzhonsons(); break;
                     case "Ŕëăîđčňě Äĺéęńňđű": MessageRezult = "ß ďîęŕ íĺ óěĺţ ňŕęîĺ đĺřŕňü"; break;
@@ -204,6 +204,30 @@ namespace MathModelingSimulator.ViewModels
                 MessageRezult = "Âŕřŕ ěŕňđčöŕ íĺ ďîäőîäčň äë˙ ýňîé çŕäŕ÷č";
                 Answer = "0";
             }
+        }
+		void GetTravelingSalesmanProblem() {
+            TravelingSalesmanProblem travelingSalesmanProblem = new TravelingSalesmanProblem();
+			if (CountRows == CountColumns)
+			{
+				var rezult = travelingSalesmanProblem.MainTravelingSalesmanProblem(_matrixBD);
+
+				if (rezult != null)
+				{
+					Answer = rezult.ToString();
+				}
+				else
+				{
+					IsVisibleRezult = true;
+					MessageRezult = "Âŕřŕ ěŕňđčöŕ íĺ ďîäőîäčň äë˙ ýňîé çŕäŕ÷č";
+					Answer = "0";
+				}
+			}
+			else {
+                IsVisibleRezult = true;
+                MessageRezult = "Âŕřŕ ěŕňđčöŕ íĺ ďîäőîäčň äë˙ ýňîé çŕäŕ÷č";
+                Answer = "0";
+            }
+            
         }
         public void CreateTask()
 		{