union_find 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3d86fb59ebdb3b6b5f55e175db95bd7dccf0126b
4
+ data.tar.gz: 8dfbcfdff8f2a1f20e808df6404312bbd2793967
5
+ SHA512:
6
+ metadata.gz: fee3e76ee13f5c64e8b8f278e8c42ac0ebb979603bc3b1e19b4e76980ba8128c9c1fd594c31d04006f61149afbf86b364cd71d0f59a2206aa7140d3350f13096
7
+ data.tar.gz: 280542a0d35dc79eb7343f3efd350e8ea8629a47377cb9b04ae58076b9e3da45f983ffa5ce522518515bbef974746a097b16dfd8b87db09f8b93bbb1928b98b5
data/.gitignore ADDED
@@ -0,0 +1,24 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ .ruby-gemset
24
+ .ruby-version
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in union_find.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Michael Imstepf
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # Weighted quick-union algorithm with path compression
2
+
3
+ Union Find is an algorithm that uses a disjoint-set data structure. It allows us to efficiently connect any items of a given list and to efficiently check whether two items of this list are connected (any degree of separation) or not.
4
+
5
+ Possible applications where we might want to find out whether two items are connected to each other are:
6
+ * Social networks
7
+ * Computers in a network
8
+ * Web pages on the Internet
9
+ * Transistors in a computer chip
10
+ * Pixels in a digital photo
11
+ * Metallic sites in a composite system
12
+
13
+ Click [here](https://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf) for more information.
14
+
15
+ The running time of this algorithm is linear.
16
+
17
+ This a Ruby implementation of [Robert Sedgewick](http://www.cs.princeton.edu/~rs/)'s and [Kevin Wayne](http://www.cs.princeton.edu/~wayne/contact/)'s [weighted quick-union algorithm with path compression](http://algs4.cs.princeton.edu/15uf/UF.java.html). Credit goes to these two authors of the book [Algorithms](http://www.amazon.com/gp/product/032157351X/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&tag=algs4-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=032157351X) and to the many computer scientists that have contributed to this algorithm in the past decades.
18
+
19
+ ## Installation
20
+
21
+ Add this line to your application's Gemfile:
22
+
23
+ gem 'union_find'
24
+
25
+ And then execute:
26
+
27
+ $ bundle
28
+
29
+ Or install it yourself as:
30
+
31
+ $ gem install union_find
32
+
33
+ ## Usage
34
+
35
+ Create a new instance of `UnionFind` and pass in an array of items:
36
+
37
+ ```ruby
38
+ union_find = UnionFind::UnionFind.new(['Grandfather', 'Father', 'Daughter', 'Single Person'])
39
+ ```
40
+
41
+ Connect items (in any order):
42
+
43
+ ```ruby
44
+ union_find.union('Grandfather', 'Father')
45
+ union_find.union('Father', 'Daughter')
46
+ ```
47
+
48
+ Check whether two items are connected (in any order):
49
+
50
+ ```ruby
51
+ union_find.connected?('Grandfather', 'Daughter')
52
+ => true
53
+ union_find.connected?('Daughter', 'Father')
54
+ => true
55
+ union_find.connected?('Grandfather', 'Single Person')
56
+ => false
57
+ ```
58
+
59
+ Check how many isolated items there are. In this example, there are 2, namely the family the family tree (Grandfather - Father - Daugther) and the Single Person:
60
+
61
+ ```ruby
62
+ union_find.count_isolated_components
63
+ => 2
64
+ ```
65
+
66
+ ## Contributing
67
+
68
+ 1. Fork it ( https://github.com/[my-github-username]/union_find/fork )
69
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
70
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
71
+ 4. Push to the branch (`git push origin my-new-feature`)
72
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,3 @@
1
+ module UnionFind
2
+ VERSION = "0.0.1"
3
+ end
data/lib/union_find.rb ADDED
@@ -0,0 +1,106 @@
1
+ require 'union_find/version'
2
+
3
+ module UnionFind
4
+
5
+ # The UnionFind class represents a union-find data type
6
+ # (also known as the disjoint-sets data type).
7
+ # It supports the union and find operations,
8
+ # along with a connected operation for determinig whether
9
+ # two sites in the same component are connected and a count operation that
10
+ # returns the total number of components.
11
+
12
+ # This implementation uses weighted quick union by rank with path compression
13
+ # by halving.
14
+
15
+ # Initializing a data structure with number_of_components sites takes linear time.
16
+ # Afterwards, the union, find, and connected
17
+ # operations take logarithmic time (in the worst case) and the
18
+ # count operation takes constant time.
19
+
20
+ # @author Robert Sedgewick
21
+ # @author Kevin Wayne
22
+ # @author Michael Imstepf
23
+ # @see http://algs4.cs.princeton.edu/15uf/UF.java.html
24
+ # @see http://algs4.cs.princeton.edu/15uf/
25
+ class UnionFind
26
+
27
+ # Initializes an empty union-find data structure with
28
+ # n isolated components 0 through n-1.
29
+ # @param components [Array] components
30
+ # @raise [ArgumentError] if components.length < 1 or if components is not an Array
31
+ def initialize(components)
32
+ raise ArgumentError, 'input is not an Array' unless components.class == Array
33
+
34
+ components = components.uniq # remove duplicates
35
+ @number_of_isolated_components = components.length
36
+
37
+ raise ArgumentError, 'number of components is < 1' if @number_of_isolated_components < 1
38
+
39
+ @parent = {} # parent of i
40
+ @tree_size = {} # size of tree rooted at i (cannot be more than 31)
41
+ components.each do |component|
42
+ @parent[component] = component
43
+ @tree_size[component] = 1
44
+ end
45
+ end
46
+
47
+ # Returns the number of isolated components.
48
+ # @return [Interger] the number of components
49
+ def count_isolated_components
50
+ @number_of_isolated_components
51
+ end
52
+
53
+ # Returns the root of a component.
54
+ # @param component_id [Integer] the integer representing one component
55
+ # @return [Component] the root of the component
56
+ # @raise [IndexError] unless component exists
57
+ def find_root(component)
58
+ raise IndexError, 'component does not exist' unless @parent[component]
59
+
60
+ while component != @parent[component] # stop at the top node where component id == parent id
61
+ @parent[component] = @parent[@parent[component]] # path compression by halving
62
+ component = @parent[component]
63
+ end
64
+
65
+ return component
66
+ end
67
+
68
+ # Connect root of component 1 with root of component 2
69
+ # by attaching smaller subtree root node with larger tree.
70
+ # If both trees have the same size, the root of the second
71
+ # component becomes a child of the root of the first component.
72
+ # @param component_1_id [Integer] the integer representing one component
73
+ # @param component_2_id [Integer] the integer representing the other component
74
+ # @return [Component, NilClass] the root of the larger tree or the root of the first component if both have the same tree size or nil if no connection has been made
75
+ def union(component_1, component_2)
76
+ root_component_1 = find_root(component_1)
77
+ root_component_2 = find_root(component_2)
78
+
79
+ return nil if root_component_1 == root_component_2
80
+
81
+ # make smaller root point to larger one
82
+ if @tree_size[root_component_1] < @tree_size[root_component_2]
83
+ @parent[root_component_1] = root_component_2
84
+ root = root_component_2
85
+ @tree_size[root_component_2] += @tree_size[root_component_1]
86
+ else
87
+ @parent[root_component_2] = root_component_1
88
+ root = root_component_1
89
+ @tree_size[root_component_1] += @tree_size[root_component_2]
90
+ end
91
+
92
+ @number_of_isolated_components -= 1
93
+
94
+ root
95
+ end
96
+
97
+ # Do two components share the same root?
98
+ # @param component_1 [Integer] the integer representing one component
99
+ # @param component_2 [Integer] the integer representing the other component
100
+ # @return [Boolean]
101
+ def connected?(component_1, component_2)
102
+ find_root(component_1) == find_root(component_2)
103
+ end
104
+ end
105
+
106
+ end
@@ -0,0 +1,23 @@
1
+ require 'union_find'
2
+ require 'pry' # to use binding.pry
3
+
4
+ RSpec.configure do |config|
5
+ # Run specs in random order to surface order dependencies. If you find an
6
+ # order dependency and want to debug it, you can fix the order by providing
7
+ # the seed, which is printed after each run.
8
+ # --seed 1234
9
+ config.order = 'random'
10
+
11
+ # when a focus tag is present in RSpec, only run tests with focus tag: http://railscasts.com/episodes/285-spork
12
+ config.filter_run focus: true
13
+ config.run_all_when_everything_filtered = true
14
+ end
15
+
16
+ def create_family_tree(union_find)
17
+ union_find.union('Grandfather', 'Father')
18
+ union_find.union('Grandfather', 'Mother')
19
+ union_find.union('Mother', 'Son')
20
+ union_find.union('Father', 'Daughter')
21
+
22
+ union_find
23
+ end
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+ random = Random.new
3
+
4
+ describe UnionFind::UnionFind do
5
+ # 1 family and 1 single person
6
+ people = ['Grandfather', 'Father', 'Mother', 'Son', 'Daughter', 'Single']
7
+
8
+ describe '#initialize' do
9
+ context 'when no components are provided' do
10
+ it 'raises an exception' do
11
+ expect {UnionFind::UnionFind.new()}.to raise_exception(ArgumentError)
12
+ end
13
+ end
14
+
15
+ context 'when components in form other than Array are provided' do
16
+ it 'raises an exception' do
17
+ expect {UnionFind::UnionFind.new('Some Person')}.to raise_exception(ArgumentError)
18
+ end
19
+ end
20
+ end
21
+
22
+ describe '#find_root' do
23
+ union_find = UnionFind::UnionFind.new(people)
24
+
25
+ context 'when component does not exist' do
26
+ it 'raises an exception' do
27
+ expect {union_find.find_root('Some Person')}.to raise_exception(IndexError)
28
+ end
29
+ end
30
+
31
+ context 'when component exists' do
32
+ context 'when component has no parent' do
33
+ it 'returns same component' do
34
+ expect(union_find.find_root('Single')).to eq 'Single'
35
+ end
36
+ end
37
+
38
+ context 'when component has parent' do
39
+ create_family_tree(union_find)
40
+
41
+ it 'returns parent' do
42
+ expect(union_find.find_root('Daughter')).to eq 'Grandfather'
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ describe '#union' do
49
+ context 'when one component gets connected to itself' do
50
+ union_find = UnionFind::UnionFind.new(people)
51
+
52
+ it 'returns the component' do
53
+ expect(union_find.union('Grandfather', 'Grandfather')).to be_nil
54
+ end
55
+ end
56
+
57
+ context 'when one unconnected component gets connected to another unconnected component' do
58
+ union_find = UnionFind::UnionFind.new(people)
59
+
60
+ it 'returns the first component' do
61
+ expect(union_find.union('Grandfather', 'Father')).to eq 'Grandfather'
62
+ end
63
+ end
64
+
65
+ context 'when one unconnected component gets connected to a connected component' do
66
+ union_find = UnionFind::UnionFind.new(people)
67
+ create_family_tree(union_find)
68
+
69
+ it 'connects and returns the root of the larger tree' do
70
+ expect(union_find.union('Single', 'Father')).to eq 'Grandfather'
71
+ expect(union_find.connected?('Father', 'Single')).to be_truthy
72
+ expect(union_find.find_root('Father')).to eq 'Grandfather'
73
+ expect(union_find.find_root('Single')).to eq 'Grandfather'
74
+ end
75
+ end
76
+ end
77
+
78
+ describe '#connected?' do
79
+ union_find = UnionFind::UnionFind.new(people)
80
+ create_family_tree(union_find)
81
+
82
+ context 'when two components are not connected' do
83
+ it 'returns false' do
84
+ expect(union_find.connected?('Father', 'Single')).to be_falsey
85
+ end
86
+ end
87
+
88
+ context 'when two components are the same' do
89
+ it 'returns false' do
90
+ expect(union_find.connected?('Father', 'Father')).to be_truthy
91
+ end
92
+ end
93
+
94
+ context 'when two components are connected' do
95
+ it 'returns false' do
96
+ expect(union_find.connected?('Grandfather', 'Daughter')).to be_truthy
97
+ end
98
+ end
99
+ end
100
+
101
+ describe '#count_isolated_components' do
102
+ context 'when no connections have been made' do
103
+ union_find = UnionFind::UnionFind.new(people)
104
+
105
+ it 'returns number of components' do
106
+ expect(union_find.count_isolated_components).to eq people.size
107
+ end
108
+ end
109
+
110
+ context 'when connections have been made' do
111
+ union_find = UnionFind::UnionFind.new(people)
112
+ create_family_tree(union_find)
113
+
114
+ it 'returns number of components' do
115
+ expect(union_find.count_isolated_components).to eq 2
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'union_find/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "union_find"
8
+ spec.version = UnionFind::VERSION
9
+ spec.authors = ["Michael Imstepf"]
10
+ spec.email = ["michael.imstepf@gmail.com"]
11
+ spec.summary = %q{Weighted quick-union algorithm with path compression.}
12
+ spec.description = %q{Union Find is an algorithm that uses a disjoint-set data structure. It allows us to efficiently connect any items of a given list and to efficiently check whether two items of this list are connected (any degree of separation) or not.}
13
+ spec.homepage = "https://github.com/michaelimstepf/union-find"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "pry"
25
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: union_find
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Michael Imstepf
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Union Find is an algorithm that uses a disjoint-set data structure. It
70
+ allows us to efficiently connect any items of a given list and to efficiently check
71
+ whether two items of this list are connected (any degree of separation) or not.
72
+ email:
73
+ - michael.imstepf@gmail.com
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - ".gitignore"
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - lib/union_find.rb
84
+ - lib/union_find/version.rb
85
+ - spec/spec_helper.rb
86
+ - spec/union_find_spec.rb
87
+ - union_find.gemspec
88
+ homepage: https://github.com/michaelimstepf/union-find
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.2.2
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Weighted quick-union algorithm with path compression.
112
+ test_files:
113
+ - spec/spec_helper.rb
114
+ - spec/union_find_spec.rb