PotentialMethod.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace MathModTasks
  8. {
  9. struct Element
  10. {
  11. public int Delivery { get; set; }
  12. public int Value { get; set; }
  13. public static int FindMinElement(int a, int b)
  14. {
  15. if (a > b) return b;
  16. if (a == b) { return a; }
  17. else return a;
  18. }
  19. }
  20. /// <summary>
  21. /// Решение трансопортной задачи методом потенциалов с использованием распределения по мин. элементу
  22. /// </summary>
  23. class PotentialMethod
  24. {
  25. int n, m, summ;
  26. int[] whogive, whoget, minData, U, V;
  27. int[,] delta;
  28. bool optimSolution = false;
  29. bool[,] elemChecked;
  30. List<int[]> maxDelta = new List<int[]>();
  31. Element[,] mainData;
  32. MinDistrib md;
  33. /// <summary>
  34. /// Инициализирует новый экземпляр класса PotentialMethod с чтением исходных данных по указанному пути, задавая начальные значение внутренним переменным
  35. /// </summary>
  36. /// <param name="path">Путь к файлу с исходными данными</param>
  37. public PotentialMethod(string path)
  38. {
  39. List<string[]> newdata = ReadSaveData.ReadData(path);
  40. n = newdata.Count - 1;
  41. m = newdata.First().Length - 1;
  42. whogive = new int[n];
  43. whoget = new int[m];
  44. V = new int[n];
  45. U = new int[m];
  46. elemChecked = new bool[n, m];
  47. mainData = new Element[n, m];
  48. for (int i = 0; i < newdata.Count; i++)
  49. {
  50. for (int j = 0; j < newdata.First().Length; j++)
  51. {
  52. if (i != 0)
  53. {
  54. whogive[i - 1] = Convert.ToInt32(newdata[i][0]);
  55. if (j != 0)
  56. {
  57. whoget[j - 1] = Convert.ToInt32(newdata[0][j]);
  58. mainData[i - 1, j - 1].Value = Convert.ToInt32(newdata[i][j]);
  59. }
  60. }
  61. }
  62. }
  63. }
  64. /// <summary>
  65. /// Осуществляет проверку на вырожденность
  66. /// </summary>
  67. /// <param name="mg">Экземпляр класса MinDistrib, выполняющий распределение по мин. элементу</param>
  68. void CheckVir(MinDistrib mg)
  69. {
  70. if (mg.CountNotNullElement != m + n - 1)
  71. {
  72. FindMin();
  73. while (true)
  74. {
  75. if (mainData[minData[1], minData[2]].Delivery == 0)
  76. {
  77. mainData[minData[1], minData[2]].Delivery = -1;
  78. mg.CountNotNullElement++;
  79. break;
  80. }
  81. else FindMin();
  82. }
  83. }
  84. }
  85. /// <summary>
  86. /// Находит оптимальное решение транспортной задачи
  87. /// </summary>
  88. public void MainSolution()
  89. {
  90. //выполняется распределение по минимальному элементу
  91. md = new MinDistrib(mainData, whogive, whoget, m, n);
  92. //проверка на вырожденность, где добавляется фиктивная поставка
  93. while (md.MinDistribute(ref mainData) != m + n - 1)
  94. {
  95. CheckVir(md);
  96. }
  97. while (optimSolution == false)
  98. {
  99. // заполняет изначально массивы с потенциалами, чтобы потом перезаписать на нормальные
  100. V = new int[n];
  101. for (int i = 0; i < V.Length; i++)
  102. {
  103. V[i] = 99999;
  104. }
  105. U = new int[m];
  106. for (int i = 0; i < U.Length; i++)
  107. {
  108. U[i] = 99999;
  109. }
  110. //нахождение макс. затрат в заполненых ячейках, чтобы были хоть какие-то значения для подсчета потенциалов
  111. double MaxCost = 0;
  112. int maxV = 0;
  113. int maxU = 0;
  114. for (int i = 0; i < n; i++)
  115. {
  116. for (int j = 0; j < m; j++)
  117. {
  118. if (mainData[i, j].Delivery != 0 && mainData[i, j].Value > MaxCost)
  119. {
  120. MaxCost = mainData[i, j].Value;
  121. maxV = i;
  122. maxU = j;
  123. }
  124. }
  125. }
  126. //расчет потенциалов
  127. V[maxV] = 0;
  128. U[maxU] = mainData[maxV, maxU].Value - V[maxV];
  129. for (int sania = 0; sania < m; sania++) // саню не трогать!!!
  130. for (int i = 0; i < n; i++)
  131. for (int j = 0; j < m; j++)
  132. if (mainData[i, j].Delivery != 0 && (V[i] == 99999 || U[j] == 99999)) //проверяется, является ли поставка пустой и не записаны ли для этой ячейки нормальные потенциалы
  133. {
  134. if (V[i] == 99999 && U[j] == 99999) //если обе такие, считать нечего
  135. continue;
  136. if (V[i] != 99999)
  137. {
  138. for (int k = 0; k < m; k++)
  139. if (mainData[i, k].Delivery != 0)
  140. U[k] = mainData[i, k].Value - V[i];
  141. }
  142. if (U[j] != 99999)
  143. for (int k = 0; k < n; k++)
  144. if (mainData[k, j].Delivery != 0) V[k] = mainData[k, j].Value - U[j];
  145. }
  146. //рассчет дельты (V[i] + U[j] - C[i,j]) и занесения максимальной в соответсвующий список
  147. //список очищается для выполнения алгоритма в цикле
  148. maxDelta.Clear();
  149. maxDelta.Add(new int[] { 0, 0, 0 });
  150. delta = new int[n, m];
  151. for (int i = 0; i < n; i++)
  152. for (int j = 0; j < m; j++)
  153. if (mainData[i, j].Delivery == 0)
  154. {
  155. delta[i, j] = U[j] + V[i] - mainData[i, j].Value;
  156. if (delta[i, j] > maxDelta[0][0])
  157. {
  158. maxDelta.RemoveAt(0);
  159. maxDelta.Add(new int[] { delta[i, j], i, j });
  160. }
  161. }
  162. //проверка оптимальности метода
  163. //если есть хоть одна положительная дельта, то решение не оптимально
  164. for (int i = 0; i < n; i++)
  165. {
  166. for (int j = 0; j < m; j++)
  167. {
  168. if (delta[i, j] > 0) { optimSolution = false; break; }
  169. else optimSolution = true;
  170. }
  171. if (optimSolution == false) break;
  172. }
  173. if (optimSolution == false)
  174. {
  175. double MaxCostInRowWithDelta = 0;
  176. int MaxCostInRowWithDeltaI = 0, MaxCostInRowWithDeltaJ = 0;
  177. //находим ячейку с поставкой и максимальными затратами в строке с макс. дельта
  178. //maxDelta[0][1] - возвращает индекс строки максимальной дельты
  179. //maxDelta[0][2] - возвращает индекс столбца максимальной дельты
  180. for (int j = 0; j < m; j++)
  181. {
  182. if (mainData[maxDelta[0][1], j].Delivery != 0 && mainData[maxDelta[0][1], j].Value > MaxCostInRowWithDelta)
  183. {
  184. MaxCostInRowWithDelta = mainData[maxDelta[0][1], j].Value;
  185. MaxCostInRowWithDeltaI = maxDelta[0][1];
  186. MaxCostInRowWithDeltaJ = j;
  187. }
  188. }
  189. //находим ячейку с поставкой и максимальными затратами в столбце с макс. дельта
  190. double MaxCostInCOLUMNWithDelta = 0;
  191. int MaxCostInColumnWithDeltaI = 0, MaxCostInColumnWithDeltaJ = 0;
  192. for (int i = 0; i < n; i++)
  193. {
  194. if (mainData[i, maxDelta[0][2]].Delivery != 0 && mainData[i, maxDelta[0][2]].Value > MaxCostInCOLUMNWithDelta)
  195. {
  196. MaxCostInCOLUMNWithDelta = mainData[i, maxDelta[0][2]].Value;
  197. MaxCostInColumnWithDeltaI = i;
  198. MaxCostInColumnWithDeltaJ = maxDelta[0][2];
  199. }
  200. }
  201. //находим, сколько товара мы можем переместить
  202. // сравнивается ячейка с поставкой и макс. затратами в столбце и в строке с макс. дельта
  203. // если какая-то из них больше, он берет меньшую
  204. double MaxAmountWeCanAfford;
  205. if (mainData[MaxCostInColumnWithDeltaI, MaxCostInColumnWithDeltaJ].Delivery > mainData[MaxCostInRowWithDeltaI, MaxCostInRowWithDeltaJ].Delivery)
  206. {
  207. MaxAmountWeCanAfford = mainData[MaxCostInRowWithDeltaI, MaxCostInRowWithDeltaJ].Delivery;
  208. }
  209. else
  210. {
  211. MaxAmountWeCanAfford = mainData[MaxCostInColumnWithDeltaI, MaxCostInColumnWithDeltaJ].Delivery;
  212. }
  213. //перемещаем товар
  214. //эта ячейка - поставка с макс. затратами в строке с макс. дельта, из неё вычитают перемещенный поставку
  215. mainData[MaxCostInRowWithDeltaI, MaxCostInRowWithDeltaJ].Delivery -= (int)MaxAmountWeCanAfford;
  216. //эта ячейка - сама макс. дельта, в неё перемещают поставку
  217. mainData[maxDelta[0][1], maxDelta[0][2]].Delivery += (int)MaxAmountWeCanAfford;
  218. //эта ячейка - поставка с макс. затратами в столбце с макс. дельта, из неё вычитают перемещенный поставку
  219. mainData[MaxCostInColumnWithDeltaI, MaxCostInColumnWithDeltaJ].Delivery -= (int)MaxAmountWeCanAfford;
  220. //эта ячейка сод-ит индекс стр. из ячейки с макс. зат-ми в ст-бце с макс.д. и индекс ст-бца из ячейки с макс. зат-ми в стр-ке с макс.д.
  221. //её заполняют как противолежащую максимальной дельте, таким образом образуя прямоугольник или квадрат
  222. mainData[MaxCostInColumnWithDeltaI, MaxCostInRowWithDeltaJ].Delivery += (int)MaxAmountWeCanAfford;
  223. }
  224. //рассчет суммы, если решение оптимально
  225. else
  226. {
  227. for (int i = 0; i < n; i++)
  228. for (int j = 0; j < m; j++)
  229. summ += mainData[i, j].Delivery * mainData[i, j].Value;
  230. break;
  231. }
  232. }
  233. //подготовка результатов для записи в файл
  234. List<string[]> message = new List<string[]>();
  235. for (int i = 0; i < n; i++)
  236. {
  237. string[] row = new string[m];
  238. for (int j = 0; j < m; j++)
  239. row[j] = (mainData[i, j].Value + "/" + mainData[i, j].Delivery);
  240. message.Add(row);
  241. }
  242. message.Add(new string[] {"Оптимальная стоимость", summ.ToString() });
  243. ReadSaveData.WriteToFile("TransportSolution.csv", message);
  244. }
  245. /// <summary>
  246. /// Находит минимальный элемент в свойстве "затраты на перевозку" основной матрицы
  247. /// </summary>
  248. void FindMin()
  249. {
  250. int min = mainData[0, 0].Value;
  251. minData = new int[3];
  252. for (int i = 0; i < n; i++)
  253. for (int j = 0; j < m; j++)
  254. if (min > mainData[i, j].Value && mainData[i, j].Value != 0 && min != 0 && mainData[i, j].Delivery == 0 && elemChecked[i, j] == false)
  255. {
  256. min = mainData[i, j].Value;
  257. minData[0] = min;
  258. minData[1] = i;
  259. minData[2] = j;
  260. }
  261. }
  262. }
  263. }