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 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,2 @@
1
+ module HiogawaPlayground
2
+ end
@@ -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: