permutation 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (11) hide show
  1. data/CHANGES +11 -0
  2. data/GPL +340 -0
  3. data/README.en +35 -0
  4. data/Rakefile +78 -0
  5. data/VERSION +1 -0
  6. data/examples/tsp.rb +56 -0
  7. data/install.rb +12 -0
  8. data/lib/permutation.rb +474 -0
  9. data/make_doc.rb +6 -0
  10. data/test/test.rb +247 -0
  11. metadata +52 -0
data/CHANGES ADDED
@@ -0,0 +1,11 @@
1
+ 2004-07-16 (0.1.3)
2
+ * Moved gemspec into Rakefile
3
+ * Cleaned up a bit.
4
+ 2004-07-16 (0.1.2)
5
+ * Added Permutation.for factory method for convenience.
6
+ * Raising ArgumentError instead of simple strings.
7
+ 2004-07-03 (0.1.1)
8
+ * First Rubyforge version.
9
+ * Supports rubygems now.
10
+ 2004-04-29 (0.1.0)
11
+ * Initial version
data/GPL ADDED
@@ -0,0 +1,340 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Library General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ <one line to give the program's name and a brief idea of what it does.>
294
+ Copyright (C) <year> <name of author>
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License
307
+ along with this program; if not, write to the Free Software
308
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
309
+
310
+
311
+ Also add information on how to contact you by electronic and paper mail.
312
+
313
+ If the program is interactive, make it output a short notice like this
314
+ when it starts in an interactive mode:
315
+
316
+ Gnomovision version 69, Copyright (C) year name of author
317
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
318
+ This is free software, and you are welcome to redistribute it
319
+ under certain conditions; type `show c' for details.
320
+
321
+ The hypothetical commands `show w' and `show c' should show the appropriate
322
+ parts of the General Public License. Of course, the commands you use may
323
+ be called something other than `show w' and `show c'; they could even be
324
+ mouse-clicks or menu items--whatever suits your program.
325
+
326
+ You should also get your employer (if you work as a programmer) or your
327
+ school, if any, to sign a "copyright disclaimer" for the program, if
328
+ necessary. Here is a sample; alter the names:
329
+
330
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
331
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
332
+
333
+ <signature of Ty Coon>, 1 April 1989
334
+ Ty Coon, President of Vice
335
+
336
+ This General Public License does not permit incorporating your program into
337
+ proprietary programs. If your program is a subroutine library, you may
338
+ consider it more useful to permit linking proprietary applications with the
339
+ library. If this is what you want to do, use the GNU Library General
340
+ Public License instead of this License.
data/README.en ADDED
@@ -0,0 +1,35 @@
1
+ Installation
2
+ ============
3
+
4
+ Just type into the command line as root:
5
+
6
+ # ruby install.rb
7
+
8
+ Or if you already use rubygems (you should!) just type and rubygems fetches
9
+ the gem and installs it for you:
10
+
11
+ # gem install permutation
12
+
13
+ Documentation
14
+ =============
15
+
16
+ To create the documentation of this module, type
17
+
18
+ $ ruby make_doc.rb
19
+
20
+ and the API documentation is generated by your rdoc command in
21
+ the doc/ sub-directory.
22
+
23
+ In the examples direcotry is a small example that solves the
24
+ Traveling Salesman Problem (TSP) with this library.
25
+
26
+ Author
27
+ ======
28
+
29
+ Florian Frank <flori@ping.de>
30
+
31
+ License
32
+ =======
33
+
34
+ GNU General Public License (GPL)
35
+
data/Rakefile ADDED
@@ -0,0 +1,78 @@
1
+ require 'rake/gempackagetask'
2
+ require 'rbconfig'
3
+
4
+ include Config
5
+
6
+ PKG_NAME = 'permutation'
7
+ PKG_VERSION = File.read('VERSION').chomp
8
+ PKG_FILES = Dir.glob("**/*").delete_if { |item|
9
+ item.include?("CVS") or item.include?("pkg")
10
+ }
11
+
12
+ desc "Installing library"
13
+ task :install do
14
+ ruby 'install.rb'
15
+ end
16
+
17
+ desc "Creating documentation"
18
+ task :doc do
19
+ ruby 'make_doc.rb'
20
+ end
21
+
22
+ desc "Testing library"
23
+ task :test do
24
+ ruby %{-Ilib test/test.rb}
25
+ end
26
+
27
+ spec = Gem::Specification.new do |s|
28
+
29
+ #### Basic information.
30
+
31
+ s.name = 'permutation'
32
+ s.version = PKG_VERSION
33
+ s.summary = 'Permutation library in pure Ruby'
34
+ s.description = ""
35
+
36
+ #### Dependencies and requirements.
37
+
38
+ #s.add_dependency('log4r', '> 1.0.4')
39
+ #s.requirements << ""
40
+
41
+ s.files = PKG_FILES
42
+
43
+ #### C code extensions.
44
+
45
+ #s.extensions << "ext/extconf.rb"
46
+
47
+ #### Load-time details: library and application (you will need one or both).
48
+
49
+ s.require_path = 'lib' # Use these for libraries.
50
+ s.autorequire = 'permutation'
51
+
52
+ #s.bindir = "bin" # Use these for applications.
53
+ #s.executables = ["bla.rb"]
54
+ #s.default_executable = "bla.rb"
55
+
56
+ #### Documentation and testing.
57
+
58
+ s.has_rdoc = true
59
+ #s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a
60
+ #s.rdoc_options <<
61
+ # '--title' << 'Rake -- Ruby Make' <<
62
+ # '--main' << 'README' <<
63
+ # '--line-numbers'
64
+ s.test_files << 'test/test.rb'
65
+
66
+ #### Author and project details.
67
+
68
+ s.author = "Florian Frank"
69
+ s.email = "flori@ping.de"
70
+ s.homepage = "http://permutation.rubyforge.org"
71
+ s.rubyforge_project = "permutation"
72
+ end
73
+
74
+ Rake::GemPackageTask.new(spec) do |pkg|
75
+ pkg.need_tar = true
76
+ pkg.package_files += PKG_FILES
77
+ end
78
+ # vim: set et sw=2 ts=2:
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.3
data/examples/tsp.rb ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Solves the Traveling Salesmen Problem: A minimal length tour through n
4
+ # cities (and returning to the first city) is computed -- by brute
5
+ # force.
6
+
7
+ require 'permutation'
8
+
9
+ def make_matrix(n)
10
+ dist = Array.new(n) { [] }
11
+ for i in 0...n
12
+ for j in 0...n
13
+ case
14
+ when i == j then dist[i][j] = 0
15
+ when i < j then dist[i][j] = 1 + rand(999)
16
+ when i > j then dist[i][j] = dist[j][i]
17
+ end
18
+ end
19
+ end
20
+ dist
21
+ end
22
+
23
+ def pretty_distances(dist)
24
+ dist.map { |line| line.map { |x| "%3u" % x }.join(' ') }.join("\n")
25
+ end
26
+
27
+ def solution(p, dist)
28
+ (0...p.size).map { |i| dist[ p[i - 1] ][ p[i] ] }.join(' + ')
29
+ end
30
+
31
+ n = ARGV.empty? ? 7 : ARGV.shift.to_i
32
+ distances = make_matrix(n)
33
+ puts "Random distance matrix", pretty_distances(distances)
34
+ optimum, minsum = nil, nil
35
+ perm = Permutation.new(distances.size - 1)
36
+ puts "Searching through #{perm.last + 1} solutions..."
37
+ projection = (1...distances.size).to_a
38
+ perm.each! do |p|
39
+ tour = p.project(projection)
40
+ # Little trick: We always start in city 0:
41
+ tour.unshift(0)
42
+ sum = 0
43
+ 0.upto(tour.size - 1) do |i|
44
+ sum += distances[ tour[i - 1] ][ tour[i] ]
45
+ end
46
+ if $DEBUG
47
+ puts "Computed tour #{tour.inspect} with sum = #{sum} = " +
48
+ "#{solution(tour, distances)}."
49
+ end
50
+ if not minsum or minsum > sum
51
+ optimum, minsum = tour, sum
52
+ end
53
+ end
54
+ puts "Optimal tour is #{optimum.inspect} with sum = #{minsum} = " +
55
+ "#{solution(optimum, distances)}."
56
+ # vim: set et sw=4 ts=4:
data/install.rb ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbconfig'
4
+ require 'fileutils'
5
+ include FileUtils::Verbose
6
+
7
+ include Config
8
+
9
+ file = 'lib/permutation.rb'
10
+ dest = CONFIG["sitelibdir"]
11
+ install(file, dest)
12
+ # vim: set et sw=4 ts=4:
@@ -0,0 +1,474 @@
1
+ # = permutation.rb - Permutation class for Ruby
2
+ #
3
+ # == Author
4
+ #
5
+ # Florian Frank mailto:flori@ping.de
6
+ #
7
+ # == License
8
+ #
9
+ # This is free software; you can redistribute it and/or modify it under the
10
+ # terms of the GNU General Public License Version 2 as published by the Free
11
+ # Software Foundation: www.gnu.org/copyleft/gpl.html
12
+ #
13
+ # == Download
14
+ #
15
+ # The latest version of <b>permutation</b> can be found at
16
+ #
17
+ # * http://rubyforge.org/frs/?group_id=291
18
+ #
19
+ # The homepage of this library is located at
20
+ #
21
+ # * http://permutation.rubyforge.org
22
+ #
23
+ # == Description
24
+ #
25
+ # This class has a dual purpose: It can be used to create permutations
26
+ # of a given size and to do some simple computations with/on
27
+ # permutations. The instances of this class don't require much memory
28
+ # because they don't include the permutation as a data structure. They
29
+ # only save the information necessary to create the permutation if asked
30
+ # to do so.
31
+ #
32
+ # To generate permutations the ranking/unranking method described in [SS97]
33
+ # is used. Because of Ruby's Bignum arithmetic it is useful also
34
+ # for permutations of very large size.
35
+ #
36
+ # == Examples
37
+ #
38
+ # In this section some examples show what can be done with this class.
39
+ #
40
+ # Creating all permutations and project them on data:
41
+ #
42
+ # perm = Permutation.new(3)
43
+ # # => #<Permutation:0x57dc94 @last=5, @rank=0, @size=3>
44
+ # perm.map { |p| p.value }
45
+ # # => [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]]
46
+ # colors = [:r, :g, :b]
47
+ # # => [:r, :g, :b]
48
+ # perm.map { |p| p.project(colors) }
49
+ # # => [[:r, :g, :b], [:r, :b, :g], [:g, :r, :b], [:g, :b, :r], [:b, :r, :g],
50
+ # # [:b, :g, :r]]
51
+ # string = "abc"# => "abc"
52
+ # perm.map { |p| p.project(string) }
53
+ # # => ["abc", "acb", "bac", "bca", "cab", "cba"]
54
+ #
55
+ # Or perhaps more convenient to use:
56
+ #
57
+ # perm = Permutation.for("abc")
58
+ # perm.map { |p| p.project }
59
+ # # => ["abc", "acb", "bac", "bca", "cab", "cba"]
60
+ #
61
+ # Finding the successor and predecessor of Permutations or a
62
+ # certain Permutation for a given rank:
63
+ #
64
+ # perm = Permutation.new(7)
65
+ # # => #<Permutation:0x8453c @rank=0, @size=7, @last=5039>
66
+ # perm.succ!
67
+ # # => #<Permutation:0x8453c @rank=1, @size=7, @last=5039>
68
+ # perm.succ!
69
+ # # => #<Permutation:0x8453c @rank=2, @size=7, @last=5039>
70
+ # perm.succ!
71
+ # # => #<Permutation:0x8453c @rank=3, @size=7, @last=5039>
72
+ # perm.pred!
73
+ # # => #<Permutation:0x8453c @rank=2, @size=7, @last=5039>
74
+ # perm.rank = 3200
75
+ # # => 3200
76
+ # perm
77
+ # # => #<Permutation:0x8453c @rank=3200, @size=7, @last=5039>
78
+ # perm.value
79
+ # # => [4, 2, 5, 1, 3, 0, 6]
80
+ #
81
+ # Generating random Permutations
82
+ #
83
+ # perm = Permutation.new(10)
84
+ # # => #<Permutation:0x59f4c0 @rank=0, @size=10, @last=3628799>
85
+ # perm.random!.value
86
+ # # => [6, 4, 9, 7, 3, 5, 8, 1, 2, 0]
87
+ # perm.random!.value
88
+ # # => [3, 7, 6, 1, 4, 8, 9, 2, 5, 0]
89
+ # perm.random!.value
90
+ # # => [2, 8, 4, 9, 3, 5, 6, 7, 0, 1]
91
+ # perm.random!.project("ABCDEFGHIJ")
92
+ # # => "DFJGAEBCIH"
93
+ # perm.random!.project("ABCDEFGHIJ")
94
+ # # => "BFADEGHJCI"
95
+ #
96
+ # Performing some mathematical operations on/with Permutations
97
+ #
98
+ # p1 = Permutation.from_cycles([[1, 3, 2], [5, 7]], 10)
99
+ # # => #<Permutation:0x593594 @rank=80694, @size=10, @last=3628799>
100
+ # p2 = Permutation.from_value [3, 2, 0, 5, 6, 8, 9, 1, 4, 7]
101
+ # # => #<Permutation:0x5897b0 @rank=1171050, @size=10, @last=3628799>
102
+ # p3 = p1 * p2
103
+ # # => #<Permutation:0x586a88 @rank=769410, @size=10, @last=3628799>
104
+ # p3.value
105
+ # # => [2, 1, 0, 7, 6, 8, 9, 3, 4, 5]
106
+ # p3.cycles
107
+ # # => [[0, 2], [3, 7], [4, 6, 9, 5, 8]]
108
+ # p4 = p1 * -p2
109
+ # # => #<Permutation:0x581a10 @rank=534725, @size=10, @last=3628799>
110
+ # p4.value
111
+ # # => [1, 5, 3, 0, 8, 2, 4, 9, 7, 6]
112
+ # p4.cycles
113
+ # # => [[0, 1, 5, 2, 3], [4, 8, 7, 9, 6]]
114
+ # id = p1 * -p1
115
+ # # => #<Permutation:0x583a7c @rank=0, @size=10, @last=3628799>
116
+ #
117
+ # == References
118
+ #
119
+ # [SS97] The Algorithm Design Manual, Steven S. Skiena, Telos/Springer, 1997.
120
+ #
121
+
122
+ class Permutation
123
+
124
+ include Enumerable
125
+ include Comparable
126
+
127
+ # Creates a new Permutation instance of <code>size</code>
128
+ # (and ranked with <code>rank</code>).
129
+ def initialize(size, rank = 0)
130
+ @size, @rank = size, rank
131
+ @last = factorial(size) - 1
132
+ end
133
+
134
+ # Creates a new Permutation instance from the Array
135
+ # <code>indices</code>, that should consist of a permutation of Fixnums
136
+ # in the range of <code>0</code> and <code>indices.size - 1</code>. This is
137
+ # for example the result of a call to the Permutation#value method.
138
+ def self.from_value(indices)
139
+ obj = new(indices.size)
140
+ obj.instance_eval do
141
+ self.rank = rank_indices(indices)
142
+ end
143
+ obj
144
+ end
145
+
146
+ # Creates a new Permutation instance from the Array of Arrays
147
+ # <code>cycles</code>. This is for example the result of a
148
+ # call to the Permutation#cycles method .
149
+ def self.from_cycles(cycles, max = 0)
150
+ indices = Array.new(max)
151
+ cycles.each do |cycle|
152
+ cycle.empty? and next
153
+ for i in 0...cycle.size
154
+ indices[ cycle[i - 1] ] = cycle[i]
155
+ end
156
+ end
157
+ indices.each_with_index { |r, i| r or indices[i] = i }
158
+ from_value(indices)
159
+ end
160
+
161
+ # A permutation instance of size collection.size is created with
162
+ # collection as the default Permutation#project data object. A
163
+ # collection should respond to size, [], and []=. The Permutation
164
+ # instance will default to rank 0 if none is given.
165
+ def self.for(collection, rank = 0)
166
+ perm = new(collection.size, rank)
167
+ perm.instance_variable_set(:@collection, collection)
168
+ perm
169
+ end
170
+
171
+ # Returns the size of this permutation, a Fixnum.
172
+ attr_reader :size
173
+
174
+ # Returns the size of this permutation, a Fixnum in the range
175
+ # of 0 and Permutation#last.
176
+ attr_reader :rank
177
+
178
+ # Returns the rank of the last ranked Permutation of size
179
+ # Permutation#size .
180
+ attr_reader :last
181
+
182
+ # Assigns <code>m</code> to the rank attribute of this Permutation
183
+ # instance. That implies that the indices produced by a call to the
184
+ # Permutation#value method of this instance is the permutation ranked with
185
+ # this new <code>rank</code>.
186
+ def rank=(m)
187
+ last = factorial(size) - 1
188
+ while m > last do m -= last end
189
+ while m < 0 do m += last end
190
+ @rank = m
191
+ end
192
+
193
+ # Returns the indices in the range of 0 to Permutation#size - 1
194
+ # of this permutation that is ranked with Permutation#rank.
195
+ #
196
+ # <b>Example:</b>
197
+ # perm = Permutation.new(6, 312)
198
+ # # => #<Permutation:0x6ae34 @last=719, @rank=312, @size=6>
199
+ # perm.value
200
+ # # => [2, 4, 0, 1, 3, 5]
201
+ def value
202
+ unrank_indices(@rank)
203
+ end
204
+
205
+ # Returns the projection of this instance's Permutation#value
206
+ # into the <code>data</code> object that should respond to
207
+ # the #[] method. If this Permutation inbstance was created
208
+ # with Permutation.for the collection used to create
209
+ # it is used as a data object.
210
+ #
211
+ # <b>Example:</b>
212
+ # perm = Permutation.new(6, 312)
213
+ # # => #<Permutation:0x6ae34 @last=719, @rank=312, @size=6>
214
+ # perm.project("abcdef")
215
+ # # => "ceabdf"
216
+ def project(data = @collection)
217
+ data or raise ArgumentError.new("a collection is required to project")
218
+ raise ArgumentError.new("data size is != #{size}!") if data.size != size
219
+ projection = data.clone
220
+ value.each_with_index { |i, j| projection[j] = data[i] }
221
+ projection
222
+ end
223
+
224
+ # Switches this instances to the next ranked Permutation.
225
+ # If this was the Permutation#last permutation it wraps around
226
+ # the first (<code>rank == 0</code>) permutation.
227
+ def next!
228
+ @rank += 1
229
+ last = factorial(size) - 1
230
+ @rank = 0 if @rank > last
231
+ self
232
+ end
233
+
234
+ alias succ! next!
235
+
236
+ # Returns the next ranked Permutation instance.
237
+ # If this instance is the Permutation#last permutation it returns the first
238
+ # (<code>rank == 0</code>) permutation.
239
+ def next
240
+ clone.next!
241
+ end
242
+
243
+ alias succ next
244
+
245
+ # Switches this instances to the previously ranked Permutation.
246
+ # If this was the first permutation it returns the last (<code>rank ==
247
+ # Permutation#last</code>) permutation.
248
+ def pred!
249
+ @rank -= 1
250
+ last = factorial(size) - 1
251
+ @rank = last if @rank < 0
252
+ self
253
+ end
254
+
255
+ # Returns the previously ranked Permutation. If this was the first
256
+ # permutation it returns the last (<code>rank == Permutation#last</code>)
257
+ # permutation.
258
+ def pred
259
+ clone.pred!
260
+ end
261
+
262
+ # Switches this Permutation instance to random permutation
263
+ # of size Permutation#size.
264
+ def random!
265
+ new_rank = rand(last).to_i
266
+ self.rank = new_rank
267
+ self
268
+ end
269
+
270
+ # Returns a random Permutation instance # of size Permutation#size.
271
+ def random
272
+ clone.random!
273
+ end
274
+
275
+ # Iterates over all permutations of size Permutation#size starting with the
276
+ # first (<code>rank == 0</code>) ranked permutation and ending with the
277
+ # last (<code>rank == Permutation#last</code>) ranked permutation while
278
+ # yielding to a freshly created Permutation instance for every iteration
279
+ # step.
280
+ #
281
+ # The mixed in methods from the Enumerable module rely on this method.
282
+ def each # :yields: perm
283
+ 0.upto(last) do |r|
284
+ klon = clone
285
+ klon.rank = r
286
+ yield klon
287
+ end
288
+ end
289
+
290
+ # Does something similar to Permutation#each. It doesn't create new
291
+ # instances (less overhead) for every iteration step, but yields to a
292
+ # modified self instead. This is useful if one only wants to call a
293
+ # method on the yielded value and work with the result of this call. It's
294
+ # not a good idea to put the yielded values in a data structure because the
295
+ # will all reference the same (this!) instance. If you want to do this
296
+ # use Permutation#each.
297
+ def each!
298
+ old_rank = rank
299
+ 0.upto(last) do |r|
300
+ self.rank = r
301
+ yield self
302
+ end
303
+ self.rank = old_rank
304
+ end
305
+
306
+ # Compares to Permutation instances according to their Permutation#size
307
+ # and the Permutation#rank.
308
+ #
309
+ # The mixed in methods from the Comparable module rely on this method.
310
+ def <=>(other)
311
+ size <=> other.size.zero? || rank <=> other.rank
312
+ end
313
+
314
+ # Returns true if this Permutation instance and the other have the same
315
+ # value, that is both Permutation instances have the same Permutation#size
316
+ # and the same Permutation#rank.
317
+ def eql?(other)
318
+ self.class == other.class && size == other.size && rank == other.rank
319
+ end
320
+
321
+ alias == eql?
322
+
323
+ # Computes a unique hash value for this Permutation instance.
324
+ def hash
325
+ size.hash ^ rank.hash
326
+ end
327
+
328
+ # Switchtes this Permutation instance to the inverted permutation.
329
+ # (See Permutation#compose for an example.)
330
+ def invert!
331
+ indices = unrank_indices(rank)
332
+ inverted = Array.new(size)
333
+ for i in 0...size
334
+ inverted[indices[i]] = i
335
+ end
336
+ self.rank = rank_indices(inverted)
337
+ self
338
+ end
339
+
340
+ # Returns the inverted Permutation of this Permutation instance.
341
+ # (See Permutation#compose for an example.)
342
+ def invert
343
+ clone.invert!
344
+ end
345
+
346
+ alias -@ invert
347
+
348
+ # Compose this Permutation instance and the other to
349
+ # a new Permutation. Note that a permutation
350
+ # composed with it's inverted permutation yields
351
+ # the identity permutation, the permutation with rank 0.
352
+ #
353
+ # <b>Example:</b>
354
+ # p1 = Permutation.new(5, 42)
355
+ # # => #<Permutation:0x75370 @last=119, @rank=42, @size=5>
356
+ # p2 = p1.invert
357
+ # # => #<Permutation:0x653d0 @last=119, @rank=51, @size=5>
358
+ # p1.compose(p2)
359
+ # => #<Permutation:0x639a4 @last=119, @rank=0, @size=5>
360
+ # Or a little nicer to look at:
361
+ # p1 * -p1
362
+ # # => #<Permutation:0x62004 @last=119, @rank=0, @size=5>
363
+ def compose(other)
364
+ size == other.size or raise ArgumentError.new(
365
+ "permutations of unequal sizes cannot be composed!")
366
+ indices = self.value
367
+ composed = other.value.map { |i| indices[i] }
368
+ klon = clone
369
+ klon.rank = rank_indices(composed)
370
+ klon
371
+ end
372
+
373
+ alias * compose
374
+
375
+ # Returns the cycles representation of this Permutation instance.
376
+ # The return value of this method can be used to create a
377
+ # new Permutation instance with the Permutation.from_cycles method.
378
+ #
379
+ # <b>Example:</b>
380
+ # perm = Permutation.new(7, 23)
381
+ # # => #<Permutation:0x58541c @last=5039, @rank=23, @size=7>
382
+ # perm.cycles
383
+ # # => [[3, 6], [4, 5]]
384
+ def cycles
385
+ perm = value
386
+ result = [[]]
387
+ seen = {}
388
+ current = nil
389
+ until seen == perm.size
390
+ current or current = perm.find { |x| !seen[x] }
391
+ break unless current
392
+ if seen[current]
393
+ current = nil
394
+ result << []
395
+ else
396
+ seen[current] = true
397
+ result[-1] << current
398
+ current = perm[current]
399
+ end
400
+ end
401
+ result.pop
402
+ result.select { |c| c.size > 1 }.map do |c|
403
+ min_index = c.index(c.min)
404
+ c[min_index..-1] + c[0...min_index]
405
+ end
406
+ end
407
+
408
+ # Returns the signum of this Permutation instance.
409
+ # It's -1 if this permutation is odd and 1 if it's
410
+ # an even permutation.
411
+ #
412
+ # A permutation is odd if it can be represented by an odd number of
413
+ # transpositions (cycles of length 2), or even if it can be represented of
414
+ # an even number of transpositions.
415
+ def signum
416
+ s = 1
417
+ cycles.each do |c|
418
+ c.size % 2 == 0 and s *= -1
419
+ end
420
+ s
421
+ end
422
+
423
+ alias sgn signum
424
+
425
+ # Returns true if this permutation is even, false otherwise.
426
+ def even?
427
+ signum == 1
428
+ end
429
+
430
+ # Returns true if this permutation is odd, false otherwise.
431
+ def odd?
432
+ signum == -1
433
+ end
434
+
435
+ private
436
+
437
+ @@factorial_cache = {}
438
+
439
+ def factorial(n)
440
+ f = @@factorial_cache[n] and return f
441
+ f = 1
442
+ for i in 2..n do f *= i end
443
+ @@factorial_cache[n] = f
444
+ end
445
+
446
+ def rank_indices(p)
447
+ result = 0
448
+ for i in 0...size
449
+ result += p[i] * factorial(size - i - 1)
450
+ for j in (i + 1)...size
451
+ p[j] -= 1 if p[j] > p[i]
452
+ end
453
+ end
454
+ result
455
+ end
456
+
457
+ def unrank_indices(m)
458
+ result = Array.new(size, 0)
459
+ for i in 0...size
460
+ f = factorial(i)
461
+ x = m % (f * (i + 1))
462
+ m -= x
463
+ x /= f
464
+ result[size - i - 1] = x
465
+ x -= 1
466
+ for j in (size - i)...size
467
+ result[j] += 1 if result[j] > x
468
+ end
469
+ end
470
+ result
471
+ end
472
+
473
+ end
474
+ # vim: set et sw=4 ts=4:
data/make_doc.rb ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $outdir = 'doc/'
4
+ puts "Creating documentation in '#$outdir'."
5
+ system "rdoc -d -o #$outdir lib/permutation.rb"
6
+ # vim: set et sw=4 ts=4:
data/test/test.rb ADDED
@@ -0,0 +1,247 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'permutation'
5
+
6
+ class TC_Permutation < Test::Unit::TestCase
7
+
8
+ def setup
9
+ @perms = (0..4).map { |i| Permutation.new(i) }
10
+ @perms_collections = [ "", "a", "ab", "abc", "abcd" ].map do |c|
11
+ Permutation.for(c)
12
+ end
13
+ @perms_each = [
14
+ [[]],
15
+ [[0]],
16
+ [[0, 1], [1, 0]],
17
+ [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]],
18
+ [[0, 1, 2, 3], [0, 1, 3, 2], [0, 2, 1, 3], [0, 2, 3, 1],
19
+ [0, 3, 1, 2], [0, 3, 2, 1], [1, 0, 2, 3], [1, 0, 3, 2],
20
+ [1, 2, 0, 3], [1, 2, 3, 0], [1, 3, 0, 2], [1, 3, 2, 0],
21
+ [2, 0, 1, 3], [2, 0, 3, 1], [2, 1, 0, 3], [2, 1, 3, 0],
22
+ [2, 3, 0, 1], [2, 3, 1, 0], [3, 0, 1, 2], [3, 0, 2, 1],
23
+ [3, 1, 0, 2], [3, 1, 2, 0], [3, 2, 0, 1], [3, 2, 1, 0]]
24
+ ]
25
+ @next_pred = [
26
+ [ [], [] ],
27
+ [ [ 0 ], [ 0 ] ],
28
+ [ [ 0, 1 ], [ 1, 0 ] ],
29
+ [ [ 1, 0 ], [ 0, 1 ] ],
30
+ [ [ 0, 1, 2 ], [ 0, 2, 1 ] ],
31
+ [ [ 0, 2, 1 ], [ 1, 0, 2 ] ],
32
+ [ [ 1, 0, 2 ], [ 1, 2, 0 ] ],
33
+ [ [ 1, 2, 0 ], [ 2, 0, 1 ] ],
34
+ [ [ 2, 0, 1 ], [ 2, 1, 0 ] ],
35
+ [ [ 2, 1, 0 ], [ 0, 1, 2 ] ],
36
+ ]
37
+ @projected = [
38
+ [ "" ],
39
+ [ "a" ],
40
+ [ "ab", "ba", ],
41
+ [ "abc", "acb", "bac", "bca", "cab", "cba" ],
42
+ [ "abcd", "abdc", "acbd", "acdb", "adbc", "adcb", "bacd",
43
+ "badc", "bcad", "bcda", "bdac", "bdca", "cabd", "cadb",
44
+ "cbad", "cbda", "cdab", "cdba", "dabc", "dacb", "dbac",
45
+ "dbca", "dcab", "dcba"]
46
+ ]
47
+ @products = [
48
+ {[0, 0]=>[]},
49
+ {[0, 0]=>[0]},
50
+ {[0, 0]=>[0, 1], [1, 1]=>[0, 1], [1, 0]=>[1, 0], [0, 1]=>[1, 0]},
51
+ {[2, 4]=>[2, 1, 0], [1, 2]=>[2, 0, 1], [0, 0]=>[0, 1, 2],
52
+ [5, 4]=>[0, 2, 1], [3, 3]=>[2, 0, 1], [2, 1]=>[1, 2, 0],
53
+ [0, 5]=>[2, 1, 0], [3, 5]=>[0, 2, 1], [1, 1]=>[0, 1, 2],
54
+ [0, 3]=>[1, 2, 0], [5, 3]=>[1, 0, 2], [4, 1]=>[2, 1, 0],
55
+ [3, 2]=>[2, 1, 0], [2, 0]=>[1, 0, 2], [0, 4]=>[2, 0, 1],
56
+ [3, 4]=>[0, 1, 2], [1, 0]=>[0, 2, 1], [0, 2]=>[1, 0, 2],
57
+ [5, 2]=>[1, 2, 0], [4, 0]=>[2, 0, 1], [3, 1]=>[1, 0, 2],
58
+ [2, 3]=>[0, 2, 1], [1, 5]=>[1, 2, 0], [4, 5]=>[1, 0, 2],
59
+ [5, 1]=>[2, 0, 1], [4, 3]=>[0, 1, 2], [3, 0]=>[1, 2, 0],
60
+ [2, 2]=>[0, 1, 2], [1, 4]=>[1, 0, 2], [4, 4]=>[1, 2, 0],
61
+ [5, 0]=>[2, 1, 0], [4, 2]=>[0, 2, 1], [2, 5]=>[2, 0, 1],
62
+ [1, 3]=>[2, 1, 0], [0, 1]=>[0, 2, 1], [5, 5]=>[0, 1, 2]}
63
+ ]
64
+ @cycles = [
65
+ [[]],
66
+ [[]],
67
+ [[], [[0, 1]]],
68
+ [[], [[1, 2]], [[0, 1]], [[0, 1, 2]], [[0, 2, 1]], [[0, 2]]],
69
+ [[], [[2, 3]], [[1, 2]], [[1, 2, 3]], [[1, 3, 2]], [[1, 3]],
70
+ [[0, 1]], [[0, 1], [2, 3]], [[0, 1, 2]], [[0, 1, 2, 3]],
71
+ [[0, 1, 3, 2]], [[0, 1, 3]], [[0, 2, 1]], [[0, 2, 3, 1]],
72
+ [[0, 2]], [[0, 2, 3]], [[0, 2], [1, 3]], [[0, 2, 1, 3]],
73
+ [[0, 3, 2, 1]], [[0, 3, 1]], [[0, 3, 2]], [[0, 3]],
74
+ [[0, 3, 1, 2]], [[0, 3], [1, 2]]]
75
+ ]
76
+ @signum = [
77
+ [1],
78
+ [1],
79
+ [1, -1],
80
+ [1, -1, -1, 1, 1, -1],
81
+ [1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1,
82
+ -1, 1, 1, -1, -1, 1]
83
+ ]
84
+ end
85
+
86
+ def test_created
87
+ factorial = 1
88
+ @perms.each_with_index do |p, i|
89
+ assert_equal(i, p.size)
90
+ assert_equal(factorial - 1, p.last)
91
+ factorial *= (i + 1)
92
+ end
93
+ end
94
+
95
+ def test_rank_assign
96
+ perm = Permutation.new(3)
97
+ perms = [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1],
98
+ [2, 1, 0]]
99
+ (-5...0).each do |i|
100
+ perm.rank = i
101
+ assert_equal(perms[i + 5], perm.value)
102
+ end
103
+ (-10...-5).each do |i|
104
+ perm.rank = i
105
+ assert_equal(perms[i + 10], perm.value)
106
+ end
107
+ (0..5).each do |i|
108
+ perm.rank = i
109
+ assert_equal(perms[i], perm.value)
110
+ end
111
+ (6..10).each do |i|
112
+ perm.rank = i
113
+ assert_equal(perms[i - 5], perm.value)
114
+ end
115
+ (11..15).each do |i|
116
+ perm.rank = i
117
+ assert_equal(perms[i - 10], perm.value)
118
+ end
119
+ end
120
+
121
+ def test_compare
122
+ perm1 = Permutation.new(3)
123
+ perm2 = Permutation.new(3)
124
+ perm3 = perm1.dup
125
+ perm4 = Permutation.new(3, 1)
126
+ assert(!perm1.equal?(perm2))
127
+ assert(perm1 == perm2)
128
+ assert(perm1.eql?(perm2))
129
+ assert_equal(0, perm1 <=> perm2)
130
+ assert_equal(perm1.hash, perm2.hash)
131
+ assert(!perm1.equal?(perm3))
132
+ assert(perm1 == perm3)
133
+ assert(perm1.eql?(perm3))
134
+ assert_equal(0, perm1 <=> perm3)
135
+ assert_equal(perm1.hash, perm3.hash)
136
+ assert(!perm1.equal?(perm4))
137
+ assert(perm1 != perm4)
138
+ assert(!perm1.eql?(perm4))
139
+ assert_equal(-1, perm1 <=> perm4)
140
+ assert_equal(1, perm4 <=> perm1)
141
+ assert(perm1 < perm4)
142
+ assert(perm4 > perm1)
143
+ assert(perm1.hash != perm4.hash)
144
+ perms = perm1.to_a
145
+ perms[1..-1].each_with_index do |p, i|
146
+ assert(p > perms[i])
147
+ assert_equal(1, p <=> perms[i])
148
+ assert(perms[i] < p)
149
+ assert_equal(-1, perms[i] <=> p)
150
+ end
151
+ end
152
+
153
+ def test_random
154
+ @perms_each.each_with_index do |perms, i|
155
+ perm = Permutation.new(i)
156
+ 24.times do
157
+ assert(perms.member?(perm.random.value))
158
+ end
159
+ end
160
+ end
161
+
162
+ def test_enumerable
163
+ @perms.each_with_index do |perm, i|
164
+ assert_equal(@perms_each[i], perm.map { |x| x.value })
165
+ end
166
+ @perms.each_with_index do |perm, i|
167
+ ary = []
168
+ old_rank = perm.rank
169
+ perm.each! { |x| ary << x.value }
170
+ assert_equal(@perms_each[i], ary)
171
+ assert_equal(old_rank, perm.rank)
172
+ end
173
+ end
174
+
175
+ def test_next
176
+ @next_pred.each do |before, after|
177
+ beforep = Permutation.from_value(before)
178
+ afterp = Permutation.from_value(after)
179
+ assert_equal(afterp, beforep.next)
180
+ assert_equal(beforep, afterp.pred)
181
+ assert_equal(afterp, beforep.succ)
182
+ end
183
+ end
184
+
185
+ def test_project
186
+ too_big = Array.new(10)
187
+ @perms.each_with_index do |perms, i|
188
+ assert_equal(@projected[i],
189
+ perms.map { |p| p.project(@projected[i][0]) })
190
+ assert_raises(ArgumentError) { perms.project }
191
+ assert_raises(ArgumentError) { perms.project(too_big) }
192
+ end
193
+ @perms_collections.each_with_index do |perms, i|
194
+ assert_equal(@projected[i], perms.map { |p| p.project })
195
+ end
196
+ end
197
+
198
+ def test_compose
199
+ too_big = Permutation.new(10)
200
+ @perms[0..3].each do |perm|
201
+ elements = perm.to_a
202
+ for i in 0...elements.size
203
+ for j in 0...elements.size
204
+ assert_equal(@products[perm.size][[i, j]],
205
+ (elements[i].compose(elements[j])).value)
206
+ assert_equal(@products[perm.size][[i, j]],
207
+ (elements[i] * elements[j]).value)
208
+ end
209
+ assert_raises(ArgumentError) { elements[i] * too_big }
210
+ assert_raises(ArgumentError) { elements[i].compose(too_big) }
211
+ end
212
+ end
213
+ end
214
+
215
+ def test_invert
216
+ @perms.each do |perm|
217
+ id = perm
218
+ perm.each do |p|
219
+ assert_equal(id.value, (p * p.invert).value)
220
+ assert_equal(id, p * p.invert)
221
+ assert_equal(id.value, (p * -p).value)
222
+ assert_equal(id, p * -p)
223
+ end
224
+ end
225
+ end
226
+
227
+ def test_cycles
228
+ @perms.each_with_index do |perm, i|
229
+ assert_equal(@cycles[i], perm.map { |p| p.cycles })
230
+ assert_equal(perm.to_a,
231
+ @cycles[i].map { |c| Permutation.from_cycles(c, i) })
232
+ end
233
+ end
234
+
235
+ def test_signum
236
+ @perms.each_with_index do |perm, i|
237
+ assert_equal(@signum[i], perm.map { |p| p.signum })
238
+ assert_equal(@signum[i], perm.map { |p| p.sgn })
239
+ assert_equal(@signum[i].map { |x| x == 1 },
240
+ perm.map { |p| p.even? })
241
+ assert_equal(@signum[i].map { |x| x == -1 },
242
+ perm.map { |p| p.odd? })
243
+ end
244
+ end
245
+
246
+ end
247
+ # vim: set et sw=4 ts=4:
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.11
3
+ specification_version: 1
4
+ name: permutation
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.3
7
+ date: 2005-10-05 00:00:00 +02:00
8
+ summary: Permutation library in pure Ruby
9
+ require_paths:
10
+ - lib
11
+ email: flori@ping.de
12
+ homepage: http://permutation.rubyforge.org
13
+ rubyforge_project: permutation
14
+ description: ''
15
+ autorequire: permutation
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:
26
+ platform: ruby
27
+ signing_key:
28
+ cert_chain:
29
+ authors:
30
+ - Florian Frank
31
+ files:
32
+ - examples
33
+ - install.rb
34
+ - GPL
35
+ - test
36
+ - Rakefile
37
+ - VERSION
38
+ - CHANGES
39
+ - lib
40
+ - make_doc.rb
41
+ - README.en
42
+ - examples/tsp.rb
43
+ - test/test.rb
44
+ - lib/permutation.rb
45
+ test_files:
46
+ - test/test.rb
47
+ rdoc_options: []
48
+ extra_rdoc_files: []
49
+ executables: []
50
+ extensions: []
51
+ requirements: []
52
+ dependencies: []