dancing-links 0.1 → 0.1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,46 @@
1
+ This module provides an implementation of the "dancing links" algorhthm
2
+ to solve the "exact cover" problem.
3
+
4
+ The "exact cover" problem:
5
+ - Given a set of vectors containing 0's and 1's:
6
+ - Find a subset of these vectors that collectively contain one and only one 1 in each and every column.
7
+
8
+ For example the follwing set of vectors:
9
+ - A = [ 0, 1, 0]
10
+ - B = [ 1, 1, 0]
11
+ - C = [ 1, 0, 0]
12
+ - D = [ 0, 0, 1]
13
+
14
+ Has two solutions:
15
+ - A = [ 0, 1, 0]
16
+ - C = [ 1, 0, 0]
17
+ - D = [ 0, 0, 1]
18
+
19
+ and
20
+ - B = [ 1, 1, 0]
21
+ - D = [ 0, 0, 1]
22
+
23
+ A better description of the problem can be found here:
24
+ http://en.wikipedia.org/wiki/Exact_cover
25
+
26
+ The "dancing links" algorithm is a commonly used solution for this problem and
27
+ was found by Donald Knuth. The algorithm involves the construction of a
28
+ sparse matrix containing nodes that are doubly-linked both horizontally and
29
+ vertically. The matrix itself simply facilitates the depth-first search
30
+ (aka backtracking) part of the algorithm.
31
+
32
+ The importance of the doubly-linked nodes are that they allow for quick
33
+ removal/restoration of rows/columns of nodes, which is exactly what a
34
+ backtracking algorithm for the "exact cover" problem needs.
35
+
36
+ horizontal removal:
37
+ - node.left.right = node.right
38
+ - node.right.left = node.left
39
+
40
+ horizontal restoration:
41
+ - node.left.right = node
42
+ - node.right.left = node
43
+
44
+ A better description of the algorithm can be found here:
45
+ http://en.wikipedia.org/wiki/Dancing_Links
46
+
@@ -0,0 +1,34 @@
1
+ require 'rubygems'
2
+ require 'rubygems/package_task'
3
+ require 'rdoc/task'
4
+
5
+ task :default => [:rdoc, :gem]
6
+
7
+ PKG_FILES = FileList['lib/**/*.rb','[A-Z]*']
8
+
9
+ gem_spec = Gem::Specification.new do |s|
10
+ s.platform = Gem::Platform::RUBY
11
+ s.summary = 'An implementation of "Dancing Links".'
12
+ s.name = "dancing-links"
13
+ s.version = "0.1.0.1"
14
+ s.authors = ["Justin W Smith"]
15
+ s.require_path = 'lib'
16
+ s.email = 'justin dot w dot smith @t gmail.com'
17
+ s.files = PKG_FILES
18
+ s.homepage = 'https://rubygems.org/gems/dancing-links'
19
+ s.has_rdoc = true
20
+ s.rubyforge_project = 'sudoku-gtk'
21
+ s.description = 'An implementation of the "dancing links" algorithm to solve the "exact cover" problem.'
22
+ end
23
+
24
+ Rake::RDocTask.new do |rd|
25
+ rd.main = "README.rdoc"
26
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
27
+ end
28
+
29
+
30
+ Gem::PackageTask.new(gem_spec) do |t|
31
+ t.need_zip = true
32
+ t.need_tar = true
33
+ end
34
+
@@ -19,56 +19,6 @@
19
19
  # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
20
 
21
21
 
22
- =begin rdoc
23
-
24
- This module provides an implementation of the "dancing links" algorhthm
25
- to solve the "exact cover" problem.
26
-
27
- The "exact cover" problem:
28
- - Given a set of vectors containing 0's and 1's:
29
- - Find a subset of these vectors that collectively contain one and only one 1 in each and every column.
30
-
31
- For example the follwing set of vectors:
32
- - A = [ 0, 1, 0]
33
- - B = [ 1, 1, 0]
34
- - C = [ 1, 0, 0]
35
- - D = [ 0, 0, 1]
36
-
37
- Has two solutions:
38
- - A = [ 0, 1, 0]
39
- - C = [ 1, 0, 0]
40
- - D = [ 0, 0, 1]
41
-
42
- and
43
- - B = [ 1, 1, 0]
44
- - D = [ 0, 0, 1]
45
-
46
- A better description of the problem can be found here:
47
- http://en.wikipedia.org/wiki/Exact_cover
48
-
49
- The "dancing links" algorithm is a commonly used solution for this problem, and
50
- was found by Donald Knuth. The algorithm involves the construction of a
51
- sparse matrix containing nodes that are doubly-linked both horizontally and
52
- vertically. The matrix itself simply facilitates the depth-first search
53
- (aka backtracking) part of the algorithm.
54
-
55
- The importance of the doubly-linked nodes are that they allow for quick
56
- removal/restoration of rows/columns of nodes, which is exactly what a
57
- backtracking algorithm for the "exact cover" problem needs.
58
-
59
- horizontal removal:
60
- - node.left.right = node.right
61
- - node.right.left = node.left
62
-
63
- horizontal restoration:
64
- - node.left.right = node
65
- - node.right.left = node
66
-
67
- A better description of the algorithm can be found here:
68
- http://en.wikipedia.org/wiki/Dancing_Links
69
-
70
- =end
71
-
72
22
  module DancingLinks
73
23
 
74
24
  private
metadata CHANGED
@@ -1,39 +1,48 @@
1
- --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.10
3
- specification_version: 1
1
+ --- !ruby/object:Gem::Specification
4
2
  name: dancing-links
5
- version: !ruby/object:Gem::Version
6
- version: "0.1"
7
- date: 2006-01-28
8
- summary: Implementation of the "Dancing Links" algorithm.
9
- require_paths:
10
- - lib
11
- email: justin dot w dot smith at gmail dot com
12
- homepage: http://rubyforge.org/projects/sudoku-gtk/
13
- rubyforge_project:
14
- description: Dancing-links is an implementation of the "Dancing Links" algorthm to solve the "Exact Cover" problem. Algorithm found by Donald Knuth.
15
- autorequire: dancing_links
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: true
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- -
22
- - ">"
23
- - !ruby/object:Gem::Version
24
- version: 0.0.0
25
- version:
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.1
5
+ prerelease:
26
6
  platform: ruby
27
- authors: []
28
- files:
29
- - lib/dancing_links.rb
30
- - lib/array_combs_perms.rb
31
- test_files:
32
- - test/tc_dancing_links.rb
33
- rdoc_options: []
34
- extra_rdoc_files: []
7
+ authors:
8
+ - Justin W Smith
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-27 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: An implementation of the "dancing links" algorithm to solve the "exact
15
+ cover" problem.
16
+ email: justin dot w dot smith @t gmail.com
35
17
  executables: []
36
18
  extensions: []
37
- requirements:
38
- - none
39
- dependencies: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/dancing_links.rb
22
+ - Rakefile
23
+ - README.rdoc
24
+ homepage: https://rubygems.org/gems/dancing-links
25
+ licenses: []
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ! '>='
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ requirements: []
43
+ rubyforge_project: sudoku-gtk
44
+ rubygems_version: 1.8.11
45
+ signing_key:
46
+ specification_version: 3
47
+ summary: An implementation of "Dancing Links".
48
+ test_files: []
@@ -1,59 +0,0 @@
1
-
2
- # Copyright (c) 2006 Justin W Smith
3
-
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy of this
5
- # software and associated documentation files (the "Software"), to deal in the Software
6
- # without restriction, including without limitation the rights to use, copy, modify,
7
- # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8
- # permit persons to whom the Software is furnished to do so, subject to the following
9
- # conditions:
10
-
11
- # The above copyright notice and this permission notice shall be included in all copies
12
- # or substantial portions of the Software.
13
-
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15
- # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
16
- # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
17
- # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
18
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
19
- # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
-
21
- class Array
22
- def to_s
23
- s = "["
24
- each_index do |i|
25
- s << " " + self[i].to_s
26
- s << ", " unless (i == self.length-1)
27
- end
28
- s << "]"
29
- end
30
-
31
- def comb_helper head, tail, &block
32
- if tail.length == 0
33
- block.call(head)
34
- else
35
- tail.each_index do |i|
36
- t = tail.clone
37
- t.delete_at(i)
38
- comb_helper head.clone << tail[i], t, &block
39
- end
40
- end
41
- end
42
-
43
- def combinations &block
44
- comb_helper [], self, &block
45
- end
46
-
47
- def permutations &block
48
- perm_helper [self[0]], self[1...self.length], &block
49
- end
50
-
51
- def perm_helper head, tail, &block
52
- block.call(head)
53
- t = tail.clone
54
- until t.empty?
55
- val = t.pop
56
- comb_helper head.clone << val, t, &block
57
- end
58
- end
59
- end
@@ -1,222 +0,0 @@
1
-
2
- # Copyright (c) 2006 Justin W Smith
3
-
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy of this
5
- # software and associated documentation files (the "Software"), to deal in the Software
6
- # without restriction, including without limitation the rights to use, copy, modify,
7
- # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8
- # permit persons to whom the Software is furnished to do so, subject to the following
9
- # conditions:
10
-
11
- # The above copyright notice and this permission notice shall be included in all copies
12
- # or substantial portions of the Software.
13
-
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15
- # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
16
- # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
17
- # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
18
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
19
- # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
-
21
-
22
- require 'test/unit'
23
- require 'lib/dancing_links'
24
-
25
- class Array
26
- def to_s
27
- s = "["
28
- each_index do |i|
29
- s << " " + self[i].to_s
30
- s << ", " unless (i == self.length-1)
31
- end
32
- s << "]"
33
- end
34
-
35
- def comb_helper head, tail, &block
36
- if tail.length == 0
37
- block.call(head)
38
- else
39
- tail.each_index do |i|
40
- t = tail.clone
41
- t.delete_at(i)
42
- comb_helper head.clone << tail[i], t, &block
43
- end
44
- end
45
- end
46
-
47
- def combinations &block
48
- comb_helper [], self, &block
49
- end
50
- end
51
-
52
- class TC_DancingLinks < Test::Unit::TestCase
53
- include DancingLinks
54
-
55
- def setup
56
- @a1 = [ [1, 0, 1], [0, 0, 1], [1, 1, 0], [ 1, 0, 0] ]
57
- @b1 = [ [ true, false, true],
58
- [false, false, true],
59
- [true, true, false],
60
- [true, false, false] ]
61
- @matrix1 = SparseMatrix.new convert_rows_fixnum_to_boolean(@a1)
62
- @s1 = [ [ 0, 0, 1], [ 1, 1, 0] ]
63
-
64
- @a2 = [ [1, 0, 1], [1, 1, 0], [0, 1, 1], [ 0, 0, 0] ]
65
- @e2 = [ [0, 0, 1] ]
66
- @matrix2 = SparseMatrix.new convert_rows_fixnum_to_boolean(@a2)
67
- @s2 = [ [0, 0, 1], [ 1, 1, 0] ]
68
-
69
- @a3 = [ [1, 0, 1], [1, 1, 0], [0, 1, 1], [ 1, 1, 1] ]
70
- @matrix3 = SparseMatrix.new convert_rows_fixnum_to_boolean(@a3)
71
- @s3 = [[1, 1, 1]]
72
-
73
- @a4 = [ [1, 0, 1], [1, 1, 0], [0, 1, 1], [ 0, 1, 0] ]
74
- @matrix4 = SparseMatrix.new convert_rows_fixnum_to_boolean(@a4)
75
- @s4 = [[1, 0, 1], [0, 1, 0]]
76
-
77
- @a5 = [ [1, 0, 1], [1, 1, 0], [1, 1, 1], [ 0, 1, 1] ]
78
- @matrix5 = SparseMatrix.new convert_rows_fixnum_to_boolean(@a4)
79
- @s5 = [[1, 1, 1]]
80
-
81
- @a6 = [ [1, 0, 1], [1, 0, 0], [1, 1, 1], [ 0, 1, 1], [1, 1, 0] ]
82
-
83
- @a7 = [ [0, 0, 1], [ 0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1] ]
84
- @s7 = [
85
- [ [ 1, 1, 1] ],
86
- [ [ 0, 1, 1], [ 1, 0, 0 ] ].sort,
87
- [ [ 1, 0, 1], [ 0, 1, 0 ] ].sort,
88
- [ [ 1, 1, 0], [ 0, 0, 1] ].sort,
89
- [ [ 0, 0, 1], [0, 1, 0], [1, 0, 0] ].sort ]
90
- end
91
-
92
- def test_convert_rows
93
- assert_equal(@a1, convert_rows_boolean_to_fixnum(@b1) )
94
- assert_equal(@b1, convert_rows_fixnum_to_boolean(@a1) )
95
- end
96
-
97
- def test_sparse_matrix
98
- root = @matrix1.root
99
- assert_equal(3, root.count)
100
- assert_equal(3, root.right.count)
101
- assert_equal(1, root.right.right.count)
102
- assert_equal(2, root.right.right.right.count)
103
- assert_equal(root.right.up.left, root.right.up)
104
- assert_equal(root.left.up.left, root.right.right.right.down.down)
105
- assert_equal(root.right.down.header, root.right)
106
- assert_equal(root.left.up.header, root.left)
107
- assert_equal(root.right.right.right.right, root)
108
- assert_equal(root.left.left.left.left, root)
109
-
110
- assert_equal(0, root.left.down.row_num)
111
- assert_equal(1, root.left.down.down.row_num)
112
- assert_equal(2, root.right.right.down.row_num)
113
- assert_equal(3, root.right.up.row_num)
114
-
115
- assert_equal(0, root.right.index)
116
- assert_equal(3, root.right.count)
117
- assert_equal(1, root.right.right.index)
118
- assert_equal(1, root.right.right.count)
119
- assert_equal(2, root.right.right.right.index)
120
- assert_equal(2, root.right.right.right.count)
121
-
122
- end
123
-
124
- def test_remove_rows_for_header
125
- root = @matrix1.root
126
- remove_rows_for_header root.right
127
- assert_equal( 0, root.right.count)
128
- assert_equal( 0, root.right.right.count)
129
- assert_equal( 1, root.right.right.right.count)
130
- assert_equal( root.right.right.right, root.left)
131
- assert_equal( root.left.down, root.right.right.right.up)
132
- assert_equal( 1, root.left.down.row_num)
133
- end
134
-
135
- def test_restore_rows_for_header
136
- root = @matrix1.root
137
- restore_rows_for_header( remove_rows_for_header( root.right))
138
- test_sparse_matrix
139
- end
140
-
141
- def test_select_row
142
- root = @matrix1.root
143
- header_stk, row_stk_stk = select_row root.right.right.down
144
-
145
- assert_equal( 1, root.count)
146
- assert_equal( 1, root.right.count)
147
- assert_equal( 2, root.right.index )
148
- assert_equal( root.right.down, root.left.down)
149
- assert_equal( root.right.down.down, root.left)
150
- assert_equal( root.right.down.down, root.left)
151
-
152
- end
153
-
154
- def test_restore_selected_row
155
-
156
- root = @matrix1.root
157
- restore_selected_row( *select_row( root.right.right.down))
158
- test_sparse_matrix
159
- restore_selected_row( *select_row( root.right.down))
160
- test_sparse_matrix
161
- restore_selected_row( *select_row( root.left.down))
162
- test_sparse_matrix
163
- restore_selected_row( *select_row( root.left.up))
164
- test_sparse_matrix
165
- restore_selected_row( *select_row( root.right.up))
166
- test_sparse_matrix
167
- restore_selected_row( *select_row( root.left.up))
168
- test_sparse_matrix
169
-
170
- header_stk, row_stk_stk = select_row( root.right.right.down)
171
- restore_selected_row( *select_row( root.right.down) )
172
- restore_selected_row( header_stk, row_stk_stk)
173
- test_sparse_matrix
174
-
175
- end
176
-
177
- def test_min_column
178
- root = @matrix1.root
179
- header = min_column root
180
- assert_equal( root.right.right, header)
181
- end
182
-
183
- def test_solve_exact_cover
184
- s = convert_rows_boolean_to_fixnum(solve_exact_cover( @b1))
185
- assert_equal(@s1.sort, s.sort)
186
-
187
- s = solve_exact_cover( convert_rows_fixnum_to_boolean(@a2))
188
- assert_equal(0, s)
189
-
190
- s = convert_rows_boolean_to_fixnum(solve_exact_cover( convert_rows_fixnum_to_boolean(@a2), convert_rows_fixnum_to_boolean(@e2)))
191
- assert_equal(@s2.sort, s.sort)
192
-
193
- s = convert_rows_boolean_to_fixnum(solve_exact_cover( convert_rows_fixnum_to_boolean(@a3)))
194
- assert_equal(@s3.sort, s.sort)
195
-
196
- s = convert_rows_boolean_to_fixnum(solve_exact_cover( convert_rows_fixnum_to_boolean(@a4)))
197
- assert_equal(@s4.sort, s.sort)
198
-
199
- s = convert_rows_boolean_to_fixnum(solve_exact_cover( convert_rows_fixnum_to_boolean(@a5)))
200
- assert_equal(@s5.sort, s.sort)
201
-
202
- s = solve_exact_cover( convert_rows_fixnum_to_boolean(@a6)){false}
203
- assert_equal(2, s)
204
-
205
- end
206
-
207
- def test_solve_exact_cover2
208
-
209
- @a7.combinations do |comb|
210
- s = @s7.clone
211
- solve_exact_cover(convert_rows_fixnum_to_boolean(comb)) do |result|
212
- result = convert_rows_boolean_to_fixnum( result ).sort!
213
- assert( s.member?( result ), "\ncomb: #{comb.to_s}\nresult: #{result}\nsolutions left: #{s.to_s}")
214
- s.delete result
215
- false
216
- end
217
- assert_equal( [], s)
218
- end
219
-
220
- end
221
-
222
- end