hiogawa_playground 0.0.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 +7 -0
- data/lib/hiogawa_playground.rb +2 -0
- data/lib/hiogawa_playground/mst.rb +95 -0
- data/lib/hiogawa_playground/tsp.rb +99 -0
- metadata +47 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 42915689772174d0f2719d0e4075864e2a2dad8f
|
|
4
|
+
data.tar.gz: ebaa52dd0daa36bd1db4b8b6706416112d549214
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 62b06f1207f56b902a3447eb8b297770b8acda5648595d3033d8552a73fe91573b79018b21727c9218b4d77defe9610c0128dc4fa830eecba32ffdc5c711e7b8
|
|
7
|
+
data.tar.gz: 9a0f0bc53ff68305d9c5d141762d1f12f794d30811044c5603c5986bb00829051e87330b078cc7a479bf49fbdc8a44acc134c7515ae81109f74510a213029c86
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
module HiogawaPlayground
|
|
2
|
+
module Mst
|
|
3
|
+
class << self
|
|
4
|
+
# points: array of nodes
|
|
5
|
+
# distance: distance function
|
|
6
|
+
def solve(points, distance) # O(n * n)
|
|
7
|
+
num_points = points.length
|
|
8
|
+
parents_relation = Array.new(num_points)
|
|
9
|
+
candidates = []
|
|
10
|
+
|
|
11
|
+
# Procedure:
|
|
12
|
+
# 1. init `candidates` based on `points[0]`
|
|
13
|
+
# 2. loop until there's no `candidates`
|
|
14
|
+
# a. pick closest `candidates`
|
|
15
|
+
# b. update `candidates` and `parents_relation` with picked one
|
|
16
|
+
# 3. return `parents_relation`
|
|
17
|
+
|
|
18
|
+
# 1
|
|
19
|
+
(1..(num_points - 1)).each do |idx|
|
|
20
|
+
candidates.push([idx, 0])
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# 2
|
|
24
|
+
while !candidates.empty? # O(n)
|
|
25
|
+
# a
|
|
26
|
+
picked_pair, *rest_candidates = candidates
|
|
27
|
+
rest_candidates.each do |pair| # O(n)
|
|
28
|
+
if (
|
|
29
|
+
distance.call(pair[0], pair[1]) <
|
|
30
|
+
distance.call(picked_pair[0], picked_pair[1])
|
|
31
|
+
)
|
|
32
|
+
picked_pair = pair
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# b
|
|
37
|
+
candidates.delete(picked_pair) # O(n)
|
|
38
|
+
parents_relation[picked_pair[0]] = picked_pair[1]
|
|
39
|
+
candidates.each do |pair| # O(n)
|
|
40
|
+
if (
|
|
41
|
+
distance.call(pair[0], picked_pair[0]) <
|
|
42
|
+
distance.call(pair[0], pair[1])
|
|
43
|
+
)
|
|
44
|
+
pair[1] = picked_pair[0]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# 3
|
|
50
|
+
parents_relation
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def solve_edges(points, distance)
|
|
54
|
+
solve(points, distance)
|
|
55
|
+
.each_with_index
|
|
56
|
+
.select { |parent, _| parent != nil }
|
|
57
|
+
.map { |parent, i| [parent, i] }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# TODO: estimate time complexity
|
|
61
|
+
def solve_clustering(points, distance, threshold)
|
|
62
|
+
edges = solve_edges(points, distance).select { |i, j| distance.call(i, j) <= threshold }
|
|
63
|
+
adjacency_list = (0..(points.length - 1)).map { |i| [i] }
|
|
64
|
+
edges.each do |i, j|
|
|
65
|
+
adjacency_list[i].push(j)
|
|
66
|
+
adjacency_list[j].push(i)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
connected_components(points.length, adjacency_list)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def connected_components(num_vs, adjacency_list)
|
|
73
|
+
visited = (0..(num_vs - 1)).map { false }
|
|
74
|
+
|
|
75
|
+
dfs = lambda do |i, current_cluster|
|
|
76
|
+
current_cluster.push(i)
|
|
77
|
+
visited[i] = true
|
|
78
|
+
adjacency_list[i].each do |j|
|
|
79
|
+
dfs.call(j, current_cluster) unless visited[j]
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
clusters = []
|
|
84
|
+
(0..(num_vs - 1)).each do |i|
|
|
85
|
+
unless visited[i]
|
|
86
|
+
cluster = []
|
|
87
|
+
dfs.call(i, cluster)
|
|
88
|
+
clusters.push(cluster)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
clusters
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
module HiogawaPlayground
|
|
2
|
+
module Tsp
|
|
3
|
+
class << self
|
|
4
|
+
def solve_tour(points, distance)
|
|
5
|
+
n = points.length
|
|
6
|
+
init_path = (0..(n - 1)).to_a.push(0)
|
|
7
|
+
two_opt(distance, init_path)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def solve_path(points, distance, st_idx, end_idx)
|
|
11
|
+
n = points.length
|
|
12
|
+
init_path = [st_idx] +
|
|
13
|
+
(0..(n - 1)).to_a.select { |i| i != st_idx && i != end_idx } +
|
|
14
|
+
[end_idx]
|
|
15
|
+
two_opt(distance, init_path)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def solve_arbitrary_path(points, distance)
|
|
19
|
+
path = solve_tour(points, distance)
|
|
20
|
+
n = points.length
|
|
21
|
+
longest_i = (0..(n - 1)).max_by { |i| distance.call(path[i], path[i + 1]) }
|
|
22
|
+
path[(longest_i + 1)..-2] + path[0..longest_i]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def solve_path_with_loose_end(points, distance, st_idx)
|
|
26
|
+
n = points.length
|
|
27
|
+
init_path = [st_idx] + (0..(n - 1)).to_a.select { |i| i != st_idx }
|
|
28
|
+
two_opt_with_loose_end(distance, init_path)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# 2-opt algorithm
|
|
32
|
+
def two_opt(distance, path)
|
|
33
|
+
loop do
|
|
34
|
+
opt = nil
|
|
35
|
+
candidate_index_pairs(path).each do |i, j|
|
|
36
|
+
before = distance.call(path[i], path[i + 1]) + distance.call(path[j], path[j + 1])
|
|
37
|
+
after = distance.call(path[i], path[j]) + distance.call(path[i + 1], path[j + 1])
|
|
38
|
+
if after < before
|
|
39
|
+
opt = [i, j]
|
|
40
|
+
break
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
break unless opt
|
|
44
|
+
path = two_swap(path, opt[0], opt[1])
|
|
45
|
+
end
|
|
46
|
+
path
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# 2-opt algorithm extended to loose path end
|
|
50
|
+
def two_opt_with_loose_end(distance, path)
|
|
51
|
+
loop do
|
|
52
|
+
opt = nil
|
|
53
|
+
candidate_index_pairs_with_loose_end(path).each do |i, j|
|
|
54
|
+
if j + 1 == path.length
|
|
55
|
+
before = distance.call(path[i], path[i + 1])
|
|
56
|
+
after = distance.call(path[i], path[j])
|
|
57
|
+
else
|
|
58
|
+
before = distance.call(path[i], path[i + 1]) + distance.call(path[j], path[j + 1])
|
|
59
|
+
after = distance.call(path[i], path[j]) + distance.call(path[i + 1], path[j + 1])
|
|
60
|
+
end
|
|
61
|
+
if after < before
|
|
62
|
+
opt = [i, j]
|
|
63
|
+
break
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
break unless opt
|
|
67
|
+
path = two_swap(path, opt[0], opt[1])
|
|
68
|
+
end
|
|
69
|
+
path
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# enumerate possible 2 points to swap
|
|
73
|
+
def candidate_index_pairs(path)
|
|
74
|
+
n = path.length
|
|
75
|
+
(2..(n - 2)).flat_map do |gap|
|
|
76
|
+
(0..(n - gap - 2)).map do |i|
|
|
77
|
+
[i, i + gap]
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def candidate_index_pairs_with_loose_end(path)
|
|
83
|
+
n = path.length
|
|
84
|
+
(2..(n - 1)).flat_map do |gap|
|
|
85
|
+
(0..(n - gap - 1)).map do |i|
|
|
86
|
+
[i, i + gap]
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# 2 points swap operation with assuming
|
|
92
|
+
# - i + 2 <= j
|
|
93
|
+
# - j < path.length
|
|
94
|
+
def two_swap(path, i, j)
|
|
95
|
+
path[0..i] + path[(i + 1)..j].reverse + path[(j + 1)..-1]
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: hiogawa_playground
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Hiroshi Ogawa
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2016-06-10 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: hiroshi's playground
|
|
14
|
+
email: hi.ogawa.zz@gmail.com
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- lib/hiogawa_playground.rb
|
|
20
|
+
- lib/hiogawa_playground/mst.rb
|
|
21
|
+
- lib/hiogawa_playground/tsp.rb
|
|
22
|
+
homepage: http://rubygems.org/gems/hiogawa_playground
|
|
23
|
+
licenses:
|
|
24
|
+
- MIT
|
|
25
|
+
metadata: {}
|
|
26
|
+
post_install_message:
|
|
27
|
+
rdoc_options: []
|
|
28
|
+
require_paths:
|
|
29
|
+
- lib
|
|
30
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
- - ">="
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: '0'
|
|
35
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
requirements: []
|
|
41
|
+
rubyforge_project:
|
|
42
|
+
rubygems_version: 2.2.2
|
|
43
|
+
signing_key:
|
|
44
|
+
specification_version: 4
|
|
45
|
+
summary: playground
|
|
46
|
+
test_files: []
|
|
47
|
+
has_rdoc:
|