gratr19 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README +335 -0
- data/examples/graph_self.rb +54 -0
- data/examples/module_graph.jpg +0 -0
- data/examples/module_graph.rb +12 -0
- data/examples/self_graph.jpg +0 -0
- data/examples/visualize.jpg +0 -0
- data/examples/visualize.rb +8 -0
- data/install.rb +49 -0
- data/lib/gratr.rb +42 -0
- data/lib/gratr/adjacency_graph.rb +230 -0
- data/lib/gratr/base.rb +34 -0
- data/lib/gratr/biconnected.rb +116 -0
- data/lib/gratr/chinese_postman.rb +123 -0
- data/lib/gratr/common.rb +74 -0
- data/lib/gratr/comparability.rb +92 -0
- data/lib/gratr/digraph.rb +115 -0
- data/lib/gratr/digraph_distance.rb +185 -0
- data/lib/gratr/dot.rb +90 -0
- data/lib/gratr/edge.rb +145 -0
- data/lib/gratr/graph.rb +314 -0
- data/lib/gratr/graph_api.rb +82 -0
- data/lib/gratr/import.rb +44 -0
- data/lib/gratr/labels.rb +103 -0
- data/lib/gratr/maximum_flow.rb +107 -0
- data/lib/gratr/rdot.rb +332 -0
- data/lib/gratr/search.rb +422 -0
- data/lib/gratr/strong_components.rb +127 -0
- data/lib/gratr/undirected_graph.rb +153 -0
- data/lib/gratr/version.rb +6 -0
- data/lib/priority-queue/benchmark/dijkstra.rb +171 -0
- data/lib/priority-queue/compare_comments.rb +49 -0
- data/lib/priority-queue/ext/priority_queue/CPriorityQueue/extconf.rb +2 -0
- data/lib/priority-queue/lib/priority_queue.rb +14 -0
- data/lib/priority-queue/lib/priority_queue/c_priority_queue.rb +1 -0
- data/lib/priority-queue/lib/priority_queue/poor_priority_queue.rb +46 -0
- data/lib/priority-queue/lib/priority_queue/ruby_priority_queue.rb +525 -0
- data/lib/priority-queue/setup.rb +1551 -0
- data/lib/priority-queue/test/priority_queue_test.rb +371 -0
- data/tests/TestBiconnected.rb +53 -0
- data/tests/TestChinesePostman.rb +53 -0
- data/tests/TestComplement.rb +54 -0
- data/tests/TestDigraph.rb +333 -0
- data/tests/TestDigraphDistance.rb +138 -0
- data/tests/TestDot.rb +75 -0
- data/tests/TestEdge.rb +171 -0
- data/tests/TestInspection.rb +57 -0
- data/tests/TestMultiEdge.rb +57 -0
- data/tests/TestNeighborhood.rb +64 -0
- data/tests/TestProperties.rb +160 -0
- data/tests/TestSearch.rb +277 -0
- data/tests/TestStrongComponents.rb +85 -0
- data/tests/TestTriagulated.rb +137 -0
- data/tests/TestUndirectedGraph.rb +219 -0
- metadata +152 -0
data/lib/gratr.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 Shawn Patrick Garbett
|
3
|
+
# Copyright (c) 2002,2004,2005 by Horst Duchene
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
6
|
+
# are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice(s),
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of the Shawn Garbett nor the names of its contributors
|
14
|
+
# may be used to endorse or promote products derived from this software
|
15
|
+
# without specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
18
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
19
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
20
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
21
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
23
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
24
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
25
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
26
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
#++
|
28
|
+
|
29
|
+
|
30
|
+
require 'gratr/base'
|
31
|
+
require 'gratr/digraph'
|
32
|
+
require 'gratr/undirected_graph'
|
33
|
+
require 'gratr/common'
|
34
|
+
|
35
|
+
# Load priority queue classes
|
36
|
+
begin
|
37
|
+
# Use installed Gem
|
38
|
+
require 'priority_queue'
|
39
|
+
rescue LoadError # Use local copy
|
40
|
+
require 'priority-queue/lib/priority_queue/ruby_priority_queue'
|
41
|
+
PriorityQueue = RubyPriorityQueue
|
42
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 Shawn Patrick Garbett
|
3
|
+
# Copyright (c) 2002,2004,2005 by Horst Duchene
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
6
|
+
# are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice(s),
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of the Shawn Garbett nor the names of its contributors
|
14
|
+
# may be used to endorse or promote products derived from this software
|
15
|
+
# without specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
18
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
19
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
20
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
21
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
23
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
24
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
25
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
26
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
#++
|
28
|
+
|
29
|
+
|
30
|
+
require 'gratr/edge'
|
31
|
+
require 'gratr/graph'
|
32
|
+
require 'set'
|
33
|
+
|
34
|
+
module GRATR
|
35
|
+
|
36
|
+
# This provides the basic routines needed to implement the Digraph, UndirectedGraph,
|
37
|
+
# PseudoGraph, DirectedPseudoGraph, MultiGraph and DirectedPseudoGraph class.
|
38
|
+
module AdjacencyGraph
|
39
|
+
|
40
|
+
include Graph
|
41
|
+
|
42
|
+
class ArrayWithAdd < Array # :nodoc:
|
43
|
+
alias add push
|
44
|
+
end
|
45
|
+
|
46
|
+
# Initialization parameters can include an Array of edges to add, Graphs to
|
47
|
+
# copy (will merge if multiple)
|
48
|
+
# :parallel_edges denotes that duplicate edges are allowed
|
49
|
+
# :loops denotes that loops are allowed
|
50
|
+
def initialize(*params)
|
51
|
+
@vertex_dict = Hash.new
|
52
|
+
raise ArgumentError if params.any? do |p|
|
53
|
+
!(p.kind_of? GRATR::Graph or
|
54
|
+
p.kind_of? Array or
|
55
|
+
p == :parallel_edges or
|
56
|
+
p == :loops)
|
57
|
+
end
|
58
|
+
clear_all_labels
|
59
|
+
|
60
|
+
# Basic configuration of adjacency
|
61
|
+
@allow_loops = params.any? {|p| p == :loops}
|
62
|
+
@parallel_edges = params.any? {|p| p == :parallel_edges}
|
63
|
+
@edgelist_class = @parallel_edges ? ArrayWithAdd : Set
|
64
|
+
if @parallel_edges
|
65
|
+
@edge_number = Hash.new
|
66
|
+
@next_edge_number = 0
|
67
|
+
end
|
68
|
+
|
69
|
+
# Copy any given graph into this graph
|
70
|
+
params.select {|p| p.kind_of? GRATR::Graph}.each do |g|
|
71
|
+
g.edges.each do |e|
|
72
|
+
add_edge!(e)
|
73
|
+
edge_label_set(e, edge_label(e)) if edge_label(e)
|
74
|
+
end
|
75
|
+
g.vertices.each do |v|
|
76
|
+
vertex_label_set(v, vertex_label(v)) if vertex_label(v)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Add all array edges specified
|
81
|
+
params.select {|p| p.kind_of? Array}.each do |a|
|
82
|
+
0.step(a.size-1, 2) {|i| add_edge!(a[i], a[i+1])}
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
# Returns true if v is a vertex of this Graph
|
88
|
+
# An O(1) implementation of vertex?
|
89
|
+
def vertex?(v) @vertex_dict.has_key?(v); end
|
90
|
+
|
91
|
+
# Returns true if [u,v] or u is an Arc
|
92
|
+
# An O(1) implementation
|
93
|
+
def edge?(u, v=nil)
|
94
|
+
u, v = u.source, u.target if u.kind_of? GRATR::Arc
|
95
|
+
vertex?(u) and @vertex_dict[u].include?(v)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Adds a vertex to the graph with an optional label
|
99
|
+
def add_vertex!(vertex, label=nil)
|
100
|
+
@vertex_dict[vertex] ||= @edgelist_class.new
|
101
|
+
self[vertex] = label if label
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
# Adds an edge to the graph
|
106
|
+
# Can be called in two basic ways, label is optional
|
107
|
+
# * add_edge!(Arc[source,target], "Label")
|
108
|
+
# * add_edge!(source,target, "Label")
|
109
|
+
def add_edge!(u, v=nil, l=nil, n=nil)
|
110
|
+
n = u.number if u.class.include? ArcNumber and n.nil?
|
111
|
+
u, v, l = u.source, u.target, u.label if u.kind_of? GRATR::Arc
|
112
|
+
return self if not @allow_loops and u == v
|
113
|
+
n = (@next_edge_number+=1) unless n if @parallel_edges
|
114
|
+
add_vertex!(u); add_vertex!(v)
|
115
|
+
@vertex_dict[u].add(v)
|
116
|
+
(@edge_number[u] ||= @edgelist_class.new).add(n) if @parallel_edges
|
117
|
+
unless directed?
|
118
|
+
@vertex_dict[v].add(u)
|
119
|
+
(@edge_number[v] ||= @edgelist_class.new).add(n) if @parallel_edges
|
120
|
+
end
|
121
|
+
self[n ? edge_class[u,v,n] : edge_class[u,v]] = l if l
|
122
|
+
self
|
123
|
+
end
|
124
|
+
|
125
|
+
# Removes a given vertex from the graph
|
126
|
+
def remove_vertex!(v)
|
127
|
+
# FIXME This is broken for multi graphs
|
128
|
+
@vertex_dict.delete(v)
|
129
|
+
@vertex_dict.each_value { |adjList| adjList.delete(v) }
|
130
|
+
@vertex_dict.keys.each do |u|
|
131
|
+
delete_label(edge_class[u,v])
|
132
|
+
delete_label(edge_class[v,u])
|
133
|
+
end
|
134
|
+
delete_label(v)
|
135
|
+
self
|
136
|
+
end
|
137
|
+
|
138
|
+
# Removes an edge from the graph, can be called with source and target or with
|
139
|
+
# and object of GRATR::Arc derivation
|
140
|
+
def remove_edge!(u, v=nil)
|
141
|
+
unless u.kind_of? GRATR::Arc
|
142
|
+
raise ArgumentError if @parallel_edges
|
143
|
+
u = edge_class[u,v]
|
144
|
+
end
|
145
|
+
raise ArgumentError if @parallel_edges and (u.number || 0) == 0
|
146
|
+
return self unless @vertex_dict[u.source] # It doesn't exist
|
147
|
+
delete_label(u) # Get rid of label
|
148
|
+
if @parallel_edges
|
149
|
+
index = @edge_number[u.source].index(u.number)
|
150
|
+
raise NoArcError unless index
|
151
|
+
@vertex_dict[u.source].delete_at(index)
|
152
|
+
@edge_number[u.source].delete_at(index)
|
153
|
+
else
|
154
|
+
@vertex_dict[u.source].delete(u.target)
|
155
|
+
end
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
# Returns an array of vertices that the graph has
|
160
|
+
def vertices() @vertex_dict.keys; end
|
161
|
+
|
162
|
+
# Returns an array of edges, most likely of class Arc or Edge depending
|
163
|
+
# upon the type of graph
|
164
|
+
def edges
|
165
|
+
@vertex_dict.keys.inject(Set.new) do |a,v|
|
166
|
+
if @parallel_edges and @edge_number[v]
|
167
|
+
@vertex_dict[v].zip(@edge_number[v]).each do |w|
|
168
|
+
s,t,n = v,w[0],w[1]
|
169
|
+
a.add( edge_class[ s,t,n, edge_label(s,t,n) ] )
|
170
|
+
end
|
171
|
+
else
|
172
|
+
@vertex_dict[v].each do |w|
|
173
|
+
a.add(edge_class[v,w,edge_label(v,w)])
|
174
|
+
end
|
175
|
+
end; a
|
176
|
+
end.to_a
|
177
|
+
end
|
178
|
+
|
179
|
+
alias graph_adjacent adjacent
|
180
|
+
def adjacent(x, options={})
|
181
|
+
options[:direction] ||= :out
|
182
|
+
if !x.kind_of?(GRATR::Arc) and (options[:direction] == :out || !directed?)
|
183
|
+
if options[:type] == :edges
|
184
|
+
i = -1
|
185
|
+
@parallel_edges ?
|
186
|
+
@vertex_dict[x].map {|v| e=edge_class[x,v,@edge_number[x][i+=1]]; e.label = self[e]; e} :
|
187
|
+
@vertex_dict[x].map {|v| e=edge_class[x,v]; e.label = self[e]; e}
|
188
|
+
else
|
189
|
+
@vertex_dict[x].to_a
|
190
|
+
end
|
191
|
+
else
|
192
|
+
graph_adjacent(x,options)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
public
|
198
|
+
|
199
|
+
def self.included(cl)
|
200
|
+
# Shortcut for creating a Graph
|
201
|
+
#
|
202
|
+
# Example: GRATR::Digraph[1,2, 2,3, 2,4, 4,5].edges.to_a.to_s =>
|
203
|
+
# "(1-2)(2-3)(2-4)(4-5)"
|
204
|
+
#
|
205
|
+
# Or as a Hash for specifying lables
|
206
|
+
# GRATR::Digraph[ [:a,:b] => 3, [:b,:c] => 4 ] (Note: Do not use for Multi or Pseudo graphs)
|
207
|
+
def cl.[] (*a)
|
208
|
+
result = new
|
209
|
+
if a.size == 1 and a[0].kind_of? Hash
|
210
|
+
# Convert to edge class
|
211
|
+
a[0].each do |k,v|
|
212
|
+
if result.edge_class.include? GRATR::ArcNumber
|
213
|
+
result.add_edge!(result.edge_class[k[0],k[1],nil,v])
|
214
|
+
else
|
215
|
+
result.add_edge!(result.edge_class[k[0],k[1],v])
|
216
|
+
end
|
217
|
+
end
|
218
|
+
elsif a[0].kind_of? GRATR::Arc
|
219
|
+
a.each{|e| result.add_edge!(e); result[e] = e.label}
|
220
|
+
elsif a.size % 2 == 0
|
221
|
+
0.step(a.size-1, 2) {|i| result.add_edge!(a[i], a[i+1])}
|
222
|
+
else
|
223
|
+
raise ArgumentError
|
224
|
+
end
|
225
|
+
result
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
end # Adjacency Graph
|
230
|
+
end # GRATR
|
data/lib/gratr/base.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 Shawn Patrick Garbett
|
3
|
+
# Copyright (c) 2002,2004,2005 by Horst Duchene
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
6
|
+
# are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice(s),
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of the Shawn Garbett nor the names of its contributors
|
14
|
+
# may be used to endorse or promote products derived from this software
|
15
|
+
# without specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
18
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
19
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
20
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
21
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
23
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
24
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
25
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
26
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
#++
|
28
|
+
|
29
|
+
GRATR_VERSION = "0.4.3"
|
30
|
+
|
31
|
+
module GRATR
|
32
|
+
class NoVertexError < IndexError; end
|
33
|
+
class NoArcError < IndexError; end
|
34
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 Shawn Patrick Garbett
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
# are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# * Redistributions of source code must retain the above copyright notice(s),
|
8
|
+
# this list of conditions and the following disclaimer.
|
9
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
# * Neither the name of the Shawn Garbett nor the names of its contributors
|
13
|
+
# may be used to endorse or promote products derived from this software
|
14
|
+
# without specific prior written permission.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
17
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
19
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
20
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
21
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
22
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
23
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
24
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
25
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
|
+
#++
|
27
|
+
|
28
|
+
|
29
|
+
require 'set'
|
30
|
+
|
31
|
+
module GRATR
|
32
|
+
module Graph
|
33
|
+
# Biconnected is a module for adding the biconnected algorithm to
|
34
|
+
# UndirectedGraphs
|
35
|
+
module Biconnected
|
36
|
+
|
37
|
+
# biconnected computes the biconnected subgraphs
|
38
|
+
# of a graph using Tarjan's algorithm based on DFS. See: Robert E. Tarjan
|
39
|
+
# _Depth_First_Search_and_Linear_Graph_Algorithms_. SIAM Journal on
|
40
|
+
# Computing, 1(2):146-160, 1972
|
41
|
+
#
|
42
|
+
# The output of the algorithm is a pair, the first value is an
|
43
|
+
# array of biconnected subgraphs. The second is the set of
|
44
|
+
# articulation vertices.
|
45
|
+
#
|
46
|
+
# A connected graph is biconnected if the removal of any single vertex
|
47
|
+
# (and all edges incident on that vertex) cannot disconnect the graph.
|
48
|
+
# More generally, the biconnected components of a graph are the maximal
|
49
|
+
# subsets of vertices such that the removal of a vertex from a particular
|
50
|
+
# component will not disconnect the component. Unlike connected components,
|
51
|
+
# vertices may belong to multiple biconnected components: those vertices
|
52
|
+
# that belong to more than one biconnected component are called articulation
|
53
|
+
# points or, equivalently, cut vertices. Articulation points are vertices
|
54
|
+
# whose removal would increase the number of connected components in the graph.
|
55
|
+
# Thus, a graph without articulation points is biconnected.
|
56
|
+
def biconnected
|
57
|
+
dfs_num = 0
|
58
|
+
number = {}; predecessor = {}; low_point = {}
|
59
|
+
stack = []; result = []; articulation= []
|
60
|
+
|
61
|
+
root_vertex = Proc.new {|v| predecessor[v]=v }
|
62
|
+
enter_vertex = Proc.new {|u| number[u]=low_point[u]=(dfs_num+=1) }
|
63
|
+
tree_edge = Proc.new do |e|
|
64
|
+
stack.push(e)
|
65
|
+
predecessor[e.target] = e.source
|
66
|
+
end
|
67
|
+
back_edge = Proc.new do |e|
|
68
|
+
if e.target != predecessor[e.source]
|
69
|
+
stack.push(e)
|
70
|
+
low_point[e.source] = [low_point[e.source], number[e.target]].min
|
71
|
+
end
|
72
|
+
end
|
73
|
+
exit_vertex = Proc.new do |u|
|
74
|
+
parent = predecessor[u]
|
75
|
+
is_articulation_point = false
|
76
|
+
if number[parent] > number[u]
|
77
|
+
parent = predecessor[parent]
|
78
|
+
is_articulation_point = true
|
79
|
+
end
|
80
|
+
if parent == u
|
81
|
+
is_articulation_point = false if (number[u] + 1) == number[predecessor[u]]
|
82
|
+
else
|
83
|
+
low_point[parent] = [low_point[parent], low_point[u]].min
|
84
|
+
if low_point[u] >= number[parent]
|
85
|
+
if number[parent] > number[predecessor[parent]]
|
86
|
+
predecessor[u] = predecessor[parent]
|
87
|
+
predecessor[parent] = u
|
88
|
+
end
|
89
|
+
result << (component = self.class.new)
|
90
|
+
while number[stack[-1].source] >= number[u]
|
91
|
+
component.add_edge!(stack.pop)
|
92
|
+
end
|
93
|
+
component.add_edge!(stack.pop)
|
94
|
+
if stack.empty?
|
95
|
+
predecessor[u] = parent
|
96
|
+
predecessor[parent] = u
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
articulation << u if is_articulation_point
|
101
|
+
end
|
102
|
+
|
103
|
+
# Execute depth first search
|
104
|
+
dfs({:root_vertex => root_vertex,
|
105
|
+
:enter_vertex => enter_vertex,
|
106
|
+
:tree_edge => tree_edge,
|
107
|
+
:back_edge => back_edge,
|
108
|
+
:exit_vertex => exit_vertex})
|
109
|
+
|
110
|
+
[result, articulation]
|
111
|
+
end # biconnected
|
112
|
+
|
113
|
+
end # Biconnected
|
114
|
+
|
115
|
+
end # Graph
|
116
|
+
end # GRATR
|
@@ -0,0 +1,123 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 Shawn Patrick Garbett
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
# are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# * Redistributions of source code must retain the above copyright notice(s),
|
8
|
+
# this list of conditions and the following disclaimer.
|
9
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
# * Neither the name of the Shawn Garbett nor the names of its contributors
|
13
|
+
# may be used to endorse or promote products derived from this software
|
14
|
+
# without specific prior written permission.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
17
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
19
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
20
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
21
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
22
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
23
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
24
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
25
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
|
+
#++
|
27
|
+
|
28
|
+
|
29
|
+
require 'gratr/digraph_distance'
|
30
|
+
|
31
|
+
module GRATR
|
32
|
+
module Graph
|
33
|
+
module ChinesePostman
|
34
|
+
|
35
|
+
# Returns the shortest walk that traverses all arcs at least
|
36
|
+
# once, returning to the specified start node.
|
37
|
+
def closed_chinese_postman_tour(start, weight=nil, zero=0)
|
38
|
+
cost, path, delta = floyd_warshall(weight, zero)
|
39
|
+
return nil unless cp_valid_least_cost? cost, zero
|
40
|
+
positive, negative = cp_unbalanced(delta)
|
41
|
+
f = cp_find_feasible(delta, positive, negative, zero)
|
42
|
+
while cp_improve(f, positive, negative, cost, zero); end
|
43
|
+
cp_euler_circuit(start, f, path)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def cp_euler_circuit(start, f, path) # :nodoc:
|
49
|
+
circuit = [u=v=start]
|
50
|
+
bridge_taken = Hash.new {|h,k| h[k] = Hash.new}
|
51
|
+
until v.nil?
|
52
|
+
if v=f[u].keys.detect {|k| f[u][k] > 0}
|
53
|
+
f[u][v] -= 1
|
54
|
+
circuit << (u = path[u][v]) while u != v
|
55
|
+
else
|
56
|
+
unless bridge_taken[u][bridge = path[u][start]]
|
57
|
+
v = vertices.detect {|v1| v1 != bridge && edge?(u,v1) && !bridge_taken[u][v1]} || bridge
|
58
|
+
bridge_taken[u][v] = true
|
59
|
+
circuit << v
|
60
|
+
end
|
61
|
+
end
|
62
|
+
u=v
|
63
|
+
end; circuit
|
64
|
+
end
|
65
|
+
|
66
|
+
def cp_cancel_cycle(cost, path, f, start, zero) # :nodoc:
|
67
|
+
u = start; k = nil
|
68
|
+
begin
|
69
|
+
v = path[u][start]
|
70
|
+
k = f[v][u] if cost[u][v] < zero and (k.nil? || k > f[v][u])
|
71
|
+
end until (u=v) != start
|
72
|
+
u = start
|
73
|
+
begin
|
74
|
+
v = path[u][start]
|
75
|
+
cost[u][v] < zero ? f[v][u] -= k : f[u][v] += k
|
76
|
+
end until (u=v) != start
|
77
|
+
true # This routine always returns true to make cp_improve easier
|
78
|
+
end
|
79
|
+
|
80
|
+
def cp_improve(f, positive, negative, cost, zero) # :nodoc:
|
81
|
+
residual = self.class.new
|
82
|
+
negative.each do |u|
|
83
|
+
positive.each do |v|
|
84
|
+
residual.add_edge!(u,v,cost[u][v])
|
85
|
+
residual.add_edge!(v,u,-cost[u][v]) if f[u][v] != 0
|
86
|
+
end
|
87
|
+
end
|
88
|
+
r_cost, r_path, r_delta = residual.floyd_warshall(nil, zero)
|
89
|
+
i = residual.vertices.detect {|v| r_cost[v][v] and r_cost[v][v] < zero}
|
90
|
+
i ? cp_cancel_cycle(r_cost, r_path, f, i) : false
|
91
|
+
end
|
92
|
+
|
93
|
+
def cp_find_feasible(delta, positive, negative, zero) # :nodoc:
|
94
|
+
f = Hash.new {|h,k| h[k] = Hash.new}
|
95
|
+
negative.each do |i|
|
96
|
+
positive.each do |j|
|
97
|
+
f[i][j] = -delta[i] < delta[j] ? -delta[i] : delta[j]
|
98
|
+
delta[i] += f[i][j]
|
99
|
+
delta[j] -= f[i][j]
|
100
|
+
end
|
101
|
+
end; f
|
102
|
+
end
|
103
|
+
|
104
|
+
def cp_valid_least_cost?(c, zero) # :nodoc:
|
105
|
+
vertices.each do |i|
|
106
|
+
vertices.each do |j|
|
107
|
+
return false unless c[i][j] and c[i][j] >= zero
|
108
|
+
end
|
109
|
+
end; true
|
110
|
+
end
|
111
|
+
|
112
|
+
def cp_unbalanced(delta) # :nodoc:
|
113
|
+
negative = []; positive = []
|
114
|
+
vertices.each do |v|
|
115
|
+
negative << v if delta[v] < 0
|
116
|
+
positive << v if delta[v] > 0
|
117
|
+
end; [positive, negative]
|
118
|
+
end
|
119
|
+
|
120
|
+
end # Chinese Postman
|
121
|
+
end # Graph
|
122
|
+
end # GRATR
|
123
|
+
|