lib/dijkstra_graph/graph.rb in dijkstra_graph-0.1.0 vs lib/dijkstra_graph/graph.rb in dijkstra_graph-0.1.1
- old
+ new
@@ -11,97 +11,136 @@
# to the neighbours list of a given vertex.
#
# Vertices 'to-visit' are stored in a priority queue that
# uses a Fibonacci heap to give O(1) insert, amortized O(1)
# decrease_priority, and amortized O(log n) delete_min.
- # Priority represents path distance from the start vertex.
+ # Priority represents path distance from the source vertex.
#
# The shortest distances found so far to each vertex are
# stored in a simple hash which gives O(1) read/write.
class Graph < WeightedGraph::PositiveWeightedGraph
# Initialize graph edges
def initialize
super
end
# Use Dijkstra's algorithm to find the shortest distances
- # from the start vertex to each of the other vertices
+ # from the source vertex to each of the other vertices
#
- # Returns a hash of form { 'start' => 0, 'a' => 3, 'b' => 4 },
- # where result[v] indicates the shortest distance from start to v
- def shortest_distances(start)
+ # Returns a hash of form { 'source' => 0, 'a' => 3, 'b' => 4 },
+ # where result[v] indicates the shortest distance from source to v
+ def shortest_distances(source)
distances = Hash.new { Float::INFINITY } # Initial distances = Inf
- queue = initialize_queue(start) # Begin at start node
+ queue = initialize_queue(source) # Begin at source node
until queue.empty?
v, distances[v] = queue.delete_min # Visit next closest vertex
update_distances_to_neighbours(v, distances, queue) # Update neighbours
end
distances
end
# Use Dijkstra's algorithm to find the shortest paths
- # from the start vertex to each of the other vertices
+ # from the source vertex to each of the other vertices
#
# Returns a hash of form { 'c' => ['a', 'b', 'c'] }, where
- # result[v] indicates the shortest path from start to v
- def shortest_paths(start)
+ # result[v] indicates the shortest path from source to v
+ def shortest_paths(source)
predecessors = {} # Initialize vertex predecessors
distances = Hash.new { Float::INFINITY } # Initialize distances to Inf
- queue = initialize_queue(start) # Initialize queue with start
+ queue = initialize_queue(source) # Initialize queue with source
until queue.empty?
# Visit next closest vertex and update neighbours
v, distances[v] = queue.delete_min
- update_paths_to_neighbours(v, predecessors, distances, queue)
+ update_paths_to_neighbours(v, distances, queue, predecessors)
end
- PathUtil.path_arrays(predecessors, start)
+ PathUtil.path_arrays(predecessors, source)
end
+ # Use Dijkstra's algorithm to find the shortest paths
+ # from the source vertex to vertices within a given radius
+ #
+ # Returns a hash of form { 'c' => ['a', 'b', 'c'] }, where
+ # result[v] indicates the shortest path from source to v
+ def shortest_paths_in_radius(source, radius)
+ predecessors = {} # Initialize vertex predecessors
+ distances = Hash.new { Float::INFINITY } # Initialize distances to Inf
+ queue = initialize_queue(source) # Initialize queue with source
+ until queue.empty?
+ # Visit next closest vertex and update neighbours
+ v, distance = queue.delete_min
+ return PathUtil.path_arrays(predecessors, source) if distance > radius
+ distances[v] = distance
+ update_neighbours_in_radius(v, distances, queue, predecessors, radius)
+ end
+ PathUtil.path_arrays(predecessors, source)
+ end
+
# Use Dijkstra's algorithm to find the shortest path
- # from the start vertex to the destination vertex
+ # from the source vertex to the destination vertex
#
# Returns an array of vertices along the shortest path
# of form ['a', 'b', 'c'], or [] if no such path exists
- def shortest_path(start, dest)
+ def shortest_path(source, dest)
predecessors = {} # Initialize vertex predecessors
distances = Hash.new { Float::INFINITY } # Initialize distances to Inf
- queue = initialize_queue(start) # Initialize queue with start
+ queue = initialize_queue(source) # Initialize queue with source
until queue.empty?
v, distances[v] = queue.delete_min # Visit next closest node
- return PathUtil.path_array(predecessors, start, dest) if v == dest
- update_paths_to_neighbours(v, predecessors, distances, queue)
+ return PathUtil.path_array(predecessors, source, dest) if v == dest
+ update_paths_to_neighbours(v, distances, queue, predecessors)
end
- [] # No path found from start to dest
+ [] # No path found from source to dest
end
private
- # Initialize priority queue with start vertex at distance = 0
- def initialize_queue(start_vertex)
+ # Initialize priority queue with source vertex at distance = 0
+ def initialize_queue(source_vertex)
queue = PriorityQueue.new
- queue[start_vertex] = 0
+ queue[source_vertex] = 0
queue
end
# Update distances to neighbours of v and queue changed neighbours
def update_distances_to_neighbours(v, distances, queue)
- distance_v = distances[v]
- get_adjacent_vertices(v).each do |w|
- distance_through_v = distance_v + get_edge_weight(v, w)
- if distance_through_v < distances[w]
- queue[w] = distances[w] = distance_through_v
- end
- end
+ update_neighbours(v, distances, method(:update_distance),
+ distances: distances, queue: queue)
end
# Update paths to neighbours of v and queue changed neighbours
- def update_paths_to_neighbours(v, predecessors, distances, queue)
+ def update_paths_to_neighbours(v, distances, queue, predecessors)
+ update_neighbours(v, distances, method(:update_path),
+ distances: distances, queue: queue,
+ predecessors: predecessors, predecessor: v)
+ end
+
+ # Update paths to neighbours of v in radius and queue changed neighbours
+ def update_neighbours_in_radius(v, distances, queue, predecessors, radius)
+ update_neighbours(v, distances, method(:update_path),
+ distances: distances, queue: queue, radius: radius,
+ predecessors: predecessors, predecessor: v)
+ end
+
+ # Apply given method to each neighbour we find a shorter path to
+ def update_neighbours(v, distances, update_method, update_params)
distance_v = distances[v]
get_adjacent_vertices(v).each do |w|
distance_through_v = distance_v + get_edge_weight(v, w)
if distance_through_v < distances[w]
- queue[w] = distances[w] = distance_through_v
- predecessors[w] = v
+ update_method.call(w, distance_through_v, update_params)
end
end
+ end
+
+ # Update shortest distance to vertex
+ def update_distance(vertex, distance, params)
+ params[:queue][vertex] = params[:distances][vertex] = distance
+ end
+
+ # Update shortest path to vertex
+ def update_path(vertex, distance, params)
+ return if params[:radius] && distance > params[:radius]
+ update_distance(vertex, distance, params)
+ params[:predecessors][vertex] = params[:predecessor]
end
end
end