dijkstra_graph 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|