array_partition 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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