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.
- data/.gitignore +21 -0
- data/README.md +27 -0
- data/array_partition.gemspec +47 -0
- data/example/fuel_tanker.rb +93 -0
- data/lib/array_partition.rb +2 -0
- data/lib/array_partition/array.rb +29 -0
- data/lib/array_partition/partitions.rb +31 -0
- data/spec/array_ext_spec.rb +51 -0
- data/spec/partition_spec.rb +49 -0
- metadata +90 -0
data/.gitignore
ADDED
data/README.md
ADDED
@@ -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,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
|