combinatorics 0.3.1 → 0.4.1
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.
- data/.gemtest +0 -0
- data/.gitignore +8 -0
- data/Benchmarks.md +257 -26
- data/ChangeLog.md +12 -0
- data/LICENSE.txt +1 -2
- data/README.md +102 -32
- data/Rakefile +13 -2
- data/benchmarks/cartesian_product.rb +18 -0
- data/benchmarks/choose.rb +19 -0
- data/benchmarks/derange.rb +18 -0
- data/benchmarks/list_comprehension.rb +2 -5
- data/benchmarks/permute.rb +18 -0
- data/benchmarks/power_set.rb +18 -0
- data/combinatorics.gemspec +124 -7
- data/gemspec.yml +11 -6
- data/lib/combinatorics.rb +7 -0
- data/lib/combinatorics/cartesian_product.rb +3 -0
- data/lib/combinatorics/cartesian_product/cardinality.rb +45 -0
- data/lib/combinatorics/cartesian_product/extensions.rb +2 -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/mixin.rb +57 -0
- data/lib/combinatorics/choose.rb +3 -0
- data/lib/combinatorics/choose/cardinality.rb +99 -0
- data/lib/combinatorics/choose/extensions.rb +2 -0
- data/lib/combinatorics/choose/extensions/array.rb +5 -0
- data/lib/combinatorics/choose/extensions/set.rb +6 -0
- data/lib/combinatorics/choose/mixin.rb +53 -0
- data/lib/combinatorics/derange.rb +3 -0
- data/lib/combinatorics/derange/cardinality.rb +23 -0
- data/lib/combinatorics/derange/extensions.rb +1 -0
- data/lib/combinatorics/derange/extensions/array.rb +5 -0
- data/lib/combinatorics/derange/mixin.rb +47 -0
- data/lib/combinatorics/enumerator.rb +2 -0
- data/lib/combinatorics/extensions/math.rb +177 -0
- data/lib/combinatorics/generator.rb +8 -1
- data/lib/combinatorics/permute.rb +3 -0
- data/lib/combinatorics/permute/cardinality.rb +98 -0
- data/lib/combinatorics/permute/extensions.rb +2 -0
- data/lib/combinatorics/permute/extensions/array.rb +7 -0
- data/lib/combinatorics/permute/extensions/set.rb +9 -0
- data/lib/combinatorics/permute/mixin.rb +48 -0
- data/lib/combinatorics/power_set.rb +1 -0
- data/lib/combinatorics/power_set/cardinality.rb +36 -0
- data/lib/combinatorics/power_set/mixin.rb +19 -22
- data/lib/combinatorics/version.rb +2 -2
- 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/derange/array_spec.rb +10 -0
- data/spec/derange/cardinality_spec.rb +14 -0
- data/spec/derange/mixin_examples.rb +52 -0
- data/spec/extensions/math_spec.rb +100 -0
- data/spec/extensions/range_spec.rb +1 -1
- 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 +5 -3
- metadata +114 -95
data/Rakefile
CHANGED
@@ -2,28 +2,39 @@ require 'rubygems'
|
|
2
2
|
require 'rake'
|
3
3
|
|
4
4
|
begin
|
5
|
+
gem 'ore-tasks', '~> 0.4'
|
5
6
|
require 'ore/tasks'
|
7
|
+
|
6
8
|
Ore::Tasks.new
|
7
9
|
rescue LoadError => e
|
8
|
-
|
9
|
-
|
10
|
+
warn e.message
|
11
|
+
warn "Run `gem install ore-tasks` to install 'ore/tasks'."
|
10
12
|
end
|
11
13
|
|
12
14
|
begin
|
15
|
+
gem 'rspec', '~> 2.4'
|
13
16
|
require 'rspec/core/rake_task'
|
17
|
+
|
14
18
|
RSpec::Core::RakeTask.new
|
15
19
|
rescue LoadError => e
|
16
20
|
task :spec do
|
17
21
|
abort "Please run `gem install rspec` to install RSpec."
|
18
22
|
end
|
19
23
|
end
|
24
|
+
task :test => :spec
|
20
25
|
task :default => :spec
|
21
26
|
|
22
27
|
begin
|
28
|
+
gem 'yard', '~> 0.7'
|
23
29
|
require 'yard'
|
30
|
+
|
24
31
|
YARD::Rake::YardocTask.new
|
25
32
|
rescue LoadError => e
|
26
33
|
task :yard do
|
27
34
|
abort "Please run `gem install yard` to install YARD."
|
28
35
|
end
|
29
36
|
end
|
37
|
+
|
38
|
+
task :benchmark do
|
39
|
+
Dir.glob('benchmarks/*.rb') { |script| ruby(script) }
|
40
|
+
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)
|
@@ -9,14 +7,13 @@ require 'combinatorics/list_comprehension'
|
|
9
7
|
|
10
8
|
Benchmark.bm(12) do |b|
|
11
9
|
singleton_list = ([1] * 500)
|
12
|
-
single_enum_list = [1..
|
13
|
-
depth_list = [1..
|
10
|
+
single_enum_list = [1..100, 1]
|
11
|
+
depth_list = [1..100]
|
14
12
|
|
15
13
|
b.report('singleton:') do
|
16
14
|
singleton_list.comprehension.each { |list| list.last }
|
17
15
|
end
|
18
16
|
|
19
|
-
|
20
17
|
b.report('single-enum:') do
|
21
18
|
single_enum_list.comprehension.each { |list| list.last }
|
22
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/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,10 +1,127 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
Gem::Specification.new do |gemspec|
|
6
|
+
files = if File.directory?('.git')
|
7
|
+
`git ls-files`.split($/)
|
8
|
+
elsif File.directory?('.hg')
|
9
|
+
`hg manifest`.split($/)
|
10
|
+
elsif File.directory?('.svn')
|
11
|
+
`svn ls -R`.split($/).select { |path| File.file?(path) }
|
12
|
+
else
|
13
|
+
Dir['{**/}{.*,*}'].select { |path| File.file?(path) }
|
14
|
+
end
|
15
|
+
|
16
|
+
filter_files = lambda { |paths|
|
17
|
+
case paths
|
18
|
+
when Array
|
19
|
+
(files & paths)
|
20
|
+
when String
|
21
|
+
(files & Dir[paths])
|
22
|
+
end
|
23
|
+
}
|
24
|
+
|
25
|
+
version = {
|
26
|
+
:file => 'lib/combinatorics/version.rb',
|
27
|
+
:constant => 'Combinatorics::VERSION'
|
28
|
+
}
|
29
|
+
|
30
|
+
defaults = {
|
31
|
+
'name' => File.basename(File.dirname(__FILE__)),
|
32
|
+
'files' => files,
|
33
|
+
'executables' => filter_files['bin/*'].map { |path| File.basename(path) },
|
34
|
+
'test_files' => filter_files['{test/{**/}*_test.rb,spec/{**/}*_spec.rb}'],
|
35
|
+
'extra_doc_files' => filter_files['*.{txt,rdoc,md,markdown,tt,textile}'],
|
36
|
+
}
|
37
|
+
|
38
|
+
metadata = defaults.merge(YAML.load_file('gemspec.yml'))
|
39
|
+
|
40
|
+
gemspec.name = metadata.fetch('name',defaults[:name])
|
41
|
+
gemspec.version = if metadata['version']
|
42
|
+
metadata['version']
|
43
|
+
elsif File.file?(version[:file])
|
44
|
+
require File.join('.',version[:file])
|
45
|
+
eval(version[:constant])
|
46
|
+
end
|
47
|
+
|
48
|
+
gemspec.summary = metadata.fetch('summary',metadata['description'])
|
49
|
+
gemspec.description = metadata.fetch('description',metadata['summary'])
|
50
|
+
|
51
|
+
case metadata['license']
|
52
|
+
when Array
|
53
|
+
gemspec.licenses = metadata['license']
|
54
|
+
when String
|
55
|
+
gemspec.license = metadata['license']
|
56
|
+
end
|
57
|
+
|
58
|
+
case metadata['authors']
|
59
|
+
when Array
|
60
|
+
gemspec.authors = metadata['authors']
|
61
|
+
when String
|
62
|
+
gemspec.author = metadata['authors']
|
63
|
+
end
|
64
|
+
|
65
|
+
gemspec.email = metadata['email']
|
66
|
+
gemspec.homepage = metadata['homepage']
|
67
|
+
|
68
|
+
case metadata['require_paths']
|
69
|
+
when Array
|
70
|
+
gemspec.require_paths = metadata['require_paths']
|
71
|
+
when String
|
72
|
+
gemspec.require_path = metadata['require_paths']
|
73
|
+
end
|
74
|
+
|
75
|
+
gemspec.files = filter_files[metadata['files']]
|
76
|
+
|
77
|
+
gemspec.executables = metadata['executables']
|
78
|
+
gemspec.extensions = metadata['extensions']
|
79
|
+
|
80
|
+
if Gem::VERSION < '1.7.'
|
81
|
+
gemspec.default_executable = gemspec.executables.first
|
82
|
+
end
|
83
|
+
|
84
|
+
gemspec.test_files = filter_files[metadata['test_files']]
|
85
|
+
|
86
|
+
unless gemspec.files.include?('.document')
|
87
|
+
gemspec.extra_rdoc_files = metadata['extra_doc_files']
|
88
|
+
end
|
89
|
+
|
90
|
+
gemspec.post_install_message = metadata['post_install_message']
|
91
|
+
gemspec.requirements = metadata['requirements']
|
92
|
+
|
93
|
+
if gemspec.respond_to?(:required_ruby_version=)
|
94
|
+
gemspec.required_ruby_version = metadata['required_ruby_version']
|
95
|
+
end
|
96
|
+
|
97
|
+
if gemspec.respond_to?(:required_rubygems_version=)
|
98
|
+
gemspec.required_rubygems_version = metadata['required_rubygems_version']
|
99
|
+
end
|
100
|
+
|
101
|
+
parse_versions = lambda { |versions|
|
102
|
+
case versions
|
103
|
+
when Array
|
104
|
+
versions.map { |v| v.to_s }
|
105
|
+
when String
|
106
|
+
versions.split(/,\s*/)
|
107
|
+
end
|
108
|
+
}
|
109
|
+
|
110
|
+
if metadata['dependencies']
|
111
|
+
metadata['dependencies'].each do |name,versions|
|
112
|
+
gemspec.add_dependency(name,parse_versions[versions])
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
if metadata['runtime_dependencies']
|
117
|
+
metadata['runtime_dependencies'].each do |name,versions|
|
118
|
+
gemspec.add_runtime_dependency(name,parse_versions[versions])
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
if metadata['development_dependencies']
|
123
|
+
metadata['development_dependencies'].each do |name,versions|
|
124
|
+
gemspec.add_development_dependency(name,parse_versions[versions])
|
125
|
+
end
|
6
126
|
end
|
7
|
-
rescue NameError
|
8
|
-
STDERR.puts "The 'combinatorics.gemspec' file requires Ore."
|
9
|
-
STDERR.puts "Run `gem install ore` to install Ore."
|
10
127
|
end
|
data/gemspec.yml
CHANGED
@@ -4,13 +4,18 @@ description:
|
|
4
4
|
A collection of modules and methods for performing Combinatoric
|
5
5
|
calculations.
|
6
6
|
license: MIT
|
7
|
-
authors:
|
8
|
-
|
7
|
+
authors:
|
8
|
+
- Postmodern
|
9
|
+
- Duper
|
10
|
+
email:
|
11
|
+
- postmodern.mod3@gmail.com
|
12
|
+
- super@manson.vistech.net
|
9
13
|
homepage: http://github.com/postmodern/combinatorics
|
10
14
|
has_yard: true
|
11
15
|
|
16
|
+
required_ruby_version: ">= 1.8.7"
|
17
|
+
|
12
18
|
development_dependencies:
|
13
|
-
ore: ~> 0.
|
14
|
-
|
15
|
-
|
16
|
-
yard: ~> 0.6.0
|
19
|
+
ore-tasks: ~> 0.4
|
20
|
+
rspec: ~> 2.4
|
21
|
+
yard: ~> 0.7
|
data/lib/combinatorics.rb
CHANGED
@@ -1,4 +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'
|
4
8
|
require 'combinatorics/version'
|
9
|
+
|
10
|
+
# @todo inversions (permutation mutations)
|
11
|
+
# @todo set families (approximate power set subsets)
|
@@ -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
|