combinatorics 0.3.0 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +1 -1
- data/.gemtest +0 -0
- data/.github/workflows/ruby.yml +28 -0
- data/.gitignore +4 -5
- data/Benchmarks.md +281 -0
- data/ChangeLog.md +42 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +1 -2
- data/README.md +178 -69
- data/Rakefile +7 -27
- data/benchmarks/cartesian_product.rb +18 -0
- data/benchmarks/choose.rb +19 -0
- data/benchmarks/derange.rb +18 -0
- data/benchmarks/list_comprehension.rb +20 -4
- data/benchmarks/permute.rb +18 -0
- data/benchmarks/power_set.rb +18 -0
- data/combinatorics.gemspec +54 -83
- data/gemspec.yml +25 -0
- data/lib/combinatorics/cartesian_product/cardinality.rb +45 -0
- data/lib/combinatorics/cartesian_product/extensions/array.rb +7 -0
- data/lib/combinatorics/cartesian_product/extensions/set.rb +9 -0
- data/lib/combinatorics/cartesian_product/extensions.rb +2 -0
- data/lib/combinatorics/cartesian_product/mixin.rb +57 -0
- data/lib/combinatorics/cartesian_product.rb +3 -0
- data/lib/combinatorics/choose/cardinality.rb +99 -0
- data/lib/combinatorics/choose/extensions/array.rb +5 -0
- data/lib/combinatorics/choose/extensions/set.rb +6 -0
- data/lib/combinatorics/choose/extensions.rb +2 -0
- data/lib/combinatorics/choose/mixin.rb +53 -0
- data/lib/combinatorics/choose.rb +3 -0
- data/lib/combinatorics/derange/cardinality.rb +23 -0
- data/lib/combinatorics/derange/extensions/array.rb +5 -0
- data/lib/combinatorics/derange/extensions.rb +1 -0
- data/lib/combinatorics/derange/mixin.rb +47 -0
- data/lib/combinatorics/derange.rb +3 -0
- data/lib/combinatorics/enumerator.rb +2 -0
- data/lib/combinatorics/extensions/math.rb +176 -0
- data/lib/combinatorics/extensions/range.rb +2 -2
- data/lib/combinatorics/generator.rb +7 -4
- data/lib/combinatorics/list_comprehension.rb +6 -1
- data/lib/combinatorics/permute/cardinality.rb +98 -0
- data/lib/combinatorics/permute/extensions/array.rb +7 -0
- data/lib/combinatorics/permute/extensions/set.rb +9 -0
- data/lib/combinatorics/permute/extensions.rb +2 -0
- data/lib/combinatorics/permute/mixin.rb +48 -0
- data/lib/combinatorics/permute.rb +3 -0
- data/lib/combinatorics/power_set/cardinality.rb +36 -0
- data/lib/combinatorics/power_set/mixin.rb +19 -22
- data/lib/combinatorics/power_set.rb +1 -0
- data/lib/combinatorics/version.rb +4 -0
- data/lib/combinatorics.rb +8 -0
- data/spec/cartesian_product/array_spec.rb +10 -0
- data/spec/cartesian_product/cardinality_spec.rb +64 -0
- data/spec/cartesian_product/mixin_examples.rb +98 -0
- data/spec/cartesian_product/set_spec.rb +10 -0
- data/spec/choose/array_spec.rb +9 -0
- data/spec/choose/cardinality_spec.rb +132 -0
- data/spec/choose/mixin_examples.rb +48 -0
- data/spec/choose/set_spec.rb +9 -0
- data/spec/combinatorics_spec.rb +5 -1
- data/spec/derange/array_spec.rb +10 -0
- data/spec/derange/cardinality_spec.rb +14 -0
- data/spec/derange/mixin_examples.rb +52 -0
- data/spec/enumerator_spec.rb +1 -1
- data/spec/extensions/math_spec.rb +100 -0
- data/spec/extensions/range_spec.rb +13 -13
- data/spec/generator_spec.rb +1 -1
- data/spec/list_comprehension_spec.rb +11 -11
- data/spec/permute/array_spec.rb +10 -0
- data/spec/permute/cardinality_spec.rb +146 -0
- data/spec/permute/mixin_examples.rb +42 -0
- data/spec/permute/set_spec.rb +10 -0
- data/spec/power_set/array_spec.rb +3 -2
- data/spec/power_set/cardinality_spec.rb +32 -0
- data/spec/power_set/mixin_examples.rb +17 -8
- data/spec/power_set/set_spec.rb +3 -2
- data/spec/spec_helper.rb +3 -3
- metadata +106 -104
- data/VERSION +0 -1
@@ -0,0 +1 @@
|
|
1
|
+
require 'combinatorics/derange/extensions/array'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Combinatorics
|
2
|
+
module Derange
|
3
|
+
#
|
4
|
+
# @author duper <super@manson.vistech.net>
|
5
|
+
#
|
6
|
+
# @since 0.4.0
|
7
|
+
#
|
8
|
+
module Mixin
|
9
|
+
#
|
10
|
+
# Calculate all derangements for an Enumerable object.
|
11
|
+
#
|
12
|
+
# @yield [derangement]
|
13
|
+
# If a block is given, it will be passed an Array representing
|
14
|
+
# an individual derangement from the full calculation.
|
15
|
+
#
|
16
|
+
# @yieldparam [Array] derangement
|
17
|
+
# One of the calculated derangements.
|
18
|
+
#
|
19
|
+
# @return [Enumerator]
|
20
|
+
# If no block is given, an Enumerator of all derangements will be
|
21
|
+
# returned.
|
22
|
+
#
|
23
|
+
# @example Produce the derangements of a three-element Array
|
24
|
+
# [1, 2, 3].derange.to_a
|
25
|
+
# # => [[2, 3, 1], [3, 1, 2]]
|
26
|
+
#
|
27
|
+
# @see http://en.wikipedia.org/wiki/Derangements
|
28
|
+
# @see http://mathworld.wolfram.com/Derangement.html
|
29
|
+
#
|
30
|
+
def derange
|
31
|
+
return enum_for(:derange) unless block_given?
|
32
|
+
|
33
|
+
if size <= 1
|
34
|
+
yield []
|
35
|
+
else
|
36
|
+
elements = self.to_a
|
37
|
+
|
38
|
+
elements.permutation do |x|
|
39
|
+
unless elements.each_with_index.any? { |e,i| e == x[i] }
|
40
|
+
yield x
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
#
|
2
|
+
# @author duper <super@manson.vistech.net>
|
3
|
+
#
|
4
|
+
module Math
|
5
|
+
#
|
6
|
+
# Mathematical summation (invokes a block for k = 1 until ++k = n).
|
7
|
+
#
|
8
|
+
# @param [Range] r
|
9
|
+
# Range containing the number of times to yield.
|
10
|
+
#
|
11
|
+
# @yield [i]
|
12
|
+
# The given block is expected to return an Integer, which is added to the
|
13
|
+
# total sum.
|
14
|
+
#
|
15
|
+
# @yieldparam [Integer] i
|
16
|
+
# An element from `r`.
|
17
|
+
#
|
18
|
+
# @return [Integer]
|
19
|
+
# Total sum after yielding for each element in `r`.
|
20
|
+
#
|
21
|
+
# @raise [TypeError]
|
22
|
+
# `r` must be a {Range}.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# sigma(1..4) { |i| i }
|
26
|
+
# # => 10
|
27
|
+
#
|
28
|
+
# @note
|
29
|
+
# "chalkboard" notation for summation is the capital Greek letter Sigma.
|
30
|
+
#
|
31
|
+
# @see http://en.wikipedia.org/wiki/Summation
|
32
|
+
#
|
33
|
+
# @since 0.4.0
|
34
|
+
#
|
35
|
+
def Math.sigma(r)
|
36
|
+
unless r.kind_of?(Range)
|
37
|
+
raise(TypeError,"r must be a Range")
|
38
|
+
end
|
39
|
+
|
40
|
+
k = 0
|
41
|
+
|
42
|
+
if block_given?
|
43
|
+
r.each { |n| k += yield n }
|
44
|
+
else
|
45
|
+
r.each { |n| k += n }
|
46
|
+
end
|
47
|
+
|
48
|
+
k
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# CamelCase alias for sigma (defined above)
|
53
|
+
#
|
54
|
+
# @see Math.sigma
|
55
|
+
#
|
56
|
+
# @since 0.4.0
|
57
|
+
#
|
58
|
+
def Math.Sigma(r)
|
59
|
+
Math.sigma(r)
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Pi notation for iterative product computations.
|
64
|
+
#
|
65
|
+
# @param [Range<Integer>] r
|
66
|
+
# Inclusive range of Integers.
|
67
|
+
#
|
68
|
+
# @yield [i]
|
69
|
+
# The given block will be passed elements of `r`. The return value from
|
70
|
+
# the block will be combined with the product.
|
71
|
+
#
|
72
|
+
# @yield [Integer] i
|
73
|
+
# An element from `r`.
|
74
|
+
#
|
75
|
+
# @return [Integer]
|
76
|
+
# The total product after iterating over each element in `r`.
|
77
|
+
#
|
78
|
+
# @raise [TypeError]
|
79
|
+
# `r` must be a Range.
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
# Math.pi(1..4)
|
83
|
+
# # => 24
|
84
|
+
#
|
85
|
+
# @see http://en.wikipedia.org/wiki/Pi_notation#Capital_Pi_notation
|
86
|
+
#
|
87
|
+
# @since 0.4.0
|
88
|
+
#
|
89
|
+
def Math.pi(r)
|
90
|
+
unless r.kind_of?(Range)
|
91
|
+
raise(TypeError,"r must be a Range")
|
92
|
+
end
|
93
|
+
|
94
|
+
k = 1
|
95
|
+
|
96
|
+
if block_given?
|
97
|
+
r.each { |n| k *= yield n }
|
98
|
+
else
|
99
|
+
r.each { |n| k *= n }
|
100
|
+
end
|
101
|
+
|
102
|
+
k
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# CamelCase alias for pi (defined above)
|
107
|
+
#
|
108
|
+
# @see Math.pi
|
109
|
+
#
|
110
|
+
# @since 0.4.0
|
111
|
+
#
|
112
|
+
def Math.Pi(r)
|
113
|
+
Math.pi(r)
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
# Subfactorial function for calculation of derangement cardinalities.
|
118
|
+
#
|
119
|
+
# @param [Fixnum] n
|
120
|
+
# The length of sequence.
|
121
|
+
#
|
122
|
+
# @raise [RangeError]
|
123
|
+
# `n` must be non-negative.
|
124
|
+
#
|
125
|
+
# @return [Integer]
|
126
|
+
# Cardinality of derangements set.
|
127
|
+
#
|
128
|
+
# @example
|
129
|
+
# subfactorial([1, 2, 3].size)
|
130
|
+
# # => 2
|
131
|
+
#
|
132
|
+
# @note The notation used in academia for subfactorial notation is "!n"
|
133
|
+
#
|
134
|
+
# @see http://mathworld.wolfram.com/Subfactorial.html
|
135
|
+
# @see Derange.cardinality
|
136
|
+
#
|
137
|
+
# @since 0.4.0
|
138
|
+
#
|
139
|
+
def Math.subfactorial(n)
|
140
|
+
if n >= 1 then ((Math.factorial(n) + 1) / Math::E).floor
|
141
|
+
elsif n == 0 then 1
|
142
|
+
else
|
143
|
+
raise(RangeError,"n must be non-negative")
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
#
|
148
|
+
# Apply the well-known factorial function to the given Integer.
|
149
|
+
#
|
150
|
+
# @param [Fixnum] x
|
151
|
+
# Positive integer to apply algebraic factorial function to.
|
152
|
+
#
|
153
|
+
# @return [Integer]
|
154
|
+
# Solution to factorial function as a whole number.
|
155
|
+
#
|
156
|
+
# @raise [RangeError]
|
157
|
+
# The given number must be non-negative.
|
158
|
+
#
|
159
|
+
# @example
|
160
|
+
# factorial(4)
|
161
|
+
# # => 24
|
162
|
+
#
|
163
|
+
# @note The factorial of zero equals one!
|
164
|
+
#
|
165
|
+
# @see http://en.wikipedia.org/wiki/Factorial
|
166
|
+
#
|
167
|
+
# @since 0.4.0
|
168
|
+
#
|
169
|
+
def Math.factorial(x=1)
|
170
|
+
if x >= 1 then pi(1..x)
|
171
|
+
elsif x == 0 then 1
|
172
|
+
else
|
173
|
+
raise(RangeError,"x must be non-negative")
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -53,7 +53,7 @@ class Range
|
|
53
53
|
|
54
54
|
self.first.upto(other.first) do |start|
|
55
55
|
self.last.upto(other.last) do |stop|
|
56
|
-
yield
|
56
|
+
yield start..stop
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
@@ -89,7 +89,7 @@ class Range
|
|
89
89
|
|
90
90
|
self.first.downto(other.first) do |start|
|
91
91
|
self.last.downto(other.last) do |stop|
|
92
|
-
yield
|
92
|
+
yield start..stop
|
93
93
|
end
|
94
94
|
end
|
95
95
|
end
|
@@ -1,10 +1,13 @@
|
|
1
|
-
require '
|
1
|
+
require 'enumerator'
|
2
|
+
require 'generator' if RUBY_VERSION < '1.9'
|
2
3
|
|
3
4
|
module Combinatorics
|
4
5
|
# auto-detects the `Generator` class.
|
5
|
-
Generator = if defined?(::
|
6
|
-
::Enumerator::Generator
|
7
|
-
elsif defined?(::Generator) # 1.8.7
|
6
|
+
Generator = if defined?(::Generator) # 1.8.7
|
8
7
|
::Generator
|
8
|
+
elsif defined?(::Enumerator::Generator) # >= 1.9.1
|
9
|
+
::Enumerator::Generator
|
10
|
+
else
|
11
|
+
raise(NameError,"unable to find the Generator class")
|
9
12
|
end
|
10
13
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'combinatorics/enumerator'
|
2
|
+
|
1
3
|
class Array
|
2
4
|
|
3
5
|
#
|
@@ -48,7 +50,10 @@ class Array
|
|
48
50
|
end
|
49
51
|
|
50
52
|
enums = self.map do |value|
|
51
|
-
|
53
|
+
case value
|
54
|
+
when Combinatorics::Enumerator
|
55
|
+
value
|
56
|
+
when Enumerable
|
52
57
|
value.enum_for
|
53
58
|
else
|
54
59
|
[value].enum_for
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'combinatorics/extensions/math'
|
2
|
+
|
3
|
+
module Combinatorics
|
4
|
+
#
|
5
|
+
# @author duper <super@manson.vistech.net>
|
6
|
+
#
|
7
|
+
# @since 0.4.0
|
8
|
+
#
|
9
|
+
module Permute
|
10
|
+
#
|
11
|
+
# Mathematically determine the number of elements in a r-permutations
|
12
|
+
# set.
|
13
|
+
#
|
14
|
+
# @param [Fixnum] n
|
15
|
+
# The number of elements in the input set.
|
16
|
+
#
|
17
|
+
# @param [Fixnum] r
|
18
|
+
# Cardinality of permuted subsets.
|
19
|
+
#
|
20
|
+
# @raise [RangeError]
|
21
|
+
# `n` must be non-negative.
|
22
|
+
#
|
23
|
+
# @raise [RangeError]
|
24
|
+
# `r` must be non-negative.
|
25
|
+
#
|
26
|
+
# @raise [RangeError]
|
27
|
+
# `r` must be less than or equal to `n`.
|
28
|
+
#
|
29
|
+
# @return [Fixnum]
|
30
|
+
# The product of the first `r` factors of `n`.
|
31
|
+
#
|
32
|
+
# @example Calculate total 4-permutations for a set whose cardinality is 6
|
33
|
+
# cardinality(6, 4)
|
34
|
+
# # => 360
|
35
|
+
#
|
36
|
+
# @see http://en.wikipedia.org/wiki/Permutations
|
37
|
+
#
|
38
|
+
# @note
|
39
|
+
# This function is well-known within fields of academic inquiry such as
|
40
|
+
# discrete mathematics and set theory. It is represented in "chalkboard"
|
41
|
+
# notation by the letter "P."
|
42
|
+
#
|
43
|
+
def self.cardinality(n,r=nil)
|
44
|
+
raise(RangeError,"n must be non-negative") if n < 0
|
45
|
+
|
46
|
+
case r
|
47
|
+
when 0 then 0
|
48
|
+
when nil then Math.factorial(n)
|
49
|
+
else
|
50
|
+
raise(RangeError,"r must be non-negative") if r < 0
|
51
|
+
raise(RangeError,"r must be less than or equal to n") if r > n
|
52
|
+
|
53
|
+
Math.factorial(n) / Math.factorial(n - r)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# @see cardinality
|
59
|
+
#
|
60
|
+
# @note In the study of set theory, permutations are often referenced by
|
61
|
+
# the name of an associated algorithm called "n-choose-r."
|
62
|
+
#
|
63
|
+
def self.N(n,r=nil); cardinality(n,r); end
|
64
|
+
def self.NR(n,r=nil); cardinality(n,r); end
|
65
|
+
def self.R(n,r=nil); cardinality(n,r); end
|
66
|
+
|
67
|
+
#
|
68
|
+
# Compute cardinality of all r-permutations for a set with cardinality c
|
69
|
+
#
|
70
|
+
# @param [Fixnum] c
|
71
|
+
# Input set cardinality.
|
72
|
+
#
|
73
|
+
# @return [Array]
|
74
|
+
# Elements are cardinalities for each subset `1 .. c`.
|
75
|
+
#
|
76
|
+
# @raise [RangeError]
|
77
|
+
# `c` must be non-negative.
|
78
|
+
#
|
79
|
+
# @example cardinality_all(4)
|
80
|
+
# # => [4, 3, 10, 1]
|
81
|
+
#
|
82
|
+
# @note sum of elements will equal `factorial(c)`
|
83
|
+
#
|
84
|
+
# @see http://en.wikipedia.org/wiki/Permutations
|
85
|
+
#
|
86
|
+
def self.cardinality_all(n,c=(1..n))
|
87
|
+
if n < 0
|
88
|
+
raise(RangeError,"n must be non-negative")
|
89
|
+
end
|
90
|
+
|
91
|
+
c.map { |r| cardinality(n,r) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.N_all(c); cardinality_all(c); end
|
95
|
+
def self.NR_all(c); cardinality_all(c); end
|
96
|
+
def self.R_all(c); cardinality_all(c); end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Combinatorics
|
2
|
+
module Permute
|
3
|
+
#
|
4
|
+
# @author duper <super@manson.vistech.net>
|
5
|
+
#
|
6
|
+
# @since 0.4.0
|
7
|
+
#
|
8
|
+
module Mixin
|
9
|
+
#
|
10
|
+
# Enumerate distinct r-permutations for a particular sequence of
|
11
|
+
# elements.
|
12
|
+
#
|
13
|
+
# @param [Fixnum] r
|
14
|
+
# Length of permuted subsets to return.
|
15
|
+
#
|
16
|
+
# @yield [permutation]
|
17
|
+
# If a block is given, it will be passed each k-permutation.
|
18
|
+
#
|
19
|
+
# @yieldparam [Array] permutation
|
20
|
+
# A k-permutation of the elements from `self`.
|
21
|
+
#
|
22
|
+
# @return [Enumerator]
|
23
|
+
# If no block is given, an Enumerator of the k-permutations of
|
24
|
+
# elements from `self` is returned.
|
25
|
+
#
|
26
|
+
# @raise [TypeError]
|
27
|
+
# `self` must be Enumerable.
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# [1, 2, 3].permute(2).to_a
|
31
|
+
# # => [[1, 2], [1, 3],
|
32
|
+
# # [2, 1], [2, 3],
|
33
|
+
# # [3, 1], [3, 2]]
|
34
|
+
#
|
35
|
+
# @see http://rubydoc.info/stdlib/core/Array#permutation-instance_method
|
36
|
+
#
|
37
|
+
def permute(r,&block)
|
38
|
+
return enum_for(:permute,r) unless block
|
39
|
+
|
40
|
+
unless kind_of?(Enumerable)
|
41
|
+
raise(TypeError,"#{inspect} must be Enumerable")
|
42
|
+
end
|
43
|
+
|
44
|
+
self.to_a.permutation(r,&block)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'combinatorics/extensions/math'
|
2
|
+
|
3
|
+
module Combinatorics
|
4
|
+
module PowerSet
|
5
|
+
#
|
6
|
+
# Get number of elements in power set from number of elements in input
|
7
|
+
# set.
|
8
|
+
#
|
9
|
+
# @param [Fixnum] n
|
10
|
+
# Number of elements input set.
|
11
|
+
#
|
12
|
+
# @return [Fixnum]
|
13
|
+
# Number of elements in power set.
|
14
|
+
#
|
15
|
+
# @see Math::factorial
|
16
|
+
# @see http://en.wikipedia.org/wiki/Cardinality
|
17
|
+
#
|
18
|
+
# @note
|
19
|
+
# Cardinality of power set on an empty set equals `factorial(0)`
|
20
|
+
# equals 1.
|
21
|
+
#
|
22
|
+
def self.cardinality(n)
|
23
|
+
Math.factorial(n)
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Wrapper function for power set cardinality method defined above
|
28
|
+
#
|
29
|
+
# @note The letter `P' stands for the power set function in the context of
|
30
|
+
# statements regarding discrete mathematics.
|
31
|
+
#
|
32
|
+
def self.P(n)
|
33
|
+
cardinality(n)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,5 +1,8 @@
|
|
1
1
|
module Combinatorics
|
2
2
|
module PowerSet
|
3
|
+
#
|
4
|
+
# @author postmodern <postmodern.mod3@gmail.com>
|
5
|
+
#
|
3
6
|
module Mixin
|
4
7
|
#
|
5
8
|
# Calculates the power-set of an Enumerable object.
|
@@ -11,40 +14,34 @@ module Combinatorics
|
|
11
14
|
# @yieldparam [Array] subset
|
12
15
|
# A sub-set from the power-set.
|
13
16
|
#
|
14
|
-
# @return [
|
17
|
+
# @return [Enumerator]
|
15
18
|
# The power set.
|
16
19
|
#
|
17
|
-
# @example Power-set of an Array.
|
18
|
-
# [1,2,3].powerset
|
19
|
-
# # => [[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
|
20
|
-
#
|
21
20
|
# @example Power-set on a Set of strings.
|
22
|
-
# Set['abc', 'xyz', '123'].powerset
|
23
|
-
# # => [#<Set: {}>,
|
24
|
-
# #<Set: {"
|
25
|
-
# #<Set: {"
|
21
|
+
# Set['abc', 'xyz', '123'].powerset.to_a
|
22
|
+
# # => [#<Set: {}>,
|
23
|
+
# #<Set: {"123"}>,
|
24
|
+
# #<Set: {"xyz"}>,
|
25
|
+
# #<Set: {"abc"}>,
|
26
|
+
# #<Set: {"xyz", "123"}>,
|
27
|
+
# #<Set: {"abc", "123"}>,
|
28
|
+
# #<Set: {"abc", "xyz"}>,
|
26
29
|
# #<Set: {"abc", "xyz", "123"}>]
|
27
30
|
#
|
28
|
-
# @see http://johncarrino.net/blog/2006/08/11/powerset-in-ruby/
|
29
|
-
#
|
30
31
|
def powerset
|
31
|
-
|
32
|
-
sub_set = []
|
32
|
+
return enum_for(:powerset) unless block_given?
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
yield new_set if block_given?
|
34
|
+
elements = self.to_a
|
35
|
+
elements.uniq!
|
37
36
|
|
38
|
-
|
39
|
-
|
37
|
+
0.upto(elements.length) do |k|
|
38
|
+
elements.combination(k) do |subset|
|
39
|
+
yield Set.new(subset)
|
40
40
|
end
|
41
|
-
|
42
|
-
sub_set
|
43
41
|
end
|
44
42
|
end
|
45
43
|
|
46
|
-
alias
|
47
|
-
|
44
|
+
alias power_set powerset
|
48
45
|
end
|
49
46
|
end
|
50
47
|
end
|
data/lib/combinatorics.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
require 'combinatorics/cartesian_product'
|
2
|
+
require 'combinatorics/choose'
|
3
|
+
require 'combinatorics/derange'
|
1
4
|
require 'combinatorics/extensions'
|
2
5
|
require 'combinatorics/list_comprehension'
|
3
6
|
require 'combinatorics/power_set'
|
7
|
+
require 'combinatorics/permute'
|
8
|
+
require 'combinatorics/version'
|
9
|
+
|
10
|
+
# @todo inversions (permutation mutations)
|
11
|
+
# @todo set families (approximate power set subsets)
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'combinatorics/cartesian_product'
|
3
|
+
|
4
|
+
describe CartesianProduct do
|
5
|
+
subject { CartesianProduct }
|
6
|
+
|
7
|
+
describe "cardinality" do
|
8
|
+
it "should return 1 for cardinality(1, 1)" do
|
9
|
+
expect(subject.cardinality(1, 1)).to eq(1)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should return 2 for cardinality(1, 2)" do
|
13
|
+
expect(subject.cardinality(1, 2)).to eq(2)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should return 2 for cardinality(2, 1)" do
|
17
|
+
expect(subject.cardinality(2, 1)).to eq(2)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return 4 for cardinality(2, 2)" do
|
21
|
+
expect(subject.cardinality(2, 2)).to eq(4)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should return 3 for cardinality(3, 1)" do
|
25
|
+
expect(subject.cardinality(3, 1)).to eq(3)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return 3 for cardinality(1, 3)" do
|
29
|
+
expect(subject.cardinality(1, 3)).to eq(3)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return 6 for cardinality(2, 3)" do
|
33
|
+
expect(subject.cardinality(2, 3)).to eq(6)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should return 6 for cardinality(3, 2)" do
|
37
|
+
expect(subject.cardinality(3, 2)).to eq(6)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should return 9 for cardinality(3, 3)" do
|
41
|
+
expect(subject.cardinality(3, 3)).to eq(9)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should raise RangeError if c1 is negative" do
|
45
|
+
expect { subject.cardinality(-1, 1) }.to raise_error(RangeError)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should raise RangeError if c2 is negative" do
|
49
|
+
expect { subject.cardinality(1, -1) }.to raise_error(RangeError)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should raise RangeError if c1 is zero" do
|
53
|
+
expect { subject.cardinality(0, 1) }.to raise_error(RangeError)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should raise RangeError if c2 is zero" do
|
57
|
+
expect { subject.cardinality(1, 0) }.to raise_error(RangeError)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should wrap cardinality with CartesianProduct.X" do
|
62
|
+
should respond_to(:X)
|
63
|
+
end
|
64
|
+
end
|