Skip to content

Instantly share code, notes, and snippets.

@KPCCoiL
Created August 26, 2016 04:07
Show Gist options
  • Save KPCCoiL/15e3ac792ac0c9fdd92848010b383218 to your computer and use it in GitHub Desktop.
Save KPCCoiL/15e3ac792ac0c9fdd92848010b383218 to your computer and use it in GitHub Desktop.
2-opt for TSP in Go
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