dijkstra_graph 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/README.md +56 -10
- data/lib/dijkstra_graph/graph.rb +70 -31
- data/lib/dijkstra_graph/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d771e3085a0ffda14a30fc7fa92cf8e69208e984
|
4
|
+
data.tar.gz: 315da882ae3971d9c9a92e92755812074b5d4f3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b0ceec8d23b964237a72e57ddcf3697300dbf755910a5ba3a459e36a0860e7cdd5818a2f8eeb8cef34bd3185b45cd9ca0aad47c145d29c1bd761cbf01b096c9
|
7
|
+
data.tar.gz: 5917752b4b7fefdf63a149b6d70c2d4e7134b21de7621d0bfc49e8992e017eb23a1423f75ae47d6c3ee27021b487a3e76b79bfda3d866dc5fac4d92b7f9df0bc
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -32,25 +32,71 @@ get_edge_weight(source, destination)
|
|
32
32
|
get_adjacent_vertices(source)
|
33
33
|
|
34
34
|
# Use Dijkstra's algorithm to find the shortest distances
|
35
|
-
# from the
|
35
|
+
# from the source vertex to each of the other vertices
|
36
36
|
#
|
37
|
-
# Returns a hash of form { '
|
38
|
-
# where result[v] indicates the shortest distance from
|
39
|
-
shortest_distances(
|
37
|
+
# Returns a hash of form { 'source' => 0, 'a' => 3, 'b' => 4 },
|
38
|
+
# where result[v] indicates the shortest distance from source to v
|
39
|
+
shortest_distances(source)
|
40
40
|
|
41
41
|
# Use Dijkstra's algorithm to find the shortest paths
|
42
|
-
# from the
|
42
|
+
# from the source vertex to each of the other vertices
|
43
43
|
#
|
44
44
|
# Returns a hash of form { 'c' => ['a', 'b', 'c'] }, where
|
45
|
-
# result[v] indicates the shortest path from
|
46
|
-
shortest_paths(
|
45
|
+
# result[v] indicates the shortest path from source to v
|
46
|
+
shortest_paths(source)
|
47
|
+
|
48
|
+
# Use Dijkstra's algorithm to find the shortest paths
|
49
|
+
# from the source vertex to vertices within a given radius
|
50
|
+
#
|
51
|
+
# Returns a hash of form { 'c' => ['a', 'b', 'c'] }, where
|
52
|
+
# result[v] indicates the shortest path from source to v
|
53
|
+
shortest_paths_in_radius(source, radius)
|
47
54
|
|
48
55
|
# Use Dijkstra's algorithm to find the shortest path
|
49
|
-
# from the
|
56
|
+
# from the source vertex to the destination vertex
|
50
57
|
#
|
51
58
|
# Returns an array of vertices along the shortest path
|
52
59
|
# of form ['a', 'b', 'c'], or [] if no such path exists
|
53
|
-
shortest_path(
|
60
|
+
shortest_path(source, destination)
|
61
|
+
```
|
62
|
+
|
63
|
+
## Installation
|
64
|
+
|
65
|
+
Add this line to your application's Gemfile:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
gem 'dijkstra_graph'
|
69
|
+
```
|
70
|
+
|
71
|
+
Then you can require the gem in Ruby programs:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
require 'dijkstra_graph'
|
75
|
+
|
76
|
+
graph = DijkstraGraph::Graph.new
|
77
|
+
graph.add_undirected_edge('Burnaby', 'Vancouver', 10)
|
78
|
+
graph.add_edge('Burnaby', 'Port Coquitlam', 23)
|
79
|
+
graph.add_edge('Vancouver', 'Langley', 37)
|
80
|
+
graph.add_undirected_edge('Langley', 'Port Coquitlam', 35)
|
81
|
+
shortest_paths_from_vancouver = graph.shortest_paths('Vancouver')
|
82
|
+
|
83
|
+
# => { 'Burnaby' => ['Vancouver', 'Burnaby'],
|
84
|
+
# 'Langley' => ['Vancouver', 'Langley'],
|
85
|
+
# 'Port Coquitlam' => ['Vancouver', 'Burnaby', 'Port Coquitlam'] }
|
86
|
+
|
87
|
+
van_paths_within_35k = graph.shortest_paths_in_radius('Vancouver', 35)
|
88
|
+
|
89
|
+
# => { 'Burnaby' => ['Vancouver', 'Burnaby'],
|
90
|
+
# 'Port Coquitlam' => ['Vancouver', 'Burnaby', 'Port Coquitlam'] }
|
91
|
+
|
92
|
+
van_to_portco_path = graph.shortest_path('Vancouver', 'Port Coquitlam')
|
93
|
+
|
94
|
+
# => ['Vancouver', 'Burnaby', 'Port Coquitlam']
|
95
|
+
|
96
|
+
distances_from_vancouver = graph.shortest_distances('Vancouver')
|
97
|
+
|
98
|
+
# => { 'Vancouver' => 0, 'Burnaby' => 10,
|
99
|
+
# 'Langley' => 37, 'Port Coquitlam' => 33 }
|
54
100
|
```
|
55
101
|
|
56
102
|
## Development
|
@@ -63,4 +109,4 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/msayso
|
|
63
109
|
|
64
110
|
## License
|
65
111
|
|
66
|
-
The
|
112
|
+
The dijkstra_graph library is open source and available under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/lib/dijkstra_graph/graph.rb
CHANGED
@@ -13,7 +13,7 @@ module DijkstraGraph
|
|
13
13
|
# Vertices 'to-visit' are stored in a priority queue that
|
14
14
|
# uses a Fibonacci heap to give O(1) insert, amortized O(1)
|
15
15
|
# decrease_priority, and amortized O(log n) delete_min.
|
16
|
-
# Priority represents path distance from the
|
16
|
+
# Priority represents path distance from the source vertex.
|
17
17
|
#
|
18
18
|
# The shortest distances found so far to each vertex are
|
19
19
|
# stored in a simple hash which gives O(1) read/write.
|
@@ -24,13 +24,13 @@ module DijkstraGraph
|
|
24
24
|
end
|
25
25
|
|
26
26
|
# Use Dijkstra's algorithm to find the shortest distances
|
27
|
-
# from the
|
27
|
+
# from the source vertex to each of the other vertices
|
28
28
|
#
|
29
|
-
# Returns a hash of form { '
|
30
|
-
# where result[v] indicates the shortest distance from
|
31
|
-
def shortest_distances(
|
29
|
+
# Returns a hash of form { 'source' => 0, 'a' => 3, 'b' => 4 },
|
30
|
+
# where result[v] indicates the shortest distance from source to v
|
31
|
+
def shortest_distances(source)
|
32
32
|
distances = Hash.new { Float::INFINITY } # Initial distances = Inf
|
33
|
-
queue = initialize_queue(
|
33
|
+
queue = initialize_queue(source) # Begin at source node
|
34
34
|
until queue.empty?
|
35
35
|
v, distances[v] = queue.delete_min # Visit next closest vertex
|
36
36
|
update_distances_to_neighbours(v, distances, queue) # Update neighbours
|
@@ -39,69 +39,108 @@ module DijkstraGraph
|
|
39
39
|
end
|
40
40
|
|
41
41
|
# Use Dijkstra's algorithm to find the shortest paths
|
42
|
-
# from the
|
42
|
+
# from the source vertex to each of the other vertices
|
43
43
|
#
|
44
44
|
# Returns a hash of form { 'c' => ['a', 'b', 'c'] }, where
|
45
|
-
# result[v] indicates the shortest path from
|
46
|
-
def shortest_paths(
|
45
|
+
# result[v] indicates the shortest path from source to v
|
46
|
+
def shortest_paths(source)
|
47
47
|
predecessors = {} # Initialize vertex predecessors
|
48
48
|
distances = Hash.new { Float::INFINITY } # Initialize distances to Inf
|
49
|
-
queue = initialize_queue(
|
49
|
+
queue = initialize_queue(source) # Initialize queue with source
|
50
50
|
until queue.empty?
|
51
51
|
# Visit next closest vertex and update neighbours
|
52
52
|
v, distances[v] = queue.delete_min
|
53
|
-
update_paths_to_neighbours(v,
|
53
|
+
update_paths_to_neighbours(v, distances, queue, predecessors)
|
54
54
|
end
|
55
|
-
PathUtil.path_arrays(predecessors,
|
55
|
+
PathUtil.path_arrays(predecessors, source)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Use Dijkstra's algorithm to find the shortest paths
|
59
|
+
# from the source vertex to vertices within a given radius
|
60
|
+
#
|
61
|
+
# Returns a hash of form { 'c' => ['a', 'b', 'c'] }, where
|
62
|
+
# result[v] indicates the shortest path from source to v
|
63
|
+
def shortest_paths_in_radius(source, radius)
|
64
|
+
predecessors = {} # Initialize vertex predecessors
|
65
|
+
distances = Hash.new { Float::INFINITY } # Initialize distances to Inf
|
66
|
+
queue = initialize_queue(source) # Initialize queue with source
|
67
|
+
until queue.empty?
|
68
|
+
# Visit next closest vertex and update neighbours
|
69
|
+
v, distance = queue.delete_min
|
70
|
+
return PathUtil.path_arrays(predecessors, source) if distance > radius
|
71
|
+
distances[v] = distance
|
72
|
+
update_neighbours_in_radius(v, distances, queue, predecessors, radius)
|
73
|
+
end
|
74
|
+
PathUtil.path_arrays(predecessors, source)
|
56
75
|
end
|
57
76
|
|
58
77
|
# Use Dijkstra's algorithm to find the shortest path
|
59
|
-
# from the
|
78
|
+
# from the source vertex to the destination vertex
|
60
79
|
#
|
61
80
|
# Returns an array of vertices along the shortest path
|
62
81
|
# of form ['a', 'b', 'c'], or [] if no such path exists
|
63
|
-
def shortest_path(
|
82
|
+
def shortest_path(source, dest)
|
64
83
|
predecessors = {} # Initialize vertex predecessors
|
65
84
|
distances = Hash.new { Float::INFINITY } # Initialize distances to Inf
|
66
|
-
queue = initialize_queue(
|
85
|
+
queue = initialize_queue(source) # Initialize queue with source
|
67
86
|
until queue.empty?
|
68
87
|
v, distances[v] = queue.delete_min # Visit next closest node
|
69
|
-
return PathUtil.path_array(predecessors,
|
70
|
-
update_paths_to_neighbours(v,
|
88
|
+
return PathUtil.path_array(predecessors, source, dest) if v == dest
|
89
|
+
update_paths_to_neighbours(v, distances, queue, predecessors)
|
71
90
|
end
|
72
|
-
[] # No path found from
|
91
|
+
[] # No path found from source to dest
|
73
92
|
end
|
74
93
|
|
75
94
|
private
|
76
95
|
|
77
|
-
# Initialize priority queue with
|
78
|
-
def initialize_queue(
|
96
|
+
# Initialize priority queue with source vertex at distance = 0
|
97
|
+
def initialize_queue(source_vertex)
|
79
98
|
queue = PriorityQueue.new
|
80
|
-
queue[
|
99
|
+
queue[source_vertex] = 0
|
81
100
|
queue
|
82
101
|
end
|
83
102
|
|
84
103
|
# Update distances to neighbours of v and queue changed neighbours
|
85
104
|
def update_distances_to_neighbours(v, distances, queue)
|
86
|
-
|
87
|
-
|
88
|
-
distance_through_v = distance_v + get_edge_weight(v, w)
|
89
|
-
if distance_through_v < distances[w]
|
90
|
-
queue[w] = distances[w] = distance_through_v
|
91
|
-
end
|
92
|
-
end
|
105
|
+
update_neighbours(v, distances, method(:update_distance),
|
106
|
+
distances: distances, queue: queue)
|
93
107
|
end
|
94
108
|
|
95
109
|
# Update paths to neighbours of v and queue changed neighbours
|
96
|
-
def update_paths_to_neighbours(v,
|
110
|
+
def update_paths_to_neighbours(v, distances, queue, predecessors)
|
111
|
+
update_neighbours(v, distances, method(:update_path),
|
112
|
+
distances: distances, queue: queue,
|
113
|
+
predecessors: predecessors, predecessor: v)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Update paths to neighbours of v in radius and queue changed neighbours
|
117
|
+
def update_neighbours_in_radius(v, distances, queue, predecessors, radius)
|
118
|
+
update_neighbours(v, distances, method(:update_path),
|
119
|
+
distances: distances, queue: queue, radius: radius,
|
120
|
+
predecessors: predecessors, predecessor: v)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Apply given method to each neighbour we find a shorter path to
|
124
|
+
def update_neighbours(v, distances, update_method, update_params)
|
97
125
|
distance_v = distances[v]
|
98
126
|
get_adjacent_vertices(v).each do |w|
|
99
127
|
distance_through_v = distance_v + get_edge_weight(v, w)
|
100
128
|
if distance_through_v < distances[w]
|
101
|
-
|
102
|
-
predecessors[w] = v
|
129
|
+
update_method.call(w, distance_through_v, update_params)
|
103
130
|
end
|
104
131
|
end
|
105
132
|
end
|
133
|
+
|
134
|
+
# Update shortest distance to vertex
|
135
|
+
def update_distance(vertex, distance, params)
|
136
|
+
params[:queue][vertex] = params[:distances][vertex] = distance
|
137
|
+
end
|
138
|
+
|
139
|
+
# Update shortest path to vertex
|
140
|
+
def update_path(vertex, distance, params)
|
141
|
+
return if params[:radius] && distance > params[:radius]
|
142
|
+
update_distance(vertex, distance, params)
|
143
|
+
params[:predecessors][vertex] = params[:predecessor]
|
144
|
+
end
|
106
145
|
end
|
107
146
|
end
|