array_partition 0.1.0

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.
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ .gem
20
+
21
+ ## PROJECT::SPECIFIC
@@ -0,0 +1,27 @@
1
+ # Array Partition
2
+ by Aish Fenton for [vWorkApp](http://www.vworkapp.com)
3
+
4
+ A little gem that gives you all partitions of an array into at most *k* sub-arrays. Useful for generating test scenarios and solving certain kinds of optimisation problems.
5
+
6
+ ["Apple", "Orange", "Pear"].partition(2)
7
+
8
+ [
9
+ [["Apple", "Orange", "Pear"]],
10
+ [["Apple", "Orange"], ["Pear"]],
11
+ [["Apple", "Pear"], ["Orange"]],
12
+ [["Apple"], ["Orange", "Pear"]]
13
+ ]
14
+
15
+ The gem can also be used to provide the [Stirling Number of the Second Kind](http://en.wikipedia.org/wiki/Stirling_numbers_of_the_second_kind) of the array.
16
+
17
+ ## Install
18
+
19
+ sudo gem install array_partition
20
+
21
+ ## Usage
22
+
23
+ See example/fuel_tanker.rb for a real-live application of partitions being used to solve a combinatorial optimisation problem.
24
+
25
+ ## Copyright
26
+
27
+ Copyright (c) 2011 vWorkApp Inc.
@@ -0,0 +1,47 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{array_partition}
3
+ s.version = "0.1.0"
4
+
5
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
6
+ s.authors = ["Aish Fenton"]
7
+ s.date = %q{2011-01-20}
8
+ s.description = %q{A little gem that gives you all partitions of an array into at most k sub-arrays}
9
+ s.email = %q{aish@vworkapp.com}
10
+ s.extra_rdoc_files = [
11
+ "README.md"
12
+ ]
13
+ s.files = [
14
+ ".gitignore",
15
+ "README.md",
16
+ "lib/array_partition.rb",
17
+ "lib/array_partition/array.rb",
18
+ "lib/array_partition/partitions.rb",
19
+ "array_partition.gemspec",
20
+ "spec/array_ext_spec.rb",
21
+ "spec/partition_spec.rb",
22
+ "example/fuel_tanker.rb",
23
+ ]
24
+ s.homepage = %q{http://github.com/visfleet/array_partition}
25
+ s.rdoc_options = ["--charset=UTF-8"]
26
+ s.require_paths = ["lib"]
27
+ s.rubygems_version = %q{1.3.6}
28
+ s.summary = %q{A little gem that gives you all partitions of an array into at most k sub-arrays}
29
+ s.test_files = [
30
+ "spec/array_ext_spec.rb",
31
+ "spec/partition_spec.rb",
32
+ ]
33
+
34
+ if s.respond_to? :specification_version then
35
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
36
+ s.specification_version = 3
37
+
38
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
39
+ s.add_development_dependency(%q<rspec>, [">= 2.3"])
40
+ else
41
+ s.add_dependency(%q<rspec>, [">= 2.3"])
42
+ end
43
+ else
44
+ s.add_dependency(%q<rspec>, [">= 2.3"])
45
+ end
46
+ end
47
+
@@ -0,0 +1,93 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') unless $LOAD_PATH.include?(File.dirname(__FILE__) + '/../lib')
2
+ require 'array_partition'
3
+ require 'permutation'
4
+
5
+ # A fuel tanker with 8 compartments (tanks). The tanker can be used to carry up to 8 separate
6
+ # products (e.g. gas, diesel, milk) but has the constraint that products can't be mixed in a compartment.
7
+ # This class provides the method fit_to_tanks(products) which returns an ordering of which products
8
+ # to put in which tanks, so that as much product is carried as possible. If the products can't fitted
9
+ # to a combination of tanks then nil is returned.
10
+ #
11
+ class FuelTanker
12
+
13
+ TANKS = [900, 200, 200, 200, 200, 200, 700, 200]
14
+
15
+ def fit_to_tanks(products)
16
+ partitions = TANKS.partition(products.size)
17
+
18
+ best = nil
19
+ partitions.each do |p|
20
+ next if p.size < products.size
21
+ product_order = try_permutations(p, products)
22
+ next if product_order.nil?
23
+ best = [p, product_order]
24
+ end
25
+ best
26
+ end
27
+
28
+ private
29
+
30
+ def try_permutations(partition, products)
31
+ perm = Permutation.for(products)
32
+ perm.each do |p|
33
+ products = p.project
34
+ next unless try_permutation(partition, products)
35
+ return products
36
+ end
37
+ nil
38
+ end
39
+
40
+ def try_permutation(partition, products)
41
+ products.each_with_index do |product, idx|
42
+ return false unless fits(partition[idx], product)
43
+ end
44
+ true
45
+ end
46
+
47
+ def fits(tanks, amount)
48
+ left_over = tanks.inject(amount) { |amount, tank| amount - tank }
49
+ left_over <= 0
50
+ end
51
+
52
+ end
53
+
54
+ class UI
55
+ def self.start
56
+ puts "Enter amounts to load (carrage return to end):"
57
+ products = []
58
+ while ((line = gets) != "\n")
59
+ products << line.to_i
60
+ end
61
+
62
+ partition, products = FuelTanker.new.fit_to_tanks(products)
63
+
64
+ if partition.nil?
65
+ puts "Sorry can't fit those amounts in the tanker"
66
+ else
67
+ puts "--------\n"
68
+ puts "Results:\n"
69
+ products.each_with_index do |product, idx|
70
+ puts "\tProduct: #{product} in tanks: #{partition[idx].join(",")}"
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ UI.start
77
+
78
+ # gem install permutation
79
+ #
80
+ # $ ruby example/fuel_tanker.rb
81
+ # Enter amounts to load (carrage return to end):
82
+ # 900
83
+ # 1200
84
+ # 700
85
+ #
86
+ # --------
87
+ # Results:
88
+ # Product: 900 in tanks: 900
89
+ # Product: 1200 in tanks: 200,200,200,200,200,200
90
+ # Product: 700 in tanks: 700
91
+
92
+
93
+ p ["Apple", "Orange", "Pear"].partition(2)
@@ -0,0 +1,2 @@
1
+ require 'array_partition/partitions'
2
+ require 'array_partition/array'
@@ -0,0 +1,29 @@
1
+ class Array
2
+
3
+ def partition(k)
4
+ partitions = ArrayPartition::Partitions.partitions(self.size, k)
5
+
6
+ result = []
7
+
8
+ partitions.each do |p|
9
+ r_part = []
10
+ p.each do |idxs|
11
+ r_block = []
12
+ idxs.each do |idx|
13
+ r_block << self[idx-1]
14
+ end
15
+ r_part << r_block
16
+ end
17
+ result << r_part
18
+ end
19
+ result
20
+ end
21
+
22
+ def each_partition(k)
23
+ r = self.partition(k)
24
+ r.each do |p|
25
+ yield p
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,31 @@
1
+
2
+ module ArrayPartition
3
+
4
+ # Partitions a set of size n into no more than k blocks.
5
+ class Partitions
6
+
7
+ def self.partitions(n,k)
8
+ rec_partition(n,k)
9
+ end
10
+
11
+ private
12
+
13
+ def self.rec_partition(n, k)
14
+ if n == 1
15
+ return [[[1]]]
16
+ else
17
+ partitions = rec_partition(n-1,k)
18
+ new_partitions = []
19
+ partitions.each do |p|
20
+ p.size.times do |offset|
21
+ np = p[0, offset] + [p[offset].clone << n] + p[offset+1..-1]
22
+ new_partitions << np
23
+ end
24
+ new_partitions << (p + [[n]]) if p.size < k
25
+ end
26
+ end
27
+ new_partitions
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,51 @@
1
+ require 'array_partition'
2
+
3
+ describe Array do
4
+
5
+ context "when calling partition(2) on [a,b,c,d]" do
6
+ subject do
7
+ arr = ['a', 'b', 'c', 'd']
8
+ arr.partition(2)
9
+ end
10
+
11
+ it "produces 8 partitions" do
12
+ subject.size.should == 8
13
+ end
14
+
15
+ it "has no blocks larger than 2" do
16
+ subject.each do |p|
17
+ p.size.should <= 2
18
+ end
19
+ end
20
+
21
+ it "contains the right partitions" do
22
+ subject.should == [
23
+ [['a','b','c','d']],
24
+ [['a','b','c'],['d']],
25
+ [['a','b','d'],['c']],
26
+ [['a','b'],['c','d']],
27
+ [['a','c','d'],['b']],
28
+ [['a','c'],['b','d']],
29
+ [['a','d'],['b','c']],
30
+ [['a'],['b','c','d']]
31
+ ]
32
+ end
33
+ end
34
+
35
+ it "lets you enumerate over the partitions" do
36
+ arr = ['Apple', 'Orange', 'Pear']
37
+
38
+ result = []
39
+ arr.each_partition(2) do |p|
40
+ result << p
41
+ end
42
+
43
+ result.should == [
44
+ [['Apple', 'Orange', 'Pear']],
45
+ [['Apple', 'Orange'], ['Pear']],
46
+ [['Apple', 'Pear'], ['Orange']],
47
+ [['Apple'], ['Orange', 'Pear']],
48
+ ]
49
+ end
50
+
51
+ end
@@ -0,0 +1,49 @@
1
+ require 'array_partition'
2
+
3
+ describe ArrayPartition::Partitions do
4
+
5
+ context "when partitioning (1,2,3,4) into 2 blocks" do
6
+ subject do
7
+ ArrayPartition::Partitions.partitions(4,2)
8
+ end
9
+
10
+ it "produces 8 partitions" do
11
+ subject.size.should == 8
12
+ end
13
+
14
+ it "has no blocks larger than 2" do
15
+ subject.each do |p|
16
+ p.size.should <= 2
17
+ end
18
+ end
19
+
20
+ it "contains the right partitions" do
21
+ subject.should == [
22
+ [[1,2,3,4]],
23
+ [[1,2,3],[4]],
24
+ [[1,2,4],[3]],
25
+ [[1,2],[3,4]],
26
+ [[1,3,4],[2]],
27
+ [[1,3],[2,4]],
28
+ [[1,4],[2,3]],
29
+ [[1],[2,3,4]]
30
+ ]
31
+ end
32
+
33
+ end
34
+
35
+ context "when partitioning larger sets and larger blocks" do
36
+
37
+ it "produces the right number of partitions for n=8,k=3" do
38
+ result = ArrayPartition::Partitions.partitions(8,3)
39
+ result.size.should == 1094
40
+ end
41
+
42
+ it "produces the right number of partitions for n=10,k=6" do
43
+ result = ArrayPartition::Partitions.partitions(10,6)
44
+ result.size.should == 109299
45
+ end
46
+
47
+ end
48
+
49
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: array_partition
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Aish Fenton
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-20 00:00:00 +13:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 5
30
+ segments:
31
+ - 2
32
+ - 3
33
+ version: "2.3"
34
+ type: :development
35
+ version_requirements: *id001
36
+ description: A little gem that gives you all partitions of an array into at most k sub-arrays
37
+ email: aish@vworkapp.com
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - README.md
44
+ files:
45
+ - .gitignore
46
+ - README.md
47
+ - lib/array_partition.rb
48
+ - lib/array_partition/array.rb
49
+ - lib/array_partition/partitions.rb
50
+ - array_partition.gemspec
51
+ - spec/array_ext_spec.rb
52
+ - spec/partition_spec.rb
53
+ - example/fuel_tanker.rb
54
+ has_rdoc: true
55
+ homepage: http://github.com/visfleet/array_partition
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options:
60
+ - --charset=UTF-8
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ hash: 3
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ requirements: []
82
+
83
+ rubyforge_project:
84
+ rubygems_version: 1.3.7
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: A little gem that gives you all partitions of an array into at most k sub-arrays
88
+ test_files:
89
+ - spec/array_ext_spec.rb
90
+ - spec/partition_spec.rb