silicium 0.0.14 → 0.0.22
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +4 -0
- data/.gitignore +13 -11
- data/.rakeTasks +8 -0
- data/.travis.yml +17 -3
- data/CODE_OF_CONDUCT.md +74 -74
- data/Gemfile +8 -4
- data/LICENSE.txt +21 -21
- data/Makefile +269 -0
- data/README.md +588 -44
- data/Rakefile +17 -10
- data/bin/console +14 -14
- data/bin/setup +8 -8
- data/docs/Object.html +117 -0
- data/docs/README_md.html +142 -0
- data/docs/Silicium/Combinatorics.html +270 -0
- data/docs/Silicium/Dice/Polyhedron.html +315 -0
- data/docs/Silicium/Dice/PolyhedronSet.html +321 -0
- data/docs/Silicium/Dice.html +99 -0
- data/docs/Silicium/Error.html +106 -0
- data/docs/Silicium/Geometry/Line2dCanon.html +243 -0
- data/docs/Silicium/Geometry/VariablesOrderException.html +106 -0
- data/docs/Silicium/Geometry.html +940 -0
- data/docs/Silicium/GraphVisualizer.html +226 -0
- data/docs/Silicium/Graphs/GraphError.html +106 -0
- data/docs/Silicium/Graphs/OrientedGraph.html +901 -0
- data/docs/Silicium/Graphs/UnorientedGraph.html +237 -0
- data/docs/Silicium/Graphs.html +374 -0
- data/docs/Silicium/IntegralDoesntExistError.html +106 -0
- data/docs/Silicium/NumericalIntegration.html +521 -0
- data/docs/Silicium/Optimization.html +629 -0
- data/docs/Silicium/Plotter/Image.html +297 -0
- data/docs/Silicium/Plotter.html +186 -0
- data/docs/Silicium.html +101 -0
- data/docs/created.rid +9 -0
- data/docs/css/fonts.css +167 -0
- data/docs/css/rdoc.css +619 -0
- data/docs/fonts/Lato-Light.ttf +0 -0
- data/docs/fonts/Lato-LightItalic.ttf +0 -0
- data/docs/fonts/Lato-Regular.ttf +0 -0
- data/docs/fonts/Lato-RegularItalic.ttf +0 -0
- data/docs/fonts/SourceCodePro-Bold.ttf +0 -0
- data/docs/fonts/SourceCodePro-Regular.ttf +0 -0
- data/docs/images/add.png +0 -0
- data/docs/images/arrow_up.png +0 -0
- data/docs/images/brick.png +0 -0
- data/docs/images/brick_link.png +0 -0
- data/docs/images/bug.png +0 -0
- data/docs/images/bullet_black.png +0 -0
- data/docs/images/bullet_toggle_minus.png +0 -0
- data/docs/images/bullet_toggle_plus.png +0 -0
- data/docs/images/date.png +0 -0
- data/docs/images/delete.png +0 -0
- data/docs/images/find.png +0 -0
- data/docs/images/loadingAnimation.gif +0 -0
- data/docs/images/macFFBgHack.png +0 -0
- data/docs/images/package.png +0 -0
- data/docs/images/page_green.png +0 -0
- data/docs/images/page_white_text.png +0 -0
- data/docs/images/page_white_width.png +0 -0
- data/docs/images/plugin.png +0 -0
- data/docs/images/ruby.png +0 -0
- data/docs/images/tag_blue.png +0 -0
- data/docs/images/tag_green.png +0 -0
- data/docs/images/transparent.png +0 -0
- data/docs/images/wrench.png +0 -0
- data/docs/images/wrench_orange.png +0 -0
- data/docs/images/zoom.png +0 -0
- data/docs/index.html +134 -0
- data/docs/js/darkfish.js +84 -0
- data/docs/js/navigation.js +105 -0
- data/docs/js/navigation.js.gz +0 -0
- data/docs/js/search.js +110 -0
- data/docs/js/search_index.js +1 -0
- data/docs/js/search_index.js.gz +0 -0
- data/docs/js/searcher.js +229 -0
- data/docs/js/searcher.js.gz +0 -0
- data/docs/table_of_contents.html +697 -0
- data/lib/algebra.rb +452 -0
- data/lib/algebra_diff.rb +258 -0
- data/lib/geometry/figure.rb +62 -0
- data/lib/geometry.rb +290 -0
- data/lib/geometry3d.rb +270 -0
- data/lib/graph/dfs.rb +41 -0
- data/lib/graph/kruskal.rb +36 -0
- data/lib/graph/scc.rb +97 -0
- data/lib/graph.rb +350 -0
- data/lib/graph_visualizer.rb +286 -0
- data/lib/ml_algorithms.rb +181 -0
- data/lib/numerical_integration.rb +184 -0
- data/lib/optimization.rb +208 -0
- data/lib/plotter.rb +258 -0
- data/lib/polynomial_division.rb +132 -0
- data/lib/polynomial_interpolation.rb +94 -0
- data/lib/regression.rb +120 -0
- data/lib/silicium/adding.rb +37 -0
- data/lib/silicium/conversions.rb +23 -0
- data/lib/silicium/multi.rb +82 -0
- data/lib/silicium/sparse.rb +76 -0
- data/lib/silicium/sugar.rb +37 -0
- data/lib/silicium/trans.rb +26 -0
- data/lib/silicium/version.rb +3 -3
- data/lib/silicium.rb +29 -6
- data/lib/theory_of_probability.rb +240 -0
- data/lib/topological_sort.rb +50 -0
- data/oriented_graph.png +0 -0
- data/plot.png +0 -0
- data/silicium.gemspec +4 -3
- metadata +122 -12
data/lib/graph/dfs.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Silicium
|
2
|
+
module Graphs
|
3
|
+
|
4
|
+
def depth_first_search?(graph, start, goal)
|
5
|
+
visited = Hash.new(false)
|
6
|
+
stack = [start]
|
7
|
+
until stack.empty?
|
8
|
+
node = stack.pop
|
9
|
+
return true if goal_node?(graph, node, goal)
|
10
|
+
add_to_stack(graph, node, stack, visited)
|
11
|
+
end
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
def goal_node?(graph, node, goal)
|
16
|
+
raise ArgumentError if graph.vertices[node].nil?
|
17
|
+
|
18
|
+
node == goal
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_to_stack(graph, node, stack, visited)
|
22
|
+
return if visited[node]
|
23
|
+
|
24
|
+
visited[node] = true
|
25
|
+
graph.vertices[node].each { |child| stack.push(child) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def dfs_traverse(graph, start)
|
29
|
+
visited = Hash.new(false)
|
30
|
+
traversed = []
|
31
|
+
dfs_traverse_recursive(graph, start, visited, traversed)
|
32
|
+
traversed
|
33
|
+
end
|
34
|
+
|
35
|
+
def dfs_traverse_recursive(graph, node, visited, traversed)
|
36
|
+
visited[node] = true
|
37
|
+
traversed.push(node)
|
38
|
+
graph.vertices[node].each { |child| dfs_traverse_recursive(graph, child, visited, traversed) unless visited[child] }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Silicium
|
2
|
+
module Graphs
|
3
|
+
|
4
|
+
class UnionFind
|
5
|
+
def initialize(graph)
|
6
|
+
@parents = []
|
7
|
+
graph.vertices.keys.each do |vertex|
|
8
|
+
@parents[vertex] = vertex
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def connected?(vertex1, vertex2)
|
13
|
+
@parents[vertex1] == @parents[vertex2]
|
14
|
+
end
|
15
|
+
|
16
|
+
def union(vertex1, vertex2)
|
17
|
+
parent1, parent2 = @parents[vertex1], @parents[vertex2]
|
18
|
+
@parents.map! { |i| i == parent1 ? parent2 : i }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Implements algorithm of Kruskal
|
23
|
+
def kruskal_mst(graph)
|
24
|
+
mst = UnorientedGraph.new
|
25
|
+
uf = UnionFind.new(graph)
|
26
|
+
graph_to_sets(graph).each do |edge, label|
|
27
|
+
unless uf.connected?(edge[0], edge[1])
|
28
|
+
add_edge!(mst, edge, label)
|
29
|
+
uf.union(edge[0], edge[1])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
mst
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
data/lib/graph/scc.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
module Silicium
|
2
|
+
|
3
|
+
module Graphs
|
4
|
+
class OrientedGraph
|
5
|
+
include Graphs
|
6
|
+
|
7
|
+
##
|
8
|
+
# Finds Strongly Connected Components (SCC) in graph. SCC is a subgraph where every vertex is reachable from every other vertex.
|
9
|
+
# @return Array of SCC. Each component is represented as Array of vertices in decreasing order of their DFS timestamps.
|
10
|
+
# @author vaimon
|
11
|
+
def find_strongly_connected_components
|
12
|
+
# Vertices we have already visited.
|
13
|
+
visited = Hash.new(false)
|
14
|
+
# Timestamps got during depth-first search.
|
15
|
+
order = []
|
16
|
+
# Resulting array of SCC.
|
17
|
+
res = []
|
18
|
+
|
19
|
+
# Step 1: Launch DFS to get order marks
|
20
|
+
@vertices.keys.each do |key|
|
21
|
+
visited, order = scc_dfs_first key, visited, order unless visited[key]
|
22
|
+
end
|
23
|
+
order.uniq!
|
24
|
+
|
25
|
+
# Step 2: Transpose adjacency list
|
26
|
+
transposed = transpose_adjacency_list
|
27
|
+
|
28
|
+
# Step 3: Launch second DFS in reverse order of timestamps from Step 1 to build components.
|
29
|
+
# HACK: In original algorithm, we use *visited* again, but here the code is a bit
|
30
|
+
# optimized using *order* instead of *visited*
|
31
|
+
until order.empty?
|
32
|
+
component = []
|
33
|
+
order, component = scc_dfs_second order.last, component, order, transposed
|
34
|
+
res << component
|
35
|
+
end
|
36
|
+
res
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
##
|
43
|
+
# Processes the first pass of <b>depth-first search</b> as a step of SCC search algorithm.
|
44
|
+
#
|
45
|
+
# Parameters:
|
46
|
+
# +v+: current vertex;
|
47
|
+
# +visited+: array of booleans representing which vertices have been processed;
|
48
|
+
# +order+: array of vertex exit timestamps.
|
49
|
+
#
|
50
|
+
# @return Tuple <code>[visited, order]</code> of params changed during current step of DFS.
|
51
|
+
def scc_dfs_first(v, visited, order)
|
52
|
+
visited[v] = true
|
53
|
+
@vertices[v].each do |adj|
|
54
|
+
visited, order = scc_dfs_first adj, visited, order unless visited[adj]
|
55
|
+
end
|
56
|
+
order << v
|
57
|
+
[visited, order]
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Transposes adjacency list as a step of SCC search algorithm.
|
62
|
+
#
|
63
|
+
# g.vertices #=> {1 => [2,3], 2 => [3], 3 => []}
|
64
|
+
# g.transpose_adjacency_list #=> {2=>[1], 3=>[1, 2]}
|
65
|
+
def transpose_adjacency_list
|
66
|
+
# HACK
|
67
|
+
res = Hash.new { |h, k| h[k] = [] }
|
68
|
+
@vertices.each do |vert, adj|
|
69
|
+
adj.each { |x| res[x] << vert }
|
70
|
+
end
|
71
|
+
res
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Processes the second pass of <b>depth-first search</b> collecting vertices to a component as a step of SCC search algorithm.
|
76
|
+
#
|
77
|
+
# Parameters:
|
78
|
+
# +v+: current vertex;
|
79
|
+
# +component+: component we are building;
|
80
|
+
# +order+: order of timestamps got after first DFS;
|
81
|
+
# +transposed+: transposed adjacency list.
|
82
|
+
#
|
83
|
+
# @return Tuple <code>[order, component]</code> of params changed during current step of DFS.
|
84
|
+
def scc_dfs_second(v, component, order, transposed)
|
85
|
+
order.delete v
|
86
|
+
component << v
|
87
|
+
transposed[v].each do |adj|
|
88
|
+
if order.include? adj
|
89
|
+
order, component = scc_dfs_second adj, component, order, transposed
|
90
|
+
end
|
91
|
+
end
|
92
|
+
[order, component]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
data/lib/graph.rb
ADDED
@@ -0,0 +1,350 @@
|
|
1
|
+
require_relative 'graph/dfs'
|
2
|
+
require_relative 'graph/scc'
|
3
|
+
require_relative 'graph/kruskal'
|
4
|
+
|
5
|
+
module Silicium
|
6
|
+
module Graphs
|
7
|
+
Pair = Struct.new(:first, :second)
|
8
|
+
|
9
|
+
class GraphError < Error
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Class represents oriented graph
|
14
|
+
class OrientedGraph
|
15
|
+
def initialize(initializer = [])
|
16
|
+
@vertices = {}; @vertex_labels = {}
|
17
|
+
@edge_labels = {}; @edge_number = 0
|
18
|
+
initializer.each do |v|
|
19
|
+
add_vertex!(v[:v])
|
20
|
+
v[:i].each do |iv|
|
21
|
+
add_vertex!(v[:v])
|
22
|
+
add_vertex!(iv)
|
23
|
+
add_edge!(v[:v], iv)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Adds vertex to graph
|
30
|
+
def add_vertex!(vertex_id)
|
31
|
+
if @vertices.has_key?(vertex_id); return end
|
32
|
+
@vertices[vertex_id] = [].to_set
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Adds edge to graph
|
37
|
+
def add_edge!(from, to)
|
38
|
+
protected_add_edge!(from, to)
|
39
|
+
@edge_number += 1
|
40
|
+
end
|
41
|
+
|
42
|
+
# should only be used in constructor
|
43
|
+
def add_edge_force!(from, to)
|
44
|
+
add_vertex!(from)
|
45
|
+
add_vertex!(to)
|
46
|
+
add_edge!(from, to)
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Returns array of vertices which adjacted with vertex
|
51
|
+
# @raise [GraphError] if graph does not contain vertex
|
52
|
+
def adjacted_with(vertex)
|
53
|
+
raise GraphError.new("Graph does not contain vertex #{vertex}") unless @vertices.has_key?(vertex)
|
54
|
+
@vertices[vertex].clone
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Adds label to edge
|
59
|
+
# @raise [GraphError] if graph does not contain edge
|
60
|
+
def label_edge!(from, to, label)
|
61
|
+
unless @vertices.has_key?(from) && @vertices[from].include?(to)
|
62
|
+
raise GraphError.new("Graph does not contain edge (#{from}, #{to})")
|
63
|
+
end
|
64
|
+
@edge_labels[Pair.new(from, to)] = label
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Adds label to vertex
|
69
|
+
# @raise [GraphError] if graph does not contain vertex
|
70
|
+
def label_vertex!(vertex, label)
|
71
|
+
unless @vertices.has_key?(vertex)
|
72
|
+
raise GraphError.new("Graph does not contain vertex #{vertex}")
|
73
|
+
end
|
74
|
+
@vertex_labels[vertex] = label
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Returns edge label
|
79
|
+
# @raise [GraphError] if graph does not contain edge
|
80
|
+
def get_edge_label(from, to)
|
81
|
+
if !@vertices.has_key?(from) || ! @vertices[from].include?(to)
|
82
|
+
raise GraphError.new("Graph does not contain edge (#{from}, #{to})")
|
83
|
+
end
|
84
|
+
@edge_labels[Pair.new(from, to)]
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Returns vertex label
|
89
|
+
# @raise [GraphError] if graph does not contain vertex
|
90
|
+
def get_vertex_label(vertex)
|
91
|
+
unless @vertices.has_key?(vertex)
|
92
|
+
raise GraphError.new("Graph does not contain vertex #{vertex}")
|
93
|
+
end
|
94
|
+
|
95
|
+
@vertex_labels[vertex]
|
96
|
+
end
|
97
|
+
##
|
98
|
+
# Returns number of vertices
|
99
|
+
def vertex_number
|
100
|
+
@vertices.count
|
101
|
+
end
|
102
|
+
##
|
103
|
+
# Returns number of edges
|
104
|
+
def edge_number
|
105
|
+
@edge_number
|
106
|
+
end
|
107
|
+
##
|
108
|
+
# Returns number of vertex labels
|
109
|
+
def vertex_label_number
|
110
|
+
@vertex_labels.count
|
111
|
+
end
|
112
|
+
##
|
113
|
+
# Returns number of edge labels
|
114
|
+
def edge_label_number
|
115
|
+
@edge_labels.count
|
116
|
+
end
|
117
|
+
##
|
118
|
+
# Checks if graph contains vertex
|
119
|
+
def has_vertex?(vertex)
|
120
|
+
@vertices.has_key?(vertex)
|
121
|
+
end
|
122
|
+
##
|
123
|
+
# Checks if graph contains edge
|
124
|
+
def has_edge?(from, to)
|
125
|
+
@vertices.has_key?(from) && @vertices[from].include?(to)
|
126
|
+
end
|
127
|
+
##
|
128
|
+
# Deletes vertex from graph
|
129
|
+
def delete_vertex!(vertex)
|
130
|
+
if has_vertex?(vertex)
|
131
|
+
@vertices.keys.each { |key| delete_edge!(key, vertex) }
|
132
|
+
@vertices.delete(vertex)
|
133
|
+
@vertex_labels.delete(vertex)
|
134
|
+
@vertices.keys.each { |key| @edge_labels.delete(Pair.new(vertex, key)) }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
##
|
138
|
+
# Deletes edge from graph
|
139
|
+
def delete_edge!(from, to)
|
140
|
+
protected_delete_edge!(from, to)
|
141
|
+
@edge_number -= 1
|
142
|
+
end
|
143
|
+
##
|
144
|
+
# Reverses graph
|
145
|
+
def reverse!
|
146
|
+
l = {}; v = {}
|
147
|
+
@vertices.keys.each { |from| v[from] = [].to_set }
|
148
|
+
@vertices.keys.each do |from|
|
149
|
+
@vertices[from].each do |to|
|
150
|
+
v[to] << from
|
151
|
+
if @edge_labels.include?(Pair.new(from, to))
|
152
|
+
l[Pair.new(to, from)] = @edge_labels[Pair.new(from, to)]
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
@vertices = v; @edge_labels = l
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
# Returns array of vertices
|
161
|
+
def vertices
|
162
|
+
@vertices
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns labels of edges
|
166
|
+
def edge_labels
|
167
|
+
@edge_labels
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
# Returns labels of vertices
|
172
|
+
def vertex_labels
|
173
|
+
@vertex_labels
|
174
|
+
end
|
175
|
+
|
176
|
+
protected
|
177
|
+
##
|
178
|
+
# Adds edge to graph
|
179
|
+
def protected_add_edge!(from, to)
|
180
|
+
@vertices[from] << to if @vertices.has_key?(from) && @vertices.has_key?(to)
|
181
|
+
end
|
182
|
+
##
|
183
|
+
# Deletes edge from graph
|
184
|
+
def protected_delete_edge!(from, to)
|
185
|
+
if has_edge?(from, to)
|
186
|
+
@vertices[from].delete(to)
|
187
|
+
@edge_labels.delete(Pair.new(from, to))
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
##
|
193
|
+
# Class represents unoriented graph
|
194
|
+
class UnorientedGraph < OrientedGraph
|
195
|
+
##
|
196
|
+
# Adds edge to graph
|
197
|
+
def add_edge!(from, to)
|
198
|
+
protected_add_edge!(from, to)
|
199
|
+
protected_add_edge!(to, from)
|
200
|
+
@edge_number += 1
|
201
|
+
end
|
202
|
+
##
|
203
|
+
# Adds label to edge
|
204
|
+
def label_edge!(from, to, label)
|
205
|
+
super(from, to, label)
|
206
|
+
super(to, from, label)
|
207
|
+
end
|
208
|
+
##
|
209
|
+
# Deletes edge from graph
|
210
|
+
def delete_edge!(from, to)
|
211
|
+
protected_delete_edge!(from, to)
|
212
|
+
protected_delete_edge!(to, from)
|
213
|
+
@edge_number -= 1
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
##
|
218
|
+
# Implements breadth-first search (BFS)
|
219
|
+
def breadth_first_search?(graph, start, goal)
|
220
|
+
visited = Hash.new(false)
|
221
|
+
queue = Queue.new
|
222
|
+
queue.push(start)
|
223
|
+
visited[start] = true
|
224
|
+
until queue.empty? do
|
225
|
+
node = queue.pop
|
226
|
+
return true if node == goal
|
227
|
+
add_to_queue(graph, queue, node, visited)
|
228
|
+
end
|
229
|
+
false
|
230
|
+
end
|
231
|
+
##
|
232
|
+
# Adds to queue not visited vertices
|
233
|
+
def add_to_queue(graph, queue, node, visited)
|
234
|
+
graph.vertices[node].each do |child|
|
235
|
+
unless visited[child]
|
236
|
+
queue.push(child)
|
237
|
+
visited[child] = true
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
##
|
242
|
+
# Checks if graph is connected
|
243
|
+
def connected?(graph)
|
244
|
+
start = graph.vertices.keys[0]
|
245
|
+
goal = graph.vertices.keys[graph.vertex_number - 1]
|
246
|
+
pred = breadth_first_search?(graph, start, goal)
|
247
|
+
graph.reverse!
|
248
|
+
pred = pred and breadth_first_search?(graph, goal, start)
|
249
|
+
graph.reverse!
|
250
|
+
pred
|
251
|
+
end
|
252
|
+
##
|
253
|
+
# Returns number of connected vertices
|
254
|
+
def number_of_connected(graph)
|
255
|
+
visited = Hash.new(false)
|
256
|
+
res = 0
|
257
|
+
graph.vertices.keys.each do |v|
|
258
|
+
unless visited[v]
|
259
|
+
dfu(graph, v, visited)
|
260
|
+
res += 1
|
261
|
+
end
|
262
|
+
end
|
263
|
+
res
|
264
|
+
end
|
265
|
+
##
|
266
|
+
# Passes graph's vertices and marks them visited
|
267
|
+
def dfu(graph, vertice, visited)
|
268
|
+
visited[vertice] = true
|
269
|
+
graph.vertices[vertice].each { |item| dfu(graph, item, visited) unless visited[item] }
|
270
|
+
end
|
271
|
+
|
272
|
+
def add_edge!(mst, edge, label)
|
273
|
+
mst.add_vertex!(edge[0])
|
274
|
+
mst.add_vertex!(edge[1])
|
275
|
+
mst.add_edge!(edge[0], edge[1])
|
276
|
+
mst.label_edge!(edge[0], edge[1], label)
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
|
281
|
+
##
|
282
|
+
# "Split" graph into elements like :[from, to] = label
|
283
|
+
def graph_to_sets(graph)
|
284
|
+
labels = {}
|
285
|
+
graph.vertices.keys.each do |from|
|
286
|
+
graph.adjacted_with(from).each { |to| labels[Pair.new(from, to)] = graph.get_edge_label(from, to) }
|
287
|
+
end
|
288
|
+
labels.to_set.sort_by { |elem| elem[1] }.to_h
|
289
|
+
end
|
290
|
+
|
291
|
+
def sum_labels(graph)
|
292
|
+
labels = 0
|
293
|
+
graph.vertices.keys.each do |from|
|
294
|
+
graph.adjacted_with(from).each { |to| labels += graph.get_edge_label(from, to) }
|
295
|
+
end
|
296
|
+
labels / 2
|
297
|
+
end
|
298
|
+
|
299
|
+
|
300
|
+
|
301
|
+
##
|
302
|
+
# Implements algorithm of Dijkstra
|
303
|
+
def dijkstra_algorithm(graph, starting_vertex)
|
304
|
+
if !graph.has_vertex?(starting_vertex)
|
305
|
+
raise GraphError.new("Graph does not contains vertex #{starting_vertex}")
|
306
|
+
end
|
307
|
+
unvisited_vertices = graph.vertices.clone.to_a
|
308
|
+
labels = {}
|
309
|
+
paths = {}
|
310
|
+
initialize_labels_and_paths(graph, labels,paths,starting_vertex)
|
311
|
+
while unvisited_vertices.size > 0
|
312
|
+
unvisited_vertices.sort { |a, b| compare_labels(a, b, labels) }
|
313
|
+
vertex = unvisited_vertices.first
|
314
|
+
vertex[1].each do |adj|
|
315
|
+
new_label = labels[vertex[0]] + graph.get_edge_label(vertex[0], adj)
|
316
|
+
if change_label?(labels[adj], new_label)
|
317
|
+
labels[adj] = new_label
|
318
|
+
paths[adj] = paths[vertex[0]].clone
|
319
|
+
paths[adj].push adj
|
320
|
+
end
|
321
|
+
end
|
322
|
+
unvisited_vertices.delete_at(0)
|
323
|
+
end
|
324
|
+
{"labels" => labels, "paths" => paths}
|
325
|
+
end
|
326
|
+
|
327
|
+
private
|
328
|
+
|
329
|
+
def initialize_labels_and_paths(graph, labels,paths,starting_vertex)
|
330
|
+
graph.vertices.each_key do |vertex|
|
331
|
+
labels[vertex] = -1
|
332
|
+
paths[vertex] = [starting_vertex]
|
333
|
+
end
|
334
|
+
labels[starting_vertex] = 0
|
335
|
+
end
|
336
|
+
|
337
|
+
def compare_labels(a, b, labels)
|
338
|
+
return -1 if labels[b[0]] == -1
|
339
|
+
return 1 if labels[a[0]] == -1
|
340
|
+
return labels[a[0]] <=> labels[b[0]]
|
341
|
+
end
|
342
|
+
|
343
|
+
def change_label?(label, new_label)
|
344
|
+
return true if label == -1
|
345
|
+
return false if new_label == -1
|
346
|
+
return new_label < label
|
347
|
+
end
|
348
|
+
|
349
|
+
end
|
350
|
+
end
|