Advertisement
Tvor0zhok

k-opt

Jan 28th, 2024 (edited)
752
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 13.25 KB | None | 0 0
  1. package org.example;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.Collections;
  5. import java.util.List;
  6.  
  7. /**
  8.  * Класс KOptSolver для решения задачи коммивояжера
  9.  * алгоритмом k-opt
  10.  */
  11. public class KOptSolver extends TSPSolver {
  12.     /**
  13.      * Тип используемого алгоритма. Возможные значения:
  14.      * <p>
  15.      * - 2-opt (Алгоритм 2-оптимизации)
  16.      * <p>
  17.      * - 2.5-opt (Алгоритм 2.5-оптимизации)
  18.      * <p>
  19.      * - 3-opt (Алгоритм 3-оптимизации)
  20.      */
  21.     private final String type;
  22.  
  23.     /**
  24.      * Матрица смежности
  25.      */
  26.     private double[][] adjMat;
  27.  
  28.     /**
  29.      * Префикс длины решения solution <p>
  30.      * pref[i] = длина пути от 0 до i (0 -> 1 -> ... -> i), pref[0] = 0
  31.      */
  32.     private double[] pref;
  33.  
  34.     /**
  35.      * Суффикс длины обратного пути solution^R <p>
  36.      * suf[i] = длина обратного пути от 0 до i (0 -> (n - 1) -> ... -> i), suf[n] = 0
  37.      */
  38.     private double[] suf;
  39.  
  40.     /**
  41.      * Алгоритм 2-opt <p>
  42.      * Первоначальное решение: 0 A (i, i + 1) B (j, j + 1) C 0
  43.      * <p>
  44.      * A - путь от 0 до i <p>
  45.      * B - путь от (i + 1) до j <p>
  46.      * C - путь от (j + 1) до 0 <p>
  47.      * <p>
  48.      * Возможны 2 случая: <p>
  49.      * <p>
  50.      * 1) 0 A (i, j) B^R (i + 1, j + 1) C 0 <p>
  51.      * 2) 0 C^R (j + 1, i + 1) B (j, i) A^R 0 <p>
  52.      * <p>
  53.      * A^R - путь от i до 0 <p>
  54.      * B^R - путь от j до (i + 1) <p>
  55.      * C^R - путь от 0 до (j + 1)
  56.      */
  57.     private boolean twoOpt() {
  58.         for (int i = 0; i < nVertices - 2; ++i) {
  59.             for (int j = i + 2; j < nVertices; ++j) {
  60.                 // A = 0 -> 1 -> ... -> (i - 1) -> i
  61.                 double newLen1 = pref[i];
  62.  
  63.                 // A^R = i -> (i - 1) -> ... -> 1 -> 0
  64.                 double newLen2 = suf[0] - suf[i];
  65.  
  66.                 // i -> j
  67.                 newLen1 += adjMat[solution.get(i)][solution.get(j)];
  68.  
  69.                 // j -> i
  70.                 newLen2 += adjMat[solution.get(j)][solution.get(i)];
  71.  
  72.                 // B^R = j -> (j - 1) -> ... -> (i + 2) -> (i + 1)
  73.                 newLen1 += suf[i + 1] - suf[j];
  74.  
  75.                 // B = (i + 1) -> (i + 2) -> ... -> (j - 1) -> j
  76.                 newLen2 += pref[j] - pref[i + 1];
  77.  
  78.                 // (i + 1) -> (j + 1)
  79.                 newLen1 += adjMat[solution.get(i + 1)][solution.get(j + 1)];
  80.  
  81.                 // (j + 1) -> (i + 1)
  82.                 newLen2 += adjMat[solution.get(j + 1)][solution.get(i + 1)];
  83.  
  84.                 // C = (j + 1) -> (j + 2) -> ... -> (n - 1) -> 0
  85.                 newLen1 += pref[nVertices] - pref[j + 1];
  86.  
  87.                 // C^R = 0 -> (n - 1) -> ... -> (j + 2) -> (j + 1)
  88.                 newLen2 += suf[j + 1];
  89.                
  90.                 if (Math.min(newLen1, newLen2) < len) {
  91.                     Collections.reverse(solution.subList(i + 1, j + 1));
  92.                    
  93.                     if (newLen1 <= newLen2) {
  94.                         len = newLen1;
  95.                     } else {
  96.                         Collections.reverse(solution.subList(1, nVertices));
  97.                         len = newLen2;
  98.                     }
  99.                    
  100.                     return true;
  101.                 }
  102.             }
  103.         }
  104.        
  105.         return false;
  106.     }
  107.  
  108.     /**
  109.      * Алгоритм 2.5-opt <p>
  110.      * Первоначальное решение: 0 A (i, i + 1) (i + 1, i + 2) B (j, j + 1) C 0
  111.      * <p>
  112.      * A - путь от 0 до i <p>
  113.      * B - путь от (i + 2) до j <p>
  114.      * C - путь от (j + 1) до 0 <p>
  115.      * <p>
  116.      * Возможны 2 случая: <p>
  117.      * <p>
  118.      * 1) 0 A (i, i + 2) B (j, i + 1) (i + 1, j + 1) C 0 <p>
  119.      * 2) 0 C^R (j + 1, i + 1) (i + 1, j) B^R (i + 2, i) A^R 0 <p>
  120.      * <p>
  121.      * A^R - путь от i до 0 <p>
  122.      * B^R - путь от j до (i + 2) <p>
  123.      * C^R - путь от 0 до (j + 1)
  124.      */
  125.     private boolean twoHalfOpt() {
  126.         for (int i = 2; i < nVertices - 2; ++i) {
  127.             double newLen1 = pref[nVertices];
  128.             double newLen2 = suf[0];
  129.  
  130.             // 0 -> 1 -> ... -> i -> (i + 1) -> ... -> (n - 1) -> 0
  131.             newLen1 -= adjMat[solution.get(0)][solution.get(1)];
  132.             newLen1 -= adjMat[solution.get(nVertices - 1)][solution.get(0)];
  133.             newLen1 -= adjMat[solution.get(i)][solution.get(i + 1)];
  134.  
  135.             // 0 -> (i + 1) -> (i + 2) -> ... -> (n - 1) -> 1 -> 2 -> ... -> i -> 0
  136.             newLen1 += adjMat[solution.get(0)][solution.get(i + 1)];
  137.             newLen1 += adjMat[solution.get(nVertices - 1)][solution.get(1)];
  138.             newLen1 += adjMat[solution.get(i)][solution.get(0)];
  139.  
  140.             // 0 -> (n - 1) -> ... -> (i + 1) -> i -> ... -> 1 -> 0
  141.             newLen2 -= adjMat[solution.get(1)][solution.get(0)];
  142.             newLen2 -= adjMat[solution.get(0)][solution.get(nVertices - 1)];
  143.             newLen2 -= adjMat[solution.get(i + 1)][solution.get(i)];
  144.  
  145.             // 0 -> i -> ... -> 2 -> 1 -> (n - 1) -> ... -> (i + 2) -> (i + 1) -> 0
  146.             newLen2 += adjMat[solution.get(i + 1)][solution.get(0)];
  147.             newLen2 += adjMat[solution.get(1)][solution.get(nVertices - 1)];
  148.             newLen2 += adjMat[solution.get(0)][solution.get(i)];
  149.  
  150.             if (Math.min(newLen1, newLen2) < len) {
  151.                 Collections.rotate(solution.subList(1, nVertices), -i);
  152.  
  153.                 if (newLen1 <= newLen2) {
  154.                     len = newLen1;
  155.                 } else {
  156.                     Collections.reverse(solution.subList(1, nVertices));
  157.                     len = newLen2;
  158.                 }
  159.  
  160.                 return true;
  161.             }
  162.         }
  163.  
  164.         for (int i = 0; i < nVertices - 1; ++i) {
  165.             for (int h = 3; h < nVertices - 1; ++h) {
  166.                 int j = (i + h) % nVertices;
  167.  
  168.                 double newLen1 = pref[nVertices];
  169.                 double newLen2 = suf[0];
  170.  
  171.                 // 0 -> 1 -> ... -> i -> (i + 1) -> (i + 2) -> ... -> j -> (j + 1) -> ... -> (n - 1) -> 0
  172.                 newLen1 -= adjMat[solution.get(i)][solution.get(i + 1)];
  173.                 newLen1 -= adjMat[solution.get(i + 1)][solution.get(i + 2)];
  174.                 newLen1 -= adjMat[solution.get(j)][solution.get(j + 1)];
  175.  
  176.                 // 0 -> 1 -> ... -> i -> (i + 2) -> ... -> j -> (i + 1) -> (j + 1) -> ... -> (n - 1) -> 0
  177.                 newLen1 += adjMat[solution.get(i)][solution.get(i + 2)];
  178.                 newLen1 += adjMat[solution.get(j)][solution.get(i + 1)];
  179.                 newLen1 += adjMat[solution.get(i + 1)][solution.get(j + 1)];
  180.  
  181.                 // 0 -> (n - 1) -> ... -> (j + 1) -> j -> ... -> (i + 2) -> (i + 1) -> i -> ... -> 1 -> 0
  182.                 newLen2 -= adjMat[solution.get(i + 1)][solution.get(i)];
  183.                 newLen2 -= adjMat[solution.get(i + 2)][solution.get(i + 1)];
  184.                 newLen2 -= adjMat[solution.get(j + 1)][solution.get(j)];
  185.  
  186.                 // 0 -> (n - 1) -> ... -> (j + 1) -> (i + 1) -> j -> ... -> (i + 2) -> i -> ... -> 1 -> 0
  187.                 newLen2 += adjMat[solution.get(i + 2)][solution.get(i)];
  188.                 newLen2 += adjMat[solution.get(i + 1)][solution.get(j)];
  189.                 newLen2 += adjMat[solution.get(j + 1)][solution.get(i + 1)];
  190.  
  191.                 if (Math.min(newLen1, newLen2) < len) {
  192.                     int pos1 = Math.min(i + 1, j + 1);
  193.                     int pos2 = Math.max(j + 1, i + 2);
  194.                     int shift = (i < j) ? -1 : 1;
  195.  
  196.                     Collections.rotate(solution.subList(pos1, pos2), shift);
  197.  
  198.                     if (newLen1 <= newLen2) {
  199.                         len = newLen1;
  200.                     } else {
  201.                         Collections.reverse(solution.subList(1, nVertices));
  202.                         len = newLen2;
  203.                     }
  204.  
  205.                     return true;
  206.                 }
  207.             }
  208.         }
  209.  
  210.         return false;
  211.     }
  212.  
  213.     /**
  214.      * Алгоритм 3-opt (возможны 8 случаев)
  215.      */
  216.     private boolean threeOpt() {
  217.         for (int i = 0; i < nVertices - 4; ++i) {
  218.             for (int j = i + 2; j < nVertices - 2; ++j) {
  219.                 for (int k = j + 2; k < nVertices - ((i == 0) ? 1 : 0); ++k) {
  220.                     // массив, задающий все случаи
  221.                     int[][] routePositions = {
  222.                             {0, i, j, i + 1, k, j + 1, k + 1, nVertices},
  223.                             {0, i, j + 1, k, i + 1, j, k + 1, nVertices},
  224.                             {0, i, k, j + 1, i + 1, j, k + 1, nVertices},
  225.                             {0, i, j + 1, k, j, i + 1, k + 1, nVertices}
  226.                     };
  227.  
  228.                     for (int l = 0; l < 4; ++l) {
  229.                         double newLen1 = 0;
  230.                         double newLen2 = 0;
  231.  
  232.                         // подсчет длины первого пути
  233.                         for (int m = 0; m < 7; ++m) {
  234.                             int u = routePositions[l][m];
  235.                             int v = routePositions[l][m + 1];
  236.  
  237.                             if (m % 2 == 0) {
  238.                                 newLen1 += (u < v) ? pref[v] - pref[u] : suf[v] - suf[u];
  239.                             } else {
  240.                                 newLen1 += adjMat[solution.get(u)][solution.get(v)];
  241.                             }
  242.                         }
  243.  
  244.                         // подсчет длины второго пути
  245.                         for (int m = 7; m > 0; --m) {
  246.                             int u = routePositions[l][m];
  247.                             int v = routePositions[l][m - 1];
  248.  
  249.                             if (m % 2 == 1) {
  250.                                 newLen2 += (u < v) ? pref[v] - pref[u] : suf[v] - suf[u];
  251.                             } else {
  252.                                 newLen2 += adjMat[solution.get(u)][solution.get(v)];
  253.                             }
  254.                         }
  255.  
  256.                         if (Math.min(newLen1, newLen2) < len) {
  257.                             if (l % 2 == 0) {
  258.                                 Collections.reverse(solution.subList(j + 1, k + 1));
  259.                             }
  260.  
  261.                             if (l % 3 == 0) {
  262.                                 Collections.reverse(solution.subList(i + 1, j + 1));
  263.                             }
  264.  
  265.                             if (l != 0) {
  266.                                 Collections.rotate(solution.subList(i + 1, k + 1), i - j);
  267.                             }
  268.  
  269.                             if (newLen1 <= newLen2) {
  270.                                 len = newLen1;
  271.                             } else {
  272.                                 Collections.reverse(solution.subList(1, nVertices));
  273.                                 len = newLen2;
  274.                             }
  275.  
  276.                             return true;
  277.                         }
  278.                     }
  279.                 }
  280.             }
  281.         }
  282.  
  283.         /*
  284.         for (int i = 0; i < nVertices - 2; ++i)
  285.             for (int j = i + 1; j < nVertices - 1; ++j)
  286.                 for (int k = j + 1; k < nVertices; ++k)
  287.                 {
  288.                     double newLen = len;
  289.  
  290.                     // ... -> x -> (x + 1) -> ...
  291.  
  292.                     newLen -= adjMat[solution.get(i)][solution.get(i + 1)];
  293.                     newLen -= adjMat[solution.get(j)][solution.get(j + 1)];
  294.                     newLen -= adjMat[solution.get(k)][solution.get(k + 1)];
  295.  
  296.                     // i -> j + 1 -> ... -> k -> i + 1 -> ... -> j -> k + 1 -> ...
  297.                     newLen += adjMat[solution.get(i)][solution.get(j + 1)];
  298.                     newLen += adjMat[solution.get(k)][solution.get(i + 1)];
  299.                     newLen += adjMat[solution.get(j)][solution.get(k + 1)];
  300.  
  301.                     if (newLen < len)
  302.                     {
  303.                         Collections.rotate(solution.subList(i + 1, k + 1), i - j);
  304.                         len = newLen;
  305.  
  306.                         return true;
  307.                     }
  308.                 }*/
  309.  
  310.         return false;
  311.     }
  312.  
  313.     public KOptSolver(String type, List<Integer> solution, double len) {
  314.         this.type = type;
  315.  
  316.         this.solution = new ArrayList<>(solution);
  317.         this.len = len;
  318.     }
  319.  
  320.     @Override
  321.     public void solve(double[][] adjMat) {
  322.         this.nVertices = adjMat.length;
  323.         this.adjMat = adjMat;
  324.        
  325.         boolean isImproved = true;
  326.  
  327.         this.pref = new double[nVertices + 1];
  328.         this.suf = new double[nVertices + 1];
  329.  
  330.         while(isImproved) {
  331.             for (int i = 1; i <= nVertices; ++i) {
  332.                 pref[i] = pref[i - 1] + adjMat[solution.get(i - 1)][solution.get(i)];
  333.             }
  334.  
  335.             for (int i = nVertices - 1; i >= 0; --i) {
  336.                 suf[i] = suf[i + 1] + adjMat[solution.get(i + 1)][solution.get(i)];
  337.             }
  338.  
  339.             switch (type) {
  340.                 case "2-opt" -> isImproved = twoOpt();
  341.                 case "2.5-opt" -> isImproved = twoHalfOpt() || twoOpt();
  342.                 case "3-opt" -> isImproved = threeOpt(); // || twoHalfOpt() || twoOpt();
  343.             }
  344.         }
  345.     }
  346. }
  347.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement