combinatorics 0.3.0 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/.document +1 -1
  3. data/.gemtest +0 -0
  4. data/.github/workflows/ruby.yml +28 -0
  5. data/.gitignore +4 -5
  6. data/Benchmarks.md +281 -0
  7. data/ChangeLog.md +42 -0
  8. data/Gemfile +14 -0
  9. data/LICENSE.txt +1 -2
  10. data/README.md +178 -69
  11. data/Rakefile +7 -27
  12. data/benchmarks/cartesian_product.rb +18 -0
  13. data/benchmarks/choose.rb +19 -0
  14. data/benchmarks/derange.rb +18 -0
  15. data/benchmarks/list_comprehension.rb +20 -4
  16. data/benchmarks/permute.rb +18 -0
  17. data/benchmarks/power_set.rb +18 -0
  18. data/combinatorics.gemspec +54 -83
  19. data/gemspec.yml +25 -0
  20. data/lib/combinatorics/cartesian_product/cardinality.rb +45 -0
  21. data/lib/combinatorics/cartesian_product/extensions/array.rb +7 -0
  22. data/lib/combinatorics/cartesian_product/extensions/set.rb +9 -0
  23. data/lib/combinatorics/cartesian_product/extensions.rb +2 -0
  24. data/lib/combinatorics/cartesian_product/mixin.rb +57 -0
  25. data/lib/combinatorics/cartesian_product.rb +3 -0
  26. data/lib/combinatorics/choose/cardinality.rb +99 -0
  27. data/lib/combinatorics/choose/extensions/array.rb +5 -0
  28. data/lib/combinatorics/choose/extensions/set.rb +6 -0
  29. data/lib/combinatorics/choose/extensions.rb +2 -0
  30. data/lib/combinatorics/choose/mixin.rb +53 -0
  31. data/lib/combinatorics/choose.rb +3 -0
  32. data/lib/combinatorics/derange/cardinality.rb +23 -0
  33. data/lib/combinatorics/derange/extensions/array.rb +5 -0
  34. data/lib/combinatorics/derange/extensions.rb +1 -0
  35. data/lib/combinatorics/derange/mixin.rb +47 -0
  36. data/lib/combinatorics/derange.rb +3 -0
  37. data/lib/combinatorics/enumerator.rb +2 -0
  38. data/lib/combinatorics/extensions/math.rb +176 -0
  39. data/lib/combinatorics/extensions/range.rb +2 -2
  40. data/lib/combinatorics/generator.rb +7 -4
  41. data/lib/combinatorics/list_comprehension.rb +6 -1
  42. data/lib/combinatorics/permute/cardinality.rb +98 -0
  43. data/lib/combinatorics/permute/extensions/array.rb +7 -0
  44. data/lib/combinatorics/permute/extensions/set.rb +9 -0
  45. data/lib/combinatorics/permute/extensions.rb +2 -0
  46. data/lib/combinatorics/permute/mixin.rb +48 -0
  47. data/lib/combinatorics/permute.rb +3 -0
  48. data/lib/combinatorics/power_set/cardinality.rb +36 -0
  49. data/lib/combinatorics/power_set/mixin.rb +19 -22
  50. data/lib/combinatorics/power_set.rb +1 -0
  51. data/lib/combinatorics/version.rb +4 -0
  52. data/lib/combinatorics.rb +8 -0
  53. data/spec/cartesian_product/array_spec.rb +10 -0
  54. data/spec/cartesian_product/cardinality_spec.rb +64 -0
  55. data/spec/cartesian_product/mixin_examples.rb +98 -0
  56. data/spec/cartesian_product/set_spec.rb +10 -0
  57. data/spec/choose/array_spec.rb +9 -0
  58. data/spec/choose/cardinality_spec.rb +132 -0
  59. data/spec/choose/mixin_examples.rb +48 -0
  60. data/spec/choose/set_spec.rb +9 -0
  61. data/spec/combinatorics_spec.rb +5 -1
  62. data/spec/derange/array_spec.rb +10 -0
  63. data/spec/derange/cardinality_spec.rb +14 -0
  64. data/spec/derange/mixin_examples.rb +52 -0
  65. data/spec/enumerator_spec.rb +1 -1
  66. data/spec/extensions/math_spec.rb +100 -0
  67. data/spec/extensions/range_spec.rb +13 -13
  68. data/spec/generator_spec.rb +1 -1
  69. data/spec/list_comprehension_spec.rb +11 -11
  70. data/spec/permute/array_spec.rb +10 -0
  71. data/spec/permute/cardinality_spec.rb +146 -0
  72. data/spec/permute/mixin_examples.rb +42 -0
  73. data/spec/permute/set_spec.rb +10 -0
  74. data/spec/power_set/array_spec.rb +3 -2
  75. data/spec/power_set/cardinality_spec.rb +32 -0
  76. data/spec/power_set/mixin_examples.rb +17 -8
  77. data/spec/power_set/set_spec.rb +3 -2
  78. data/spec/spec_helper.rb +3 -3
  79. metadata +106 -104
  80. data/VERSION +0 -1
data/Rakefile CHANGED
@@ -1,36 +1,16 @@
1
1
  require 'rubygems'
2
- require 'rake'
3
2
 
4
- begin
5
- gem 'jeweler', '~> 1.4.0'
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
- begin
29
- require 'yard'
11
+ require 'yard'
12
+ YARD::Rake::YardocTask.new
30
13
 
31
- YARD::Rake::YardocTask.new
32
- rescue LoadError
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
- list = [(1..200)] * 3
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
- puts Benchmark.measure { list.comprehension.each { |list| } }
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
@@ -1,90 +1,61 @@
1
- # Generated by jeweler
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
- Gem::Specification.new do |s|
7
- s.name = %q{combinatorics}
8
- s.version = "0.3.0"
3
+ require 'yaml'
9
4
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Postmodern"]
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
- if s.respond_to? :specification_version then
72
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
73
- s.specification_version = 3
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
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
76
- s.add_development_dependency(%q<rspec>, ["~> 2.0.0"])
77
- s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
78
- s.add_development_dependency(%q<jeweler>, ["~> 1.4.0"])
79
- else
80
- s.add_dependency(%q<rspec>, ["~> 2.0.0"])
81
- s.add_dependency(%q<yard>, ["~> 0.6.0"])
82
- s.add_dependency(%q<jeweler>, ["~> 1.4.0"])
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,7 @@
1
+ require 'combinatorics/cartesian_product/mixin'
2
+
3
+ class Array
4
+
5
+ include Combinatorics::CartesianProduct::Mixin
6
+
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'combinatorics/cartesian_product/mixin'
2
+
3
+ require 'set'
4
+
5
+ class Set
6
+
7
+ include Combinatorics::CartesianProduct::Mixin
8
+
9
+ end
@@ -0,0 +1,2 @@
1
+ require 'combinatorics/cartesian_product/extensions/array'
2
+ require 'combinatorics/cartesian_product/extensions/set'
@@ -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,3 @@
1
+ require 'combinatorics/cartesian_product/mixin'
2
+ require 'combinatorics/cartesian_product/extensions'
3
+ require 'combinatorics/cartesian_product/cardinality'
@@ -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,5 @@
1
+ require 'combinatorics/choose/mixin'
2
+
3
+ class Array
4
+ include Combinatorics::Choose::Mixin
5
+ end
@@ -0,0 +1,6 @@
1
+ require 'set'
2
+ require 'combinatorics/choose/mixin'
3
+
4
+ class Set
5
+ include Combinatorics::Choose::Mixin
6
+ end
@@ -0,0 +1,2 @@
1
+ require 'combinatorics/choose/extensions/array'
2
+ require 'combinatorics/choose/extensions/set'
@@ -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,3 @@
1
+ require 'combinatorics/choose/mixin'
2
+ require 'combinatorics/choose/extensions'
3
+ require 'combinatorics/choose/cardinality'
@@ -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