forked from tothmate/puzzles
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgreedy_salesman.js
183 lines (138 loc) · 4.86 KB
/
greedy_salesman.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/***
* Our baseline implementation of the TSP. As the name implies, we greedily select
* nodes to visit. There are far more efficient ways to win the TSP. Remember, the
* scoring algorithm is based on total distance travelled.
**/
function GreedySalesman() {
this.get_point = function(point_id) {
return this.points_by_id[point_id]
}
this.get_surrounding_points = function(point_id) {
return _.clone(this.connected_points_by_id[point_id])
}
this.get_dist = function(point1, point2) {
return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
}
this.init_graph = function(graph) {
// The graph, as given, isn't very friendly for processing. Let's extract
// points and arcs so we can do super-fast look ups
this.points_by_id = {}
this.connected_points_by_id = {}
this.graph = graph;
self = this;
_(graph.points).each(function(p) {
self.points_by_id[p.id] = p;
self.connected_points_by_id[p.id] = []
});
_(graph.arcs).each(function(a) {
self.connected_points_by_id[a[0]].push( self.get_point(a[1]) )
self.connected_points_by_id[a[1]].push( self.get_point(a[0]) )
});
}
this.compute_plan = function(graph, start_point_id) {
this.visited = {}
this.visited[start_point_id] = true;
this.init_graph(graph);
var self = this;
var start_point = this.get_point(start_point_id);
var last_point = start_point;
var closest_point;
var complete_path = [start_point]
// Greedily find the closest points...
while(closest_point = this.get_closest_unvisited_point(last_point)) {
path = this.get_path_to_point(last_point, closest_point);
_(path).each(function(pt) {
self.visited[pt.id] = true
})
last_point = closest_point;
complete_path = complete_path.concat(path)
}
// Go back to the start
path = this.get_path_to_point(last_point, start_point);
complete_path = complete_path.concat(path)
// We need make sure we just return the IDs
a = _(complete_path).map(function(p) {
return p.id
});
// Remove any sequential identicals..
final_ary = [a[0]]
for(i=1;i<a.length;i++) {
if (a[i] != a[i-1]) {
final_ary.push(a[i]);
}
}
return final_ary;
}
this.get_closest_unvisited_point = function(start_point) {
// Init
var self = this;
var closest_dist = 9999999;
var closest_point = null;
var processed = {}
var queue = this.get_surrounding_points(start_point.id);
var max_checks = 10;
var checks = 0;
// Breadth first search
while(queue.length > 0) {
var point = queue.shift();
if (processed[point.id]) continue;
if (!self.visited[point.id]) {
var this_dist = self.get_dist(start_point, point);
if (this_dist < closest_dist) {
closest_dist = this_dist;
closest_point = point;
if (checks > max_checks) break;
checks += 1;
}
}
processed[point.id] = true;
_(this.get_surrounding_points(point.id)).each(function(p) {
if (!processed[p.id]) queue.push(p);
})
}
return closest_point;
}
this.get_path_to_point = function(start_point, end_point) {
// Breadth First Search.
// The 'visit_queue' consists of the current point, and a 'breadcrumb' path back to the start point.
visit_queue = [[start_point, [start_point], 0]]
visited = {}
max_hits = 5;
hits = 0;
closest_path = null;
closest_dist = 10000000;
// We're going to BFS for the end_point. It's not gauranteed to be the shortest path.
// Is there a better way that is computationally fast enough?
while(visit_queue.length > 0) {
a = visit_queue.shift();
this_point = a[0];
this_path = a[1];
this_dist = a[2];
visited[this_point.id] = true
if (this_point.id == end_point.id) {
// We've arrived, return the breadcrumb path that took us here...
if (this_dist < closest_dist) {
closest_dist = this_dist
closest_path = this_path
}
hits += 1;
if (hits > max_hits) {
break;
}
} else {
// Otherwise, explore all the surrounding points...
new_points = self.get_surrounding_points(this_point.id)
_(new_points).each(function(p) {
if (!visited[p.id]) {
dist = self.get_dist(this_point, p)
visit_queue.push([p, this_path.concat(p), this_dist + dist])
}
});
}
}
// Otherwise, a path doesn't exist
if (closest_path == null)
throw "Could not compute path from start_point to end_point! " + start_point.id + " -> " + end_point.id;
return closest_path;
}
}