Tuesday, March 21, 2017

3-opt with DLB

Procedures to calculate the gain from 3-opt move and to make 3-opt move assume that their parameters (cities and their indices in tour) are given in the order of ascending indices of tour array (modulo N). Therefore we need a function to recognize whether three indices are in this order, or: whether in this orientation the middle index is between the two boundary indices.

proc Between(a, x, b: Tour_Index): bool =
  ## Returns true if `x` is between `a` and `b` in cyclic sequence
  ## with established orientation of nondescending indices
  # That is: when one begins a forward traversal of the tour
  # at position `a', then position `x` is reached before position `b'.
  # Returns true if and only if:
  #   a < x < b  or  b < a < x  or  x < b < a
  if (b > a):
    result = (x > a) and (x < b)
  elif (b < a):
    result = (x > a) or (x < b)
  else:
    result = false

Note the additional speed-up: the link between base city X1 and its tour neighbor X2 is not considered when at least one of these cities has its don’t-look bit set to on.

proc One_City_3_Opt_DLB(tour: var Tour_Array,
                        basePos: Tour_Index,
                        DontLook: var DLB_Array): bool =
  type
    Move3 = object
      i, j, k: Tour_Index
      optCase: Reconnection_3_optCase
  var
    improved: bool
    i, j, k: Tour_Index
    X1, X2, Y1, Y2, Z1, Z2: City_Number
    gainExpected: Length_Gain
    optCase: Reconnection_3_optCase
    goodMove: Move3

  improved = false
  block three_loops:
    for direction in [forward, backward]:
      if direction == forward:
        i  = basePos
      else:
        i  = (N + basePos - 1) mod N
      X1 = tour[i]
      X2 = tour[(i+1) mod N]

      if isDLB_on(DontLook,X1) or isDLB_on(DontLook,X2):  # NOTE
        continue

      for counter_2 in 0 .. N-1:
        j = counter_2 # second cut after j
        Y1 = tour[j]
        Y2 = tour[(j+1) mod N]

        if (X1 == Y1) or (X2 == Y1) or (Y2 == X1):
          continue

        # examine 2-opt move (see: opt3_case_1 .. opt3_case_3)
        gainExpected = Gain_From_2_Opt(X1, X2, Y1, Y2)
        if gainExpected > 0:
          goodMove = Move3(i: i,  j: j,  k: j,
                           optCase: opt3_case_1)
          improved = true
          break three_loops

        for counter_3 in 0 .. N-1:
          k = counter_3  # third cut after k
          Z1 = tour[k]
          Z2 = tour[(k+1) mod N]
          if (X1 == Z1) or (Y1 == Z1):
            continue

          if not Between(i, j, k):
            continue

          # examine pure 3-opt cases
          for optCase in [opt3_case_6, opt3_case_7]:
            gainExpected = Gain_From_3_Opt(X1, X2, Y1, Y2, Z1, Z2,
                                           optCase)
            if gainExpected > 0:
              goodMove = Move3(i: i,  j: j,  k: k,
                               optCase: optCase)
              improved = true
              break three_loops
      # end_block three_loops
  if improved:
    Set_DLB_off(DontLook, [X1, X2, Y1, Y2])
    if goodMove.optCase > opt3_case_3:
      Set_DLB_off(DontLook, [Z1, Z2])
    Make_3_Opt_Move(tour,
                    goodMove.i,  goodMove.j,  goodMove.k,
                    goodMove.optCase)
  result = improved
proc LS_3_Opt_DLB(tour: var Tour_Array) =
  ## Optimizes the given tour using 3-opt with Don't Look Bits (DLB)
  var
    locallyOptimal: bool = false
    improved: bool
    DontLook: DLB_Array
    baseCity: City_Number

  Set_DLB_off(DontLook, tour)

  while not locallyOptimal:
    locallyOptimal = true
    for basePos in 0 .. N-1:
      baseCity = tour[basePos]
      if isDLB_on(DontLook, baseCity):
        continue
      improved = One_City_3_Opt_DLB(tour, basePos, DontLook)
      if not improved:
        Set_DLB_on(DontLook, baseCity)
      else:
        locallyOptimal = false

Some simple statistics

Relative time, best length, average length and worst length for all tours created by NN heuristic and improved by 3-opt and 3-opt with DLB algorithms. Random planar problems, N=100.
Problem # Method Time Best Avg Worst
1NN100100.00107.94117.16
NN + 2opt126890.0692.88101.40
NN + 3opt7509388.6189.9291.77
NN + 3opt-DLB2812588.6190.0792.72
2NN100100.00109.67122.62
NN + 2opt114688.9194.14101.71
NN + 3opt7364688.8990.4193.56
NN + 3opt-DLB2791988.9990.8594.98
3NN100100.00107.04118.01
NN + 2opt94091.1194.7498.25
NN + 3opt7292088.7791.1993.11
NN + 3opt-DLB2593388.8691.5393.85
4NN100100.00107.40118.32
NN + 2opt97589.0392.5697.91
NN + 3opt7285687.9989.5692.33
NN + 3opt-DLB2568187.9989.8093.41
5NN100100.00104.79113.38
NN + 2opt146081.1984.6089.37
NN + 3opt7270679.8281.7684.73
NN + 3opt-DLB2916679.8281.9885.00
6NN100100.00104.66115.85
NN + 2opt136082.9886.0390.66
NN + 3opt6853982.3983.5985.12
NN + 3opt-DLB2697982.3983.5985.56
7NN100100.00109.45118.42
NN + 2opt136088.1092.8897.84
NN + 3opt7863986.6688.8391.93
NN + 3opt-DLB2802686.6689.1492.78
8NN100100.00104.37112.02
NN + 2opt104688.5091.5997.77
NN + 3opt7531386.4088.3491.11
NN + 3opt-DLB2604086.7588.9295.75
9NN100100.00108.32118.64
NN + 2opt156691.3194.95100.77
NN + 3opt6646090.4692.0595.71
NN + 3opt-DLB2572690.5992.1295.83
10NN100100.00107.87122.61
NN + 2opt114689.1293.2199.11
NN + 3opt6906687.9889.4393.58
NN + 3opt-DLB2666687.9889.6792.78

No comments:

Post a Comment