ogr 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/knife/ogr.svg?branch=master)](https://travis-ci.org/knife/ogr)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/knife/ogr/badges/gpa.svg)](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
|