Created
August 26, 2016 04:07
-
-
Save KPCCoiL/15e3ac792ac0c9fdd92848010b383218 to your computer and use it in GitHub Desktop.
2-opt for TSP in Go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package tsp | |
import ( | |
"math" | |
"math/rand" | |
"time" | |
) | |
type Point struct { | |
X, Y float64 | |
} | |
func distance(p, q Point) float64 { | |
dx := p.X - q.X | |
dy := p.Y - q.Y | |
return math.Hypot(dx, dy) | |
} | |
func shuffle(arr []int) { | |
n := len(arr) | |
for i := 0; i < n; i++ { | |
j := rand.Uint32() % uint32(n); | |
arr[i], arr[j] = arr[j], arr[i] | |
} | |
} | |
type node struct { | |
u, v int | |
} | |
func initializeTour(tour []node) { | |
n := len(tour) | |
order := make([]int, n) | |
for i := 0; i < n; i++ { | |
order[i] = i; | |
} | |
shuffle(order) | |
for i := 0; i < n; i++ { | |
prev := order[(i + n - 1) % n] | |
next := order[(i + 1) % n] | |
tour[order[i]] = node{ prev, next } | |
} | |
} | |
func nextVertex(tour []node, prev, now int) int { | |
candidates := tour[now] | |
if candidates.u == prev { | |
return candidates.v | |
} else { | |
return candidates.u | |
} | |
} | |
func MeasureLength(field []Point, tour []node) float64 { | |
var result float64 = 0 | |
for i, edges := range tour { | |
result += distance(field[i], field[edges.u]) | |
result += distance(field[i], field[edges.v]) | |
} | |
return result / 2.0; | |
} | |
func flip(target node, remove, next int) node { | |
if target.u == remove { | |
return node{next, target.v} | |
} else { | |
return node{next, target.u} | |
} | |
} | |
func TwoOpt(field []Point) (float64, []int) { | |
random.Seed(time.Nano().UnixNano()) | |
n := len(field) | |
tour := make([]node, n) | |
initializeTour(tour) | |
updated := true | |
for updated { | |
updated = false | |
for i := 0; i < n && !updated; i++ { | |
first := tour[i].u | |
second := tour[i].v | |
// attempts to swap (i-first) and (v-edgeTo) | |
for j := 0; j < 2 && !updated; j++ { | |
start := nextVertex(tour, i, first) | |
lastV := first | |
var edgeTo int | |
for v := start; v != second; v = edgeTo { | |
edgeTo = nextVertex(tour, lastV, v) | |
current := distance(field[i], field[first]) + | |
distance(field[v], field[edgeTo]) | |
newD := distance(field[i], field[v]) + | |
distance(field[first], field[edgeTo]) | |
if newD < current { | |
updated = true | |
ar := [4]int{ i, first, v, edgeTo } | |
var save [4]node | |
for k, u := range ar { | |
save[k] = tour[u] | |
} | |
for k, u := range ar { | |
tour[u] = flip(save[k], ar[(5 - k) % 4], ar[(k + 2) % 4]) | |
} | |
break | |
} else { | |
lastV = v | |
} | |
} | |
first, second = second, first | |
} | |
} | |
} | |
prev := -1 | |
v := 0 | |
var route []int | |
for i := 0; i < n; i++ { | |
route = append(route, v) | |
prev, v = v, nextVertex(tour, prev, v) | |
} | |
return MeasureLength(field, tour), route | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment