-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathsequential_salesman.js
135 lines (100 loc) · 3.55 KB
/
sequential_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
/**
* The 'sequential salesman' traverses all the points in the order they are given
* in the graph. Not efficient, but easy to implement.
*/
function SequentialSalesman() {
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) {
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) {
// Init
this.init_graph(graph);
var self = this;
var start_point = this.get_point(start_point_id);
var last_point = this.get_point(start_point_id)
var complete_path = [last_point];
// Just sequentially visit each point
_(graph.points).each(function(point) {
path = self.get_path_to_point(last_point, point);
complete_path = complete_path.concat(path)
last_point = point;
});
// 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]);
}
// Done
return final_ary;
}
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;
}
}