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
data/Rakefile
CHANGED
@@ -1,36 +1,16 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'rake'
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
require 'jeweler'
|
7
|
-
|
8
|
-
Jeweler::Tasks.new do |gem|
|
9
|
-
gem.name = 'combinatorics'
|
10
|
-
gem.summary = %Q{Bringing (more) Combinatorics to Ruby}
|
11
|
-
gem.description = %Q{A collection of modules and methods for performing Combinatoric calculations.}
|
12
|
-
gem.email = 'postmodern.mod3@gmail.com'
|
13
|
-
gem.homepage = 'http://github.com/postmodern/combinatorics'
|
14
|
-
gem.authors = ['Postmodern']
|
15
|
-
gem.add_development_dependency 'rspec', '~> 2.0.0'
|
16
|
-
gem.add_development_dependency 'yard', '~> 0.6.0'
|
17
|
-
gem.add_development_dependency 'jeweler', '~> 1.4.0'
|
18
|
-
end
|
19
|
-
Jeweler::GemcutterTasks.new
|
20
|
-
rescue LoadError
|
21
|
-
puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
|
22
|
-
end
|
3
|
+
require 'rubygems/tasks'
|
4
|
+
Gem::Tasks.new
|
23
5
|
|
24
6
|
require 'rspec/core/rake_task'
|
25
7
|
RSpec::Core::RakeTask.new
|
8
|
+
task :test => :spec
|
26
9
|
task :default => :spec
|
27
10
|
|
28
|
-
|
29
|
-
|
11
|
+
require 'yard'
|
12
|
+
YARD::Rake::YardocTask.new
|
30
13
|
|
31
|
-
|
32
|
-
|
33
|
-
task :yard do
|
34
|
-
abort 'YARD is not available. In order to run yard, you must: gem install yard'
|
35
|
-
end
|
14
|
+
task :benchmark do
|
15
|
+
Dir.glob('benchmarks/*.rb') { |script| ruby(script) }
|
36
16
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
|
4
|
+
lib_dir = File.join(root_dir,'lib')
|
5
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
6
|
+
|
7
|
+
require 'benchmark'
|
8
|
+
require 'combinatorics/cartesian_product'
|
9
|
+
|
10
|
+
Benchmark.bm(13) do |b|
|
11
|
+
[100, 200, 400, 800].each do |i|
|
12
|
+
n = (1..i).to_a
|
13
|
+
|
14
|
+
b.report("{#{i}} x {#{i}}") do
|
15
|
+
n.cartesian_product(n).to_a
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
|
4
|
+
lib_dir = File.join(root_dir,'lib')
|
5
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
6
|
+
|
7
|
+
require 'benchmark'
|
8
|
+
require 'combinatorics/choose'
|
9
|
+
|
10
|
+
Benchmark.bm do |b|
|
11
|
+
n = 20
|
12
|
+
array = (1..n).to_a
|
13
|
+
|
14
|
+
(1..n).each do |i|
|
15
|
+
b.report("n=#{n} k=#{i}") do
|
16
|
+
array.choose(i) { |s| }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
|
4
|
+
lib_dir = File.join(root_dir,'lib')
|
5
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
6
|
+
|
7
|
+
require 'benchmark'
|
8
|
+
require 'combinatorics/derange'
|
9
|
+
|
10
|
+
Benchmark.bm do |b|
|
11
|
+
(1..10).each do |n|
|
12
|
+
array = (1..n).to_a
|
13
|
+
|
14
|
+
b.report("n=#{n}") do
|
15
|
+
array.derange { |s| }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
1
|
root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
|
4
2
|
lib_dir = File.join(root_dir,'lib')
|
5
3
|
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
@@ -7,6 +5,24 @@ $LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
|
7
5
|
require 'benchmark'
|
8
6
|
require 'combinatorics/list_comprehension'
|
9
7
|
|
10
|
-
|
8
|
+
Benchmark.bm(12) do |b|
|
9
|
+
singleton_list = ([1] * 500)
|
10
|
+
single_enum_list = [1..100, 1]
|
11
|
+
depth_list = [1..100]
|
12
|
+
|
13
|
+
b.report('singleton:') do
|
14
|
+
singleton_list.comprehension.each { |list| list.last }
|
15
|
+
end
|
16
|
+
|
17
|
+
b.report('single-enum:') do
|
18
|
+
single_enum_list.comprehension.each { |list| list.last }
|
19
|
+
end
|
20
|
+
|
21
|
+
(1..3).each do |n|
|
22
|
+
deep_list = (depth_list * n)
|
11
23
|
|
12
|
-
|
24
|
+
b.report("depth #{n}:") do
|
25
|
+
deep_list.comprehension.each { |list| list.last }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
|
4
|
+
lib_dir = File.join(root_dir,'lib')
|
5
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
6
|
+
|
7
|
+
require 'benchmark'
|
8
|
+
require 'combinatorics/permute'
|
9
|
+
|
10
|
+
Benchmark.bm do |b|
|
11
|
+
array = (1..10).to_a
|
12
|
+
|
13
|
+
(1..10).each do |n|
|
14
|
+
b.report("r=#{n}") do
|
15
|
+
array.permute(n) { |s| }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
|
4
|
+
lib_dir = File.join(root_dir,'lib')
|
5
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
6
|
+
|
7
|
+
require 'benchmark'
|
8
|
+
require 'combinatorics/power_set'
|
9
|
+
|
10
|
+
Benchmark.bm do |b|
|
11
|
+
(10..20).each do |n|
|
12
|
+
array = (1..n).to_a
|
13
|
+
|
14
|
+
b.report("n=#{n}") do
|
15
|
+
array.power_set { |s| }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/combinatorics.gemspec
CHANGED
@@ -1,90 +1,61 @@
|
|
1
|
-
#
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
-
# -*- encoding: utf-8 -*-
|
1
|
+
# encoding: utf-8
|
5
2
|
|
6
|
-
|
7
|
-
s.name = %q{combinatorics}
|
8
|
-
s.version = "0.3.0"
|
3
|
+
require 'yaml'
|
9
4
|
|
10
|
-
|
11
|
-
|
12
|
-
s.date = %q{2010-10-17}
|
13
|
-
s.description = %q{A collection of modules and methods for performing Combinatoric calculations.}
|
14
|
-
s.email = %q{postmodern.mod3@gmail.com}
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"ChangeLog.md",
|
17
|
-
"LICENSE.txt",
|
18
|
-
"README.md"
|
19
|
-
]
|
20
|
-
s.files = [
|
21
|
-
".document",
|
22
|
-
".gitignore",
|
23
|
-
".rspec",
|
24
|
-
".yardopts",
|
25
|
-
"ChangeLog.md",
|
26
|
-
"LICENSE.txt",
|
27
|
-
"README.md",
|
28
|
-
"Rakefile",
|
29
|
-
"VERSION",
|
30
|
-
"benchmarks/list_comprehension.rb",
|
31
|
-
"combinatorics.gemspec",
|
32
|
-
"lib/combinatorics.rb",
|
33
|
-
"lib/combinatorics/enumerator.rb",
|
34
|
-
"lib/combinatorics/extensions.rb",
|
35
|
-
"lib/combinatorics/extensions/range.rb",
|
36
|
-
"lib/combinatorics/generator.rb",
|
37
|
-
"lib/combinatorics/list_comprehension.rb",
|
38
|
-
"lib/combinatorics/power_set.rb",
|
39
|
-
"lib/combinatorics/power_set/extensions.rb",
|
40
|
-
"lib/combinatorics/power_set/extensions/array.rb",
|
41
|
-
"lib/combinatorics/power_set/extensions/set.rb",
|
42
|
-
"lib/combinatorics/power_set/mixin.rb",
|
43
|
-
"spec/.rspec",
|
44
|
-
"spec/combinatorics_spec.rb",
|
45
|
-
"spec/enumerator_spec.rb",
|
46
|
-
"spec/extensions/range_spec.rb",
|
47
|
-
"spec/generator_spec.rb",
|
48
|
-
"spec/list_comprehension_spec.rb",
|
49
|
-
"spec/power_set/array_spec.rb",
|
50
|
-
"spec/power_set/mixin_examples.rb",
|
51
|
-
"spec/power_set/set_spec.rb",
|
52
|
-
"spec/spec_helper.rb"
|
53
|
-
]
|
54
|
-
s.homepage = %q{http://github.com/postmodern/combinatorics}
|
55
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
56
|
-
s.require_paths = ["lib"]
|
57
|
-
s.rubygems_version = %q{1.3.7}
|
58
|
-
s.summary = %q{Bringing (more) Combinatorics to Ruby}
|
59
|
-
s.test_files = [
|
60
|
-
"spec/enumerator_spec.rb",
|
61
|
-
"spec/combinatorics_spec.rb",
|
62
|
-
"spec/list_comprehension_spec.rb",
|
63
|
-
"spec/generator_spec.rb",
|
64
|
-
"spec/extensions/range_spec.rb",
|
65
|
-
"spec/spec_helper.rb",
|
66
|
-
"spec/power_set/set_spec.rb",
|
67
|
-
"spec/power_set/array_spec.rb",
|
68
|
-
"spec/power_set/mixin_examples.rb"
|
69
|
-
]
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gemspec = YAML.load_file('gemspec.yml')
|
70
7
|
|
71
|
-
|
72
|
-
|
73
|
-
|
8
|
+
gem.name = gemspec.fetch('name')
|
9
|
+
gem.version = gemspec.fetch('version') do
|
10
|
+
lib_dir = File.join(File.dirname(__FILE__),'lib')
|
11
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
74
12
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
13
|
+
require 'combinatorics/version'
|
14
|
+
Combinatorics::VERSION
|
15
|
+
end
|
16
|
+
|
17
|
+
gem.summary = gemspec['summary']
|
18
|
+
gem.description = gemspec['description']
|
19
|
+
gem.licenses = Array(gemspec['license'])
|
20
|
+
gem.authors = Array(gemspec['authors'])
|
21
|
+
gem.email = gemspec['email']
|
22
|
+
gem.homepage = gemspec['homepage']
|
23
|
+
gem.metadata = gemspec['metadata'] if gemspec['metadata']
|
24
|
+
|
25
|
+
glob = lambda { |patterns| gem.files & Dir[*patterns] }
|
26
|
+
|
27
|
+
gem.files = `git ls-files`.split($/)
|
28
|
+
gem.files = glob[gemspec['files']] if gemspec['files']
|
29
|
+
|
30
|
+
gem.executables = gemspec.fetch('executables') do
|
31
|
+
glob['bin/*'].map { |path| File.basename(path) }
|
32
|
+
end
|
33
|
+
gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
|
34
|
+
|
35
|
+
gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
|
36
|
+
gem.test_files = glob[gemspec['test_files'] || '{test/{**/}*_test.rb']
|
37
|
+
gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
|
38
|
+
|
39
|
+
gem.require_paths = Array(gemspec.fetch('require_paths') {
|
40
|
+
%w[ext lib].select { |dir| File.directory?(dir) }
|
41
|
+
})
|
42
|
+
|
43
|
+
gem.requirements = gemspec['requirements']
|
44
|
+
gem.required_ruby_version = gemspec['required_ruby_version']
|
45
|
+
gem.required_rubygems_version = gemspec['required_rubygems_version']
|
46
|
+
gem.post_install_message = gemspec['post_install_message']
|
47
|
+
|
48
|
+
split = lambda { |string| string.split(/,\s*/) }
|
49
|
+
|
50
|
+
if gemspec['dependencies']
|
51
|
+
gemspec['dependencies'].each do |name,versions|
|
52
|
+
gem.add_dependency(name,split[versions])
|
83
53
|
end
|
84
|
-
else
|
85
|
-
s.add_dependency(%q<rspec>, ["~> 2.0.0"])
|
86
|
-
s.add_dependency(%q<yard>, ["~> 0.6.0"])
|
87
|
-
s.add_dependency(%q<jeweler>, ["~> 1.4.0"])
|
88
54
|
end
|
89
|
-
end
|
90
55
|
|
56
|
+
if gemspec['development_dependencies']
|
57
|
+
gemspec['development_dependencies'].each do |name,versions|
|
58
|
+
gem.add_development_dependency(name,split[versions])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/gemspec.yml
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
name: combinatorics
|
2
|
+
summary: Bringing (more) Combinatorics to Ruby
|
3
|
+
description:
|
4
|
+
A collection of modules and methods for performing Combinatoric
|
5
|
+
calculations.
|
6
|
+
license: MIT
|
7
|
+
authors:
|
8
|
+
- Postmodern
|
9
|
+
- Duper
|
10
|
+
email:
|
11
|
+
- postmodern.mod3@gmail.com
|
12
|
+
- super@manson.vistech.net
|
13
|
+
homepage: https://github.com/postmodern/combinatorics#readme
|
14
|
+
has_yard: true
|
15
|
+
|
16
|
+
metadata:
|
17
|
+
documentation_uri: https://rubydoc.info/gems/combinatorics
|
18
|
+
source_code_uri: https://github.com/postmodern/combinatorics.rb
|
19
|
+
bug_tracker_uri: https://github.com/postmodern/combinatorics.rb/issues
|
20
|
+
changelog_uri: https://github.com/postmodern/combinatorics.rb/blob/master/ChangeLog.md
|
21
|
+
|
22
|
+
required_ruby_version: ">= 1.8.7"
|
23
|
+
|
24
|
+
development_dependencies:
|
25
|
+
bundler: ~> 2.0
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Combinatorics
|
2
|
+
#
|
3
|
+
# @author duper <super@manson.vistech.net>
|
4
|
+
#
|
5
|
+
# @since 0.4.0
|
6
|
+
#
|
7
|
+
module CartesianProduct
|
8
|
+
#
|
9
|
+
# Wrapper for Cartesian product cardinality method defined above
|
10
|
+
#
|
11
|
+
# @param [Fixnum] a
|
12
|
+
# Cardinality of first set.
|
13
|
+
#
|
14
|
+
# @param [Fixnum] b
|
15
|
+
# Cardinality of second set.
|
16
|
+
#
|
17
|
+
# @raise [RangeError]
|
18
|
+
# Inputs must be greater than zero.
|
19
|
+
#
|
20
|
+
# @return [Fixnum]
|
21
|
+
# Length of enumeration resulting from a Cartesian product.
|
22
|
+
#
|
23
|
+
# @example Calculate elements in Cartesian product of two equal-size sets
|
24
|
+
# cardinality(3, 4)
|
25
|
+
# # => 12
|
26
|
+
#
|
27
|
+
def self.cardinality(a,b)
|
28
|
+
if (a <= 0 || b <= 0)
|
29
|
+
raise(RangeError,"inputs must be greater than zero")
|
30
|
+
end
|
31
|
+
|
32
|
+
a * b
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# @note The letter `X' is scholastic notation for the Cartesian product
|
37
|
+
# set operation
|
38
|
+
#
|
39
|
+
# @see cardinality
|
40
|
+
#
|
41
|
+
def self.X(a,b)
|
42
|
+
cardinality(a,b)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'combinatorics/list_comprehension'
|
2
|
+
|
3
|
+
module Combinatorics
|
4
|
+
module CartesianProduct
|
5
|
+
#
|
6
|
+
# @author duper <super@manson.vistech.net>
|
7
|
+
#
|
8
|
+
# @since 0.4.0
|
9
|
+
#
|
10
|
+
module Mixin
|
11
|
+
#
|
12
|
+
# Calculates the Cartesian product of an Enumerable object.
|
13
|
+
#
|
14
|
+
# @yield [subset]
|
15
|
+
# If a block is given, it will be passed each individual subset
|
16
|
+
# element from the Cartesian product set as a whole.
|
17
|
+
#
|
18
|
+
# @yieldparam [Array] subset
|
19
|
+
# The sub-set from the Cartesian product.
|
20
|
+
#
|
21
|
+
# @return [Enumerator]
|
22
|
+
# Resulting Cartesian product set.
|
23
|
+
#
|
24
|
+
# @raise [TypeError]
|
25
|
+
# `other` must be Enumerable.
|
26
|
+
#
|
27
|
+
# @example Cartesian product of an Array
|
28
|
+
# [1, 2].cartesian_product([3, 4])
|
29
|
+
# # => [[1, 3], [2, 3], [1, 4], [2, 4]]
|
30
|
+
#
|
31
|
+
# @example Cartesian product over an Array of Strings
|
32
|
+
# ['a'].cartesian_product(['b', 'c', 'd']).to_a
|
33
|
+
# # => [["a", "b"], ["a", "c"], ["a", "d"]]
|
34
|
+
#
|
35
|
+
# @example Three-way Cartesian product operation
|
36
|
+
# [0, 1].cartesian_product([2, 3], [4, 5]).to_a
|
37
|
+
# # => [
|
38
|
+
# # [0, 2, 4], [0, 2, 5], [0, 3, 4], [0, 3, 5],
|
39
|
+
# # [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5],
|
40
|
+
# # ]
|
41
|
+
#
|
42
|
+
# @see http://en.wikipedia.org/wiki/Cartesian_product
|
43
|
+
#
|
44
|
+
def cartesian_product(*others,&block)
|
45
|
+
return enum_for(:cartesian_product,*others) unless block
|
46
|
+
|
47
|
+
# a single empty Set will result in an empty Set
|
48
|
+
return nil if (empty? || others.any?(&:empty?))
|
49
|
+
|
50
|
+
Array[self,*others].comprehension(&block)
|
51
|
+
end
|
52
|
+
|
53
|
+
alias cartprod cartesian_product
|
54
|
+
alias cartesian cartesian_product
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,99 @@
|
|
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 Choose
|
10
|
+
#
|
11
|
+
# Compute the number of elements in a subset of given size
|
12
|
+
#
|
13
|
+
# @param [Fixnum] n
|
14
|
+
# The number of elements in the input set
|
15
|
+
#
|
16
|
+
# @param [Fixnum] r
|
17
|
+
# Cardinality of subsets to choose
|
18
|
+
#
|
19
|
+
# @raise [RangeError]
|
20
|
+
# `n` must be non-negative.
|
21
|
+
#
|
22
|
+
# @raise [RangeError]
|
23
|
+
# `r` must be non-negative.
|
24
|
+
#
|
25
|
+
# @raise [RangeError]
|
26
|
+
# `r` must be less than or equal to `n`.
|
27
|
+
#
|
28
|
+
# @return [Fixnum]
|
29
|
+
# The binomial coefficient for "n-choose-r"
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# cardinality(6, 4)
|
33
|
+
# # => 15
|
34
|
+
#
|
35
|
+
# @see http://en.wikipedia.org/wiki/Binomial_coefficient
|
36
|
+
#
|
37
|
+
def self.cardinality(n,r=nil)
|
38
|
+
raise(RangeError,"n must be non-negative") if n < 0
|
39
|
+
|
40
|
+
case r
|
41
|
+
when 0 then 1
|
42
|
+
when nil then Math.factorial(n)
|
43
|
+
else
|
44
|
+
Math.factorial(n) / (Math.factorial(r) * Math.factorial(n - r))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Wrapper for combination cardinality method defined above. The letter `C'
|
50
|
+
# is "chalkboard" notation for subset cardinality.
|
51
|
+
#
|
52
|
+
# @note
|
53
|
+
# This method's naming convention reflects well-known notation used
|
54
|
+
# within fields of academic inquiry such as discrete mathematics and
|
55
|
+
# set theory. It represents a function returning an integer value
|
56
|
+
# for the cardinality of a k-combination (i.e. binomial coefficient.)
|
57
|
+
#
|
58
|
+
# @see Choose.cardinality
|
59
|
+
#
|
60
|
+
def self.C(n,r=nil)
|
61
|
+
cardinality(n,r)
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# @param [Integer] n
|
66
|
+
# The total number of choices.
|
67
|
+
#
|
68
|
+
# @param [Enumerable] c
|
69
|
+
# The set of `r` values to choose from `n`.
|
70
|
+
#
|
71
|
+
# @return [Array]
|
72
|
+
# Elements are cardinalities for each subset "1" through "c".
|
73
|
+
#
|
74
|
+
# @raise [RangeError]
|
75
|
+
# `n` must be non-negative.
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
# cardinality_all(4)
|
79
|
+
# # => [4, 6, 4, 1]
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
# cardinality_all(10, 5..10)
|
83
|
+
# # => [252, 210, 120, 45, 10, 1]
|
84
|
+
#
|
85
|
+
# @note
|
86
|
+
# Sum of elements will equal Math.factorial(c)
|
87
|
+
#
|
88
|
+
# @see cardinality
|
89
|
+
# @see http://en.wikipedia.org/wiki/Combinations
|
90
|
+
#
|
91
|
+
def self.cardinality_all(n,c=(1..n))
|
92
|
+
if n < 0
|
93
|
+
raise(RangeError,"c must be non-negative")
|
94
|
+
end
|
95
|
+
|
96
|
+
c.map { |r| cardinality(n,r) }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Combinatorics
|
4
|
+
module Choose
|
5
|
+
#
|
6
|
+
# @author duper <super@manson.vistech.net>
|
7
|
+
#
|
8
|
+
# @since 0.4.0
|
9
|
+
#
|
10
|
+
module Mixin
|
11
|
+
#
|
12
|
+
# Get combinations with a specified number of elements from an input
|
13
|
+
# set.
|
14
|
+
#
|
15
|
+
# @param [Fixnum] k
|
16
|
+
# Cardinality of chosen subsets
|
17
|
+
#
|
18
|
+
# @yield [combo]
|
19
|
+
# The given block will be passed each combination.
|
20
|
+
#
|
21
|
+
# @yieldparam [Array] combo
|
22
|
+
# A k-sized combination of elements from the set.
|
23
|
+
#
|
24
|
+
# @raise [TypeError]
|
25
|
+
# `self` must be Enumerable.
|
26
|
+
#
|
27
|
+
# @return [Enumerator]
|
28
|
+
# If no block is given, an Enumerator of the k-sized combinations
|
29
|
+
# within the set will be returned.
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# [1, 2, 3].choose(2).to_a
|
33
|
+
# # => [#<Set: {1, 2}>, #<Set: {1, 3}>, #<Set: {2, 3}>]
|
34
|
+
#
|
35
|
+
# @see http://rubydoc.info/stdlib/core/Array#combination-instance_method
|
36
|
+
#
|
37
|
+
def choose(k,&block)
|
38
|
+
return enum_for(:choose,k) unless block
|
39
|
+
|
40
|
+
unless kind_of?(Enumerable)
|
41
|
+
raise(TypeError,"#{inspect} must be Enumerable")
|
42
|
+
end
|
43
|
+
|
44
|
+
elements = self.to_a
|
45
|
+
elements.uniq!
|
46
|
+
|
47
|
+
elements.combination(k) do |subset|
|
48
|
+
yield Set.new(subset)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'combinatorics/extensions/math'
|
2
|
+
|
3
|
+
module Combinatorics
|
4
|
+
module Derange
|
5
|
+
#
|
6
|
+
# Compute the number of derangements for a data structure of given size
|
7
|
+
#
|
8
|
+
# @see Math.subfactorial
|
9
|
+
#
|
10
|
+
def self.cardinality(n)
|
11
|
+
Math.subfactorial(n)
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# Wrapper for derangement cardinality method defined above
|
16
|
+
#
|
17
|
+
# @note The letter `D' is academic representation for derangements
|
18
|
+
#
|
19
|
+
def self.D(n)
|
20
|
+
cardinality(n)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|