ogr 0.1.0 → 0.2.0
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 +2 -0
- data/.travis.yml +10 -1
- data/README.md +117 -33
- data/lib/ogr.rb +7 -2
- data/lib/ogr/{graphs/breadth_first_search.rb → breadth_first_search.rb} +22 -32
- data/lib/ogr/connected_components.rb +58 -0
- data/lib/ogr/depth_first_search.rb +38 -0
- data/lib/ogr/graphs/digraph.rb +1 -0
- data/lib/ogr/graphs/graph.rb +24 -12
- data/lib/ogr/graphs/graph_as_list.rb +2 -2
- data/lib/ogr/minimum_spanning_tree.rb +32 -0
- data/lib/ogr/shortest_pahts.rb +63 -0
- data/lib/ogr/topological_sort.rb +28 -0
- data/lib/ogr/union_find.rb +39 -0
- data/lib/ogr/version.rb +1 -1
- data/ogr.gemspec +1 -1
- metadata +13 -8
- data/lib/ogr/graphs/depth_first_search.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ffca6a82d71e20952e078e9805c5ad30bc1bdc73
|
4
|
+
data.tar.gz: 8c1e23fc9bfed9e0c4d798318bff2f8f23962307
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d22658bd055558b8678099d3188ac4cfa1dbc8c318fe9b0ca2cdb9d71c8ab436e88eeb4e23f5031cfae0750723188b5c4e37febfa84c40cc344ecb0c0222fff8
|
7
|
+
data.tar.gz: 34d97eeaaad02a14e25a129154e47ceefeca43c8af16525ec9fb6eb067660db59dc4ea3084a55eb5b26f5a50e7877001800be076d768968352f9a4c89b83ae42
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
@@ -1,10 +1,19 @@
|
|
1
1
|
language: ruby
|
2
2
|
cache: bundler
|
3
3
|
rvm:
|
4
|
-
- 2.
|
4
|
+
- 2.1.5
|
5
5
|
- 2.2.3
|
6
6
|
- 2.3.0
|
7
7
|
- jruby-head
|
8
|
+
env:
|
9
|
+
- BENCH=false
|
10
|
+
- BENCH=true
|
11
|
+
matrix:
|
12
|
+
exclude:
|
13
|
+
rvm: jruby-head
|
14
|
+
env: BENCH=true
|
15
|
+
allow_failures:
|
16
|
+
- env: BENCH=true
|
8
17
|
script:
|
9
18
|
- bundle exec rake
|
10
19
|
before_install:
|
data/README.md
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
# Ogr
|
2
2
|
|
3
|
-
|
3
|
+
[](https://travis-ci.org/knife/ogr)
|
4
|
+
[](https://codeclimate.com/github/knife/ogr)
|
5
|
+
|
6
|
+
General graph library for Ruby. Provides sparse(or dense), directed(or undirected) and weighted(or normal) graphs.
|
7
|
+
Graph processing algorithms like BFS, DFS, ShortestPaths, MinimumSpanningTree are also included.
|
8
|
+
This gem depends only on [DS Gem](https://github.com/knife/ds) which contains other data structures
|
9
|
+
not implemented in Ruby Standard Library.
|
4
10
|
|
5
11
|
## Installation
|
6
12
|
|
@@ -15,7 +21,7 @@ General graph library for Ruby. Provides sparse(or dense), directed(or undirecte
|
|
15
21
|
graph = Ogr::Graph.new
|
16
22
|
```
|
17
23
|
|
18
|
-
To not have to type "Ogr::" before each class
|
24
|
+
To not have to type "Ogr::" before each class:
|
19
25
|
|
20
26
|
```ruby
|
21
27
|
include Ogr
|
@@ -25,47 +31,66 @@ To not have to type "Ogr::" before each class, use:
|
|
25
31
|
|
26
32
|
## Graph
|
27
33
|
|
28
|
-
Creating new Graph
|
34
|
+
Creating new Graph is easy.
|
29
35
|
|
30
|
-
First define edges:
|
36
|
+
First define edges(weights are optional):
|
31
37
|
|
32
38
|
```ruby
|
33
39
|
edges = []
|
34
|
-
edges << Edge.new('Jim','Bob')
|
35
|
-
edges << Edge.new('Jim','Tom')
|
36
|
-
edges << Edge.new('Bob','Jack')
|
37
|
-
edges << Edge.new('Tom','Bob')
|
40
|
+
edges << Edge.new('Jim','Bob', 12)
|
41
|
+
edges << Edge.new('Jim','Tom', 3)
|
42
|
+
edges << Edge.new('Bob','Jack', 8)
|
43
|
+
edges << Edge.new('Tom','Bob', 5)
|
38
44
|
```
|
39
45
|
or
|
40
46
|
|
41
47
|
```ruby
|
42
48
|
edges = [
|
43
|
-
['Jim','Bob'],
|
44
|
-
['Jim','Tom'],
|
45
|
-
['Bob','Jack'],
|
46
|
-
['Tom','Bob']
|
49
|
+
['Jim','Bob', 12],
|
50
|
+
['Jim','Tom', 3],
|
51
|
+
['Bob','Jack', 8],
|
52
|
+
['Tom','Bob', 5]
|
47
53
|
]
|
48
54
|
```
|
49
55
|
|
50
|
-
|
56
|
+
Next create new graph:
|
51
57
|
|
52
58
|
```ruby
|
53
59
|
graph = Graph.new(edges)
|
54
60
|
```
|
55
61
|
|
56
|
-
|
62
|
+
By default graphs are implemented as adjacency lists. This implementation works in most situations.
|
63
|
+
However, sometimes triangular matrix may be better choice for internal graph representation (for e.g. dense graphs).
|
64
|
+
You can set graph internal representation by passing second argument to initializer or using special constructor.
|
57
65
|
|
58
66
|
```ruby
|
59
|
-
graph = Graph.
|
67
|
+
graph = Graph.new_dense(edges)
|
60
68
|
```
|
61
69
|
or
|
62
70
|
|
63
71
|
```ruby
|
64
72
|
graph = Graph.new(edges, :tri_matrix)
|
65
|
-
|
66
73
|
```
|
67
74
|
|
75
|
+
|
76
|
+
### Graph API
|
77
|
+
|
78
|
+
Some methods defined on graph object:
|
79
|
+
|
80
|
+
* degree
|
81
|
+
* edge?
|
82
|
+
* add_edge
|
83
|
+
* add_edges
|
84
|
+
* get_edge
|
85
|
+
* vertex_size
|
86
|
+
* remove
|
87
|
+
* add
|
88
|
+
* neighbors
|
89
|
+
* vertexes
|
90
|
+
* edges
|
91
|
+
|
68
92
|
Examples:
|
93
|
+
|
69
94
|
```ruby
|
70
95
|
graph.vertex_size #=> 4
|
71
96
|
graph.degree("Bob") #=> 3
|
@@ -74,19 +99,22 @@ Examples:
|
|
74
99
|
graph.add_edge(Edge.new("Bob", "Kate"))
|
75
100
|
graph.remove("Bob", "Jack")
|
76
101
|
graph.neighbors('Tom') #=> ["Bob", "Jim"]
|
102
|
+
graph.add("Bob", "Jack")
|
77
103
|
```
|
78
104
|
|
79
|
-
|
105
|
+
Iterators:
|
80
106
|
|
81
107
|
```ruby
|
82
|
-
graph.each_edge{ |e| p e }
|
83
|
-
graph.each_vertex{ |v| p v }
|
108
|
+
graph.each_edge { |e| p e }
|
109
|
+
graph.each_vertex { |v| p v }
|
84
110
|
```
|
85
111
|
|
86
112
|
|
87
113
|
## Digraph
|
88
114
|
|
89
|
-
Directed
|
115
|
+
Digraph (Directed Graph) is graph with directed edges.
|
116
|
+
|
117
|
+
Creating new directed graph (implemented as adjacency list):
|
90
118
|
|
91
119
|
```ruby
|
92
120
|
edges = []
|
@@ -104,38 +132,94 @@ Directed Weighted Graph
|
|
104
132
|
edges << Edge.new(:F, :B, 7)
|
105
133
|
```
|
106
134
|
|
107
|
-
New directed graph (implemented as adjency list)
|
108
|
-
|
109
135
|
```ruby
|
110
|
-
|
136
|
+
digraph = Digraph.new(edges)
|
111
137
|
```
|
112
138
|
|
113
|
-
New directed dense graph (implemented as matrix)
|
139
|
+
New directed dense graph (implemented as matrix):
|
114
140
|
```ruby
|
115
|
-
|
141
|
+
digraph = Digraph.new_dense(edges)
|
116
142
|
```
|
117
143
|
or
|
118
144
|
```ruby
|
119
|
-
|
145
|
+
digraph = Digraph.new(edges, :matrix)
|
120
146
|
```
|
121
147
|
|
148
|
+
### Digraph API
|
149
|
+
|
150
|
+
Digraph inherits from Graph so all methods defined in Graph are available.
|
151
|
+
Digraph defines some additional methods specific to directed graphs:
|
152
|
+
|
153
|
+
* in_degree
|
154
|
+
* out_degree
|
155
|
+
|
122
156
|
Examples
|
123
157
|
```ruby
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
158
|
+
digraph.get_edge(:D, :C).weight #=> 11
|
159
|
+
digraph.degree(:E) #=> 4
|
160
|
+
digraph.in_degree(:E) #=> 3
|
161
|
+
digraph.out_degree(:E) #=> 1
|
128
162
|
```
|
129
163
|
|
130
164
|
## Searching
|
131
165
|
|
132
166
|
Breadth First Search:
|
133
167
|
```ruby
|
134
|
-
BreadthFirstSearch.new(
|
168
|
+
BreadthFirstSearch.new(digraph).search(:A) #=> [:A, :C, :D, :G, :E, :F, :B]
|
135
169
|
```
|
136
170
|
|
137
171
|
Depth First Search:
|
138
172
|
```ruby
|
139
|
-
DepthFirstSearch.new(
|
140
|
-
|
173
|
+
DepthFirstSearch.new(digraph).search(:A) #=> [:A, :G, :B, :E, :D, :C, :F]
|
174
|
+
```
|
175
|
+
|
176
|
+
You can also pass block to search methods:
|
177
|
+
```ruby
|
178
|
+
DepthFirstSearch.new(digraph).search(:A) { |v| v.to_s.downcase }
|
179
|
+
```
|
180
|
+
|
181
|
+
If source vertex is not given, search method iterates over all vertexes in graph.
|
182
|
+
|
183
|
+
## Topological Sort
|
184
|
+
|
185
|
+
Topological Sort sorts vertexes in topological order (all edges in graph points in the same direction).
|
186
|
+
Works only for digraphs without cycles (Directed Acyclic Graphs)
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
TopologicalSort.new(graph).sort #=> array of vertexes in topological order
|
190
|
+
```
|
191
|
+
|
192
|
+
## Connected Compontents
|
193
|
+
|
194
|
+
ConnectedComponents finds all connected components in graph.
|
195
|
+
|
196
|
+
```ruby
|
197
|
+
cc = ConnectedComponents.new(graph)
|
198
|
+
cc.count # => 1
|
199
|
+
cc.connected?(:a, :b) # => true
|
200
|
+
```
|
201
|
+
|
202
|
+
## Minimum Spanning Tree
|
203
|
+
|
204
|
+
MinimumSpanningTree finds tree connecting all vertexes in graph with minimal weights.
|
205
|
+
Implements Kruskal's algorithm.
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
tree = MinimumSpanningTree.new(graph).calculate #=> array of edges
|
209
|
+
```
|
210
|
+
|
211
|
+
|
212
|
+
## Shortest Paths
|
213
|
+
|
214
|
+
Finds shortest paths from source to all vertexes in graph. Implements Dijkstra's algorithm and
|
215
|
+
works only for digraphs with positive weights.
|
216
|
+
|
217
|
+
Finding shortest paths in graph from vertex 0:
|
218
|
+
```ruby
|
219
|
+
sp = ShortestPaths.new(digraph, 0)
|
220
|
+
sp.distance_to(7) #=> returns shortest distance from vertex 0 to vertex 7
|
221
|
+
sp.has_path?(7) #=> returns true if shortest path to 7 exists
|
222
|
+
sp.path_to(4) #=> returns path from vertex 0 to vertex 4 (array of vertexes)
|
223
|
+
```
|
224
|
+
|
141
225
|
|
data/lib/ogr.rb
CHANGED
@@ -9,8 +9,13 @@ require 'ogr/graphs/graph_as_tri_matrix'
|
|
9
9
|
require 'ogr/graphs/edge_bag'
|
10
10
|
require 'ogr/graphs/graph'
|
11
11
|
require 'ogr/graphs/digraph'
|
12
|
-
require 'ogr/
|
13
|
-
require 'ogr/
|
12
|
+
require 'ogr/breadth_first_search'
|
13
|
+
require 'ogr/depth_first_search'
|
14
|
+
require 'ogr/topological_sort'
|
15
|
+
require 'ogr/connected_components'
|
16
|
+
require 'ogr/shortest_pahts'
|
17
|
+
require 'ogr/minimum_spanning_tree'
|
18
|
+
require 'ogr/union_find'
|
14
19
|
|
15
20
|
# Main module namespace
|
16
21
|
module Ogr
|
@@ -1,27 +1,22 @@
|
|
1
1
|
module Ogr
|
2
2
|
# Class implements Breadth First Search in graphs
|
3
3
|
class BreadthFirstSearch
|
4
|
-
|
5
|
-
private :parents=, :visited=, :distance=
|
4
|
+
attr_reader :parents, :visited, :distance
|
6
5
|
|
7
6
|
def initialize(graph)
|
8
7
|
@graph = graph
|
9
|
-
@colors =
|
8
|
+
@colors = Hash.new(:white)
|
10
9
|
@parents = {}
|
11
10
|
@visited = []
|
12
|
-
@distance =
|
11
|
+
@distance = Hash.new(Float::INFINITY)
|
13
12
|
@to_visit = SimpleQueue.new
|
14
13
|
end
|
15
14
|
|
16
|
-
def search(
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
v = to_visit.dequeue
|
22
|
-
visit_neighbors(v)
|
23
|
-
colors[v] = :black
|
24
|
-
visited << (block_given? ? yield(v) : v)
|
15
|
+
def search(source = nil, &block)
|
16
|
+
if source
|
17
|
+
visit_source(source, &block)
|
18
|
+
else
|
19
|
+
graph.each_vertex { |v| visit_source(v, &block) unless visited?(v) }
|
25
20
|
end
|
26
21
|
visited
|
27
22
|
end
|
@@ -30,20 +25,21 @@ module Ogr
|
|
30
25
|
|
31
26
|
attr_accessor :graph, :to_visit, :colors
|
32
27
|
|
33
|
-
def reset!
|
34
|
-
self.visited = []
|
35
|
-
graph.each_vertex do |v|
|
36
|
-
colors[v] = :white
|
37
|
-
parents[v] = nil
|
38
|
-
distance[v] = Float::INFINITY
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
28
|
def visit_source(s)
|
43
|
-
colors[s] = :white
|
44
|
-
parents[s] = nil
|
45
29
|
distance[s] = 0
|
46
30
|
to_visit.enqueue s
|
31
|
+
until to_visit.empty?
|
32
|
+
v = to_visit.dequeue
|
33
|
+
visit_neighbors(v)
|
34
|
+
colors[v] = :black
|
35
|
+
visited << (block_given? ? yield(v) : v)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def visit_neighbors(u)
|
40
|
+
graph.neighbors(u).each do |v|
|
41
|
+
visit_node(v, u) unless visited?(v)
|
42
|
+
end
|
47
43
|
end
|
48
44
|
|
49
45
|
def visit_node(v, from)
|
@@ -53,14 +49,8 @@ module Ogr
|
|
53
49
|
to_visit.enqueue v
|
54
50
|
end
|
55
51
|
|
56
|
-
def
|
57
|
-
|
58
|
-
visit_node(v, u) if not_visited?(v)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def not_visited?(v)
|
63
|
-
colors[v] == :white
|
52
|
+
def visited?(v)
|
53
|
+
colors[v] != :white
|
64
54
|
end
|
65
55
|
end
|
66
56
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Ogr
|
2
|
+
# ConnectedComponents class finds graph components
|
3
|
+
class ConnectedComponents
|
4
|
+
attr_reader :graph, :visited, :counter, :stack
|
5
|
+
private :graph, :visited, :counter, :stack
|
6
|
+
|
7
|
+
def initialize(graph)
|
8
|
+
@graph = graph
|
9
|
+
@visited = {}
|
10
|
+
@counter = 0
|
11
|
+
find_components
|
12
|
+
end
|
13
|
+
|
14
|
+
def connected?(v, u)
|
15
|
+
visited[v] == visited[u]
|
16
|
+
end
|
17
|
+
|
18
|
+
def component(v)
|
19
|
+
visited[v]
|
20
|
+
end
|
21
|
+
|
22
|
+
def components
|
23
|
+
visited
|
24
|
+
end
|
25
|
+
|
26
|
+
def count
|
27
|
+
counter
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def find_components
|
33
|
+
vertexes.each do |v|
|
34
|
+
unless visited[v]
|
35
|
+
dfs(v)
|
36
|
+
@counter += 1
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def dfs(v)
|
42
|
+
@stack = [v]
|
43
|
+
while u = stack.pop
|
44
|
+
visit(u)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def visit(v)
|
49
|
+
return if visited[v]
|
50
|
+
visited[v] = counter
|
51
|
+
stack.concat(graph.neighbors(v))
|
52
|
+
end
|
53
|
+
|
54
|
+
def vertexes
|
55
|
+
@vertexes ||= graph.vertexes
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Ogr
|
2
|
+
# Class implements Depth First Search in graphs
|
3
|
+
class DepthFirstSearch
|
4
|
+
attr_reader :graph, :stack, :marked, :visited
|
5
|
+
private :graph, :stack, :marked
|
6
|
+
|
7
|
+
def initialize(graph)
|
8
|
+
@graph = graph
|
9
|
+
@marked = {}
|
10
|
+
@visited = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def search(source = nil, &block)
|
14
|
+
if source
|
15
|
+
dfs(source, &block)
|
16
|
+
else
|
17
|
+
graph.vertexes.each { |v| dfs(v, &block) unless marked[v] }
|
18
|
+
end
|
19
|
+
visited
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def dfs(v, &block)
|
25
|
+
@stack = [v]
|
26
|
+
while u = stack.pop
|
27
|
+
visit(u, &block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def visit(v)
|
32
|
+
return if marked[v]
|
33
|
+
visited << (block_given? ? yield(v) : v)
|
34
|
+
marked[v] = true
|
35
|
+
stack.concat(graph.neighbors(v))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/ogr/graphs/digraph.rb
CHANGED
data/lib/ogr/graphs/graph.rb
CHANGED
@@ -24,15 +24,16 @@ module Ogr
|
|
24
24
|
new(edges, :tri_matrix)
|
25
25
|
end
|
26
26
|
|
27
|
-
#
|
27
|
+
# Creates new edge in graph.
|
28
28
|
def add(x, y, weight = 1)
|
29
|
+
weight ||= 1
|
29
30
|
@g.add(push(x), push(y), weight)
|
30
31
|
end
|
31
32
|
|
32
33
|
# Adds new edges to graph.
|
33
34
|
def add_edges(edges)
|
34
35
|
if edges[0].is_a? Array
|
35
|
-
edges.each { |e| add(e[0], e[1]) }
|
36
|
+
edges.each { |e| add(e[0], e[1], e[2]) }
|
36
37
|
else
|
37
38
|
edges.each { |e| add(e.from, e.to, e.weight) }
|
38
39
|
end
|
@@ -70,34 +71,45 @@ module Ogr
|
|
70
71
|
Edge.new(x, y, w) if w
|
71
72
|
end
|
72
73
|
|
73
|
-
|
74
|
-
|
74
|
+
# Vertex iterator
|
75
|
+
def each_vertex
|
76
|
+
vc.each { |v| yield vertexes[v] }
|
75
77
|
end
|
76
78
|
|
79
|
+
# Edge iterator
|
80
|
+
def each_edge(&block)
|
81
|
+
@g.each_edge(vertexes, &block)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns array of all graph vertexes
|
77
85
|
def vertexes
|
78
86
|
@map.to_a
|
79
87
|
end
|
80
88
|
|
89
|
+
# Vertex size
|
81
90
|
def vertex_size
|
82
91
|
vertexes.size
|
83
92
|
end
|
84
93
|
|
85
|
-
# Vertex
|
86
|
-
def
|
87
|
-
|
94
|
+
# Vertex numbers
|
95
|
+
def vc
|
96
|
+
(0...vertexes.size)
|
88
97
|
end
|
89
98
|
|
90
|
-
#
|
91
|
-
def
|
92
|
-
|
99
|
+
# Returns array of all graph edges
|
100
|
+
def edges
|
101
|
+
arr = []
|
102
|
+
each_edge { |e| arr << e }
|
103
|
+
arr
|
93
104
|
end
|
94
105
|
|
95
|
-
|
96
|
-
|
106
|
+
# Internal numeric representation of vertex
|
97
107
|
def index(x)
|
98
108
|
@map.index(x)
|
99
109
|
end
|
100
110
|
|
111
|
+
private
|
112
|
+
|
101
113
|
def push(x)
|
102
114
|
@map.push(x)
|
103
115
|
end
|
@@ -37,6 +37,7 @@ module Ogr
|
|
37
37
|
|
38
38
|
# Returns all neighbors for given vertex.
|
39
39
|
def neighbors(x)
|
40
|
+
return [] unless @store[x]
|
40
41
|
@store[x].map { |edge| edge[:v] }
|
41
42
|
end
|
42
43
|
|
@@ -44,8 +45,7 @@ module Ogr
|
|
44
45
|
# edges, :out - outcoming edges, :all - incoming and outcoming edges.
|
45
46
|
def degree(x, direction = :all)
|
46
47
|
r = Hash.new(0)
|
47
|
-
|
48
|
-
vc.each do |i|
|
48
|
+
(0..@store.size).each do |i|
|
49
49
|
r[:in] += 1 if connected?(i, x)
|
50
50
|
r[:out] += 1 if connected?(x, i)
|
51
51
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Ogr
|
2
|
+
# Class implements Kruskal algorithm for finding Minimum Spanning Tree
|
3
|
+
class MinimumSpanningTree
|
4
|
+
attr_reader :graph, :uf
|
5
|
+
private :graph, :uf
|
6
|
+
|
7
|
+
def initialize(graph)
|
8
|
+
@graph = graph
|
9
|
+
@uf = UnionFind.new(@graph.vertexes.size)
|
10
|
+
end
|
11
|
+
|
12
|
+
def calculate
|
13
|
+
tree = []
|
14
|
+
while edge = edges.shift
|
15
|
+
from = graph.index(edge.from)
|
16
|
+
to = graph.index(edge.to)
|
17
|
+
|
18
|
+
unless uf.connected?(from, to)
|
19
|
+
uf.union(from, to)
|
20
|
+
tree << edge
|
21
|
+
end
|
22
|
+
end
|
23
|
+
tree
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def edges
|
29
|
+
@edges ||= @graph.edges.sort_by(&:weight)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Ogr
|
2
|
+
# Finds shortest paths in graph using Dijkstra algorithm
|
3
|
+
class ShortestPaths
|
4
|
+
attr_reader :parent, :distance, :graph, :queue
|
5
|
+
private :parent, :distance, :graph, :queue
|
6
|
+
|
7
|
+
def initialize(graph, source)
|
8
|
+
@graph = graph
|
9
|
+
@queue = IndexedPriorityQueue.min
|
10
|
+
@parent = {}
|
11
|
+
@distance = {}
|
12
|
+
@graph.vertexes.each { |v| @distance[v] = Float::INFINITY }
|
13
|
+
|
14
|
+
find_paths(source)
|
15
|
+
end
|
16
|
+
|
17
|
+
def distance_to(goal)
|
18
|
+
distance[goal]
|
19
|
+
end
|
20
|
+
|
21
|
+
def from(goal)
|
22
|
+
parent[goal]
|
23
|
+
end
|
24
|
+
|
25
|
+
def path?(goal)
|
26
|
+
distance[goal] < Float::INFINITY
|
27
|
+
end
|
28
|
+
|
29
|
+
def path_to(goal)
|
30
|
+
v = goal
|
31
|
+
path = [v]
|
32
|
+
path.push v while v = parent[v]
|
33
|
+
path.reverse
|
34
|
+
end
|
35
|
+
|
36
|
+
def find_paths(source)
|
37
|
+
distance[source] = 0
|
38
|
+
queue.push(source, 0)
|
39
|
+
|
40
|
+
while v = queue.shift
|
41
|
+
graph.neighbors(v).each do |u|
|
42
|
+
relax(graph.get_edge(v, u))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def relax(edge)
|
48
|
+
new_distance = distance[edge.from] + edge.weight
|
49
|
+
found_better(edge, new_distance) if new_distance < distance[edge.to]
|
50
|
+
end
|
51
|
+
|
52
|
+
def found_better(edge, new_distance)
|
53
|
+
v = edge.to
|
54
|
+
distance[v] = new_distance
|
55
|
+
parent[v] = edge.from
|
56
|
+
if queue.include?(v)
|
57
|
+
queue.update(v, new_distance)
|
58
|
+
else
|
59
|
+
queue.push(v, new_distance)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Ogr
|
2
|
+
# Sorts graph vertexes in topological order
|
3
|
+
class TopologicalSort
|
4
|
+
attr_reader :graph, :sorted, :marked
|
5
|
+
private :graph, :sorted, :marked
|
6
|
+
|
7
|
+
def initialize(graph)
|
8
|
+
@graph = graph
|
9
|
+
@marked = {}
|
10
|
+
@sorted = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def sort
|
14
|
+
graph.vertexes.each { |v| dfs(v) unless marked[v] }
|
15
|
+
sorted.reverse
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def dfs(v)
|
21
|
+
marked[v] = true
|
22
|
+
graph.neighbors(v).reverse_each do |u|
|
23
|
+
dfs(u) unless marked[u]
|
24
|
+
end
|
25
|
+
sorted.push v
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Ogr
|
2
|
+
# Class implements Union Find algorithm
|
3
|
+
class UnionFind
|
4
|
+
attr_reader :store, :sizes
|
5
|
+
private :store, :sizes
|
6
|
+
|
7
|
+
def initialize(n)
|
8
|
+
@store = (0..n).to_a
|
9
|
+
@sizes = [1] * n
|
10
|
+
end
|
11
|
+
|
12
|
+
def connected?(x, y)
|
13
|
+
root(x) == root(y)
|
14
|
+
end
|
15
|
+
|
16
|
+
def union(x, y)
|
17
|
+
root_x = root(x)
|
18
|
+
root_y = root(y)
|
19
|
+
if sizes[root_y] > sizes[root_x]
|
20
|
+
update_sizes(root_x, root_y)
|
21
|
+
else
|
22
|
+
update_sizes(root_y, root_x)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def update_sizes(x, y)
|
29
|
+
store[x] = y
|
30
|
+
sizes[y] += sizes[x]
|
31
|
+
end
|
32
|
+
|
33
|
+
def root(x)
|
34
|
+
parent = store[x]
|
35
|
+
parent = store[parent] while parent != store[parent]
|
36
|
+
parent
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/ogr/version.rb
CHANGED
data/ogr.gemspec
CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
30
|
spec.require_paths = ['lib']
|
31
31
|
|
32
|
-
spec.add_runtime_dependency 'ds', '
|
32
|
+
spec.add_runtime_dependency 'ds', '>= 0.0.7'
|
33
33
|
|
34
34
|
spec.add_development_dependency 'bundler', '~> 1.11'
|
35
35
|
spec.add_development_dependency 'rake', '~> 10.0'
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ogr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- knife
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ds
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.0.7
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 0.0.7
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,8 +83,9 @@ files:
|
|
83
83
|
- bin/console
|
84
84
|
- bin/setup
|
85
85
|
- lib/ogr.rb
|
86
|
-
- lib/ogr/
|
87
|
-
- lib/ogr/
|
86
|
+
- lib/ogr/breadth_first_search.rb
|
87
|
+
- lib/ogr/connected_components.rb
|
88
|
+
- lib/ogr/depth_first_search.rb
|
88
89
|
- lib/ogr/graphs/digraph.rb
|
89
90
|
- lib/ogr/graphs/edge.rb
|
90
91
|
- lib/ogr/graphs/edge_bag.rb
|
@@ -92,6 +93,10 @@ files:
|
|
92
93
|
- lib/ogr/graphs/graph_as_list.rb
|
93
94
|
- lib/ogr/graphs/graph_as_matrix.rb
|
94
95
|
- lib/ogr/graphs/graph_as_tri_matrix.rb
|
96
|
+
- lib/ogr/minimum_spanning_tree.rb
|
97
|
+
- lib/ogr/shortest_pahts.rb
|
98
|
+
- lib/ogr/topological_sort.rb
|
99
|
+
- lib/ogr/union_find.rb
|
95
100
|
- lib/ogr/version.rb
|
96
101
|
- ogr.gemspec
|
97
102
|
homepage: https://github.com/knife/ogr
|
@@ -1,40 +0,0 @@
|
|
1
|
-
module Ogr
|
2
|
-
# Class implements Breadth First Search in graphs
|
3
|
-
class DepthFirstSearch
|
4
|
-
attr_accessor :parents, :visited, :distance, :marked
|
5
|
-
private :parents=, :visited=, :distance=, :marked=
|
6
|
-
|
7
|
-
def initialize(graph)
|
8
|
-
@graph = graph
|
9
|
-
@parents = {}
|
10
|
-
@marked = {}
|
11
|
-
@visited = []
|
12
|
-
@distance = {}
|
13
|
-
end
|
14
|
-
|
15
|
-
def search(u, &block)
|
16
|
-
reset!
|
17
|
-
dfs(u, &block)
|
18
|
-
visited
|
19
|
-
end
|
20
|
-
|
21
|
-
def dfs(v, from = nil, &block)
|
22
|
-
marked[v] = true
|
23
|
-
visited << (block_given? ? yield(v) : v)
|
24
|
-
parents[v] = from
|
25
|
-
graph.neighbors(v).reverse_each do |u|
|
26
|
-
dfs(u, v, &block) unless marked[u]
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
attr_reader :graph
|
33
|
-
|
34
|
-
def reset!
|
35
|
-
self.visited = []
|
36
|
-
self.parents = {}
|
37
|
-
self.marked = {}
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|