subset_sum 1.0.1 → 1.0.2
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.
- checksums.yaml +7 -0
- data/LICENSE +1 -1
- data/spec/subset_sum_spec.rb +20 -19
- data/subset_sum.c +12 -0
- data/subset_sum.rb +77 -73
- metadata +24 -46
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7a847f3a3319373c99e460022e1b913e60b6ab1f
|
4
|
+
data.tar.gz: b89473242737cfea78bf878446da36cc528a125a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a6c6c942a74d5fc4adeba3f2b0686f3c7b96e7ae797bdf79adce4758e25b59262bd4985134a77b5de006ad0dfe7c5f011377de97738003194b56aff78d7ee5b9
|
7
|
+
data.tar.gz: e6a57ccd8c77aa493c2e38908d7b047cc66d843e26da625937aa21caa2b0601ad1b207c046f19d67fcf5bab076ef4bce1a18bda12b582174361189b71940272f
|
data/LICENSE
CHANGED
data/spec/subset_sum_spec.rb
CHANGED
@@ -1,47 +1,48 @@
|
|
1
|
-
#!/usr/bin/env spec
|
2
1
|
require 'subset_sum'
|
2
|
+
require 'minitest/autorun'
|
3
3
|
|
4
|
-
|
4
|
+
describe "SubsetSum.subset_sum" do
|
5
5
|
it "should raise TypeError unless the first argument is an array" do
|
6
|
-
proc{SubsetSum.subset_sum(1, 1)}.
|
7
|
-
proc{SubsetSum.subset_sum(nil, 1)}.
|
6
|
+
proc{SubsetSum.subset_sum(1, 1)}.must_raise(TypeError)
|
7
|
+
proc{SubsetSum.subset_sum(nil, 1)}.must_raise(TypeError)
|
8
8
|
end
|
9
9
|
|
10
10
|
it "should raise TypeError unless the second argument is an Integer" do
|
11
|
-
proc{SubsetSum.subset_sum([1,2,3], [1,2,3])}.
|
12
|
-
proc{SubsetSum.subset_sum([1,2,3], nil)}.
|
11
|
+
proc{SubsetSum.subset_sum([1,2,3], [1,2,3])}.must_raise(TypeError)
|
12
|
+
proc{SubsetSum.subset_sum([1,2,3], nil)}.must_raise(TypeError)
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should return nil if no subset sums to the given amount" do
|
16
|
-
SubsetSum.subset_sum([1,2,3], -1).
|
17
|
-
SubsetSum.subset_sum([1,2,3], 7).
|
16
|
+
SubsetSum.subset_sum([1,2,3], -1).must_equal nil
|
17
|
+
SubsetSum.subset_sum([1,2,3], 7).must_equal nil
|
18
18
|
end
|
19
19
|
|
20
20
|
it "should return a subset of the given array summing to the given amount" do
|
21
|
-
SubsetSum.subset_sum([1,2,3], 0).
|
22
|
-
SubsetSum.subset_sum([1,2,3], 1).
|
23
|
-
SubsetSum.subset_sum([1,2,3], 2).
|
24
|
-
[[1,2], [3]].
|
25
|
-
SubsetSum.subset_sum([1,2,3], 4).
|
26
|
-
SubsetSum.subset_sum([1,2,3], 5).
|
27
|
-
SubsetSum.subset_sum([1,2,3], 6).
|
21
|
+
SubsetSum.subset_sum([1,2,3], 0).must_equal []
|
22
|
+
SubsetSum.subset_sum([1,2,3], 1).must_equal [1]
|
23
|
+
SubsetSum.subset_sum([1,2,3], 2).must_equal [2]
|
24
|
+
[[1,2], [3]].must_include(SubsetSum.subset_sum([1,2,3], 3))
|
25
|
+
SubsetSum.subset_sum([1,2,3], 4).must_equal [1,3]
|
26
|
+
SubsetSum.subset_sum([1,2,3], 5).must_equal [2,3]
|
27
|
+
SubsetSum.subset_sum([1,2,3], 6).must_equal [1,2,3]
|
28
28
|
|
29
29
|
a = [355104, 476077, 476303, 224658, -17532, -183480, -286788, 238271, 231845, -227454, 226199, 105438, 316870, 353652, 173563, 244958, 367896, 105046, 495797, 447209, 397810, -394348, 242527, 17532, -57224, -38084, 82375, 445376, -297793, 368660, -65413, 96325, -472195, -23826, -113982, -355574, 331821]
|
30
30
|
b = [-17532, 226199, 105438, 353652, 173563, 244958, 397810, 242527, 17532, -38084, 82375, 445376, 368660, -65413, -23826, 331821]
|
31
31
|
s = b.inject(0){|x,y|y+x}
|
32
|
-
SubsetSum.subset_sum(a, s).inject(0){|x,y|y+x}.
|
32
|
+
SubsetSum.subset_sum(a, s).inject(0){|x,y|y+x}.must_equal s
|
33
33
|
end
|
34
34
|
|
35
35
|
it "should raise a SubsetSum::TimeoutError if the Timeout expires" do
|
36
36
|
a = [333471, -81424, 168803, -14874, 269559, 276470, -112006, -73874, -122941, 137631, -28130, -158579, -422171, 27107, 320112, -36290, -285551, 8352, 148555, -191195, -378065, -98153, 446370, 364645, -410213, -18978, 473135, -86692, 220362, -131996, -350007, -151015, -406022, -357901, -180250, 234239, -342453, -355331, -288241, 14474, 389351, 98297, -241761, 96174, 21747, 231199, -473800, -172155, 65848, -36045, -449758, 255701, 366189, -181547, -334067, 61902, -369357, -444588, 196073, 173373, 348303, 445476, 233108, 146771, -124962, 426707, -443489, -147630, -40380, 462470, -460874, -488721, -316681, 58097, 374972, -35163, 101042, -143586, 176358, -443511, 262241, 79581, -251333, 432832, 216996, 290187, -216371, 412922, -250978, 282206, -379257, 240712, 245726, -478854, 185713, 187485, -213379, -185167, -475251, -127179, -158661]
|
37
37
|
b = [333471, -14874, 276470, 137631, -28130, -158579, 27107, -36290, 148555, -191195, -378065, 473135, -86692, -350007, -151015, -406022, 234239, -355331, 21747, 231199, -172155, -36045, -449758, 366189, -334067, -369357, -444588, 348303, 445476, -124962, 426707, 462470, -460874, 58097, 374972, -35163, 101042, 79581, -216371, 240712, -213379, -185167]
|
38
38
|
s = b.inject(0){|x,y|y+x}
|
39
|
-
proc{SubsetSum.subset_sum(a, s, 1).inject(0){|x,y|y+x}}.
|
39
|
+
proc{SubsetSum.subset_sum(a, s, 1).inject(0){|x,y|y+x}}.must_raise(SubsetSum::TimeoutError)
|
40
40
|
end
|
41
41
|
|
42
42
|
it "should not use the C version if the integers are Bignums" do
|
43
|
-
SubsetSum.
|
44
|
-
|
43
|
+
SubsetSum.stub :_subset_sum, proc{raise} do
|
44
|
+
SubsetSum.subset_sum([2**100, 1, 2,], 1+2**100).must_equal [2**100, 1]
|
45
|
+
end
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
data/subset_sum.c
CHANGED
@@ -339,9 +339,21 @@ static VALUE rbss_main(VALUE self, VALUE numbers, VALUE result, VALUE max_second
|
|
339
339
|
return answer;
|
340
340
|
}
|
341
341
|
|
342
|
+
#ifndef RB_FIXNUM_P
|
343
|
+
#define RB_FIXNUM_P(v) FIXNUM_P(v)
|
344
|
+
#endif
|
345
|
+
|
346
|
+
static VALUE rbss_supported(VALUE self, VALUE want, VALUE pos, VALUE neg, VALUE max_seconds) {
|
347
|
+
if (RB_FIXNUM_P(want) && RB_FIXNUM_P(pos) && RB_FIXNUM_P(neg) && RB_FIXNUM_P(max_seconds)) {
|
348
|
+
return Qtrue;
|
349
|
+
}
|
350
|
+
return Qfalse;
|
351
|
+
}
|
352
|
+
|
342
353
|
void Init_subset_sum() {
|
343
354
|
VALUE SubsetSum;
|
344
355
|
SubsetSum = rb_define_module("SubsetSum");
|
345
356
|
rb_define_module_function(SubsetSum, "_subset_sum", rbss_main, 3);
|
357
|
+
rb_define_module_function(SubsetSum, "_subset_sum_supported?", rbss_supported, 4);
|
346
358
|
eTimeoutError = rb_define_class_under(SubsetSum, "TimeoutError", rb_eStandardError);
|
347
359
|
}
|
data/subset_sum.rb
CHANGED
@@ -1,73 +1,77 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
values
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
return
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
return
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
subsets(values[l
|
47
|
-
raise(TimeoutError, "timeout expired") if max_seconds and Time.now - start_time > max_seconds
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
1
|
+
# ruby-subset_sum implements a subset sum solver for ruby.
|
2
|
+
#
|
3
|
+
# Homepage: http://ruby-subsetsum.jeremyevans.net
|
4
|
+
# Source: https://github.com/jeremyevans/ruby-subset_sum
|
5
|
+
|
6
|
+
# This module provides both a C and pure ruby simple subset sum problem solver.
|
7
|
+
# The subset sum problem is, given a set of numbers, can the sum of any subset
|
8
|
+
# of those numbers equal a given number. This problem is NP-complete.
|
9
|
+
#
|
10
|
+
# Both the C and pure ruby versions implement a fairly simple
|
11
|
+
# meet-in-the-middle algorithm. The C version uses an AVL tree to store the
|
12
|
+
# data, while the pure ruby version uses a ruby hash. For the C version to be
|
13
|
+
# used, the sum of the positive numbers and the sum of the negative numbers in
|
14
|
+
# the set, as well as the wanted number, must all be Fixnums. Additionally,
|
15
|
+
# max_seconds should be nil or a Fixnum.
|
16
|
+
module SubsetSum
|
17
|
+
# Exception raised when timeout expires
|
18
|
+
class TimeoutError < StandardError
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return first subset of values that sum to want, using the meet in the
|
22
|
+
# middle algorithm (O(n * 2^(n/2)).
|
23
|
+
def self.subset_sum(values, want, max_seconds=nil)
|
24
|
+
raise(TypeError, "values must be an array of Integers") unless values.is_a?(Array)
|
25
|
+
raise(TypeError, "want must be an Integer") unless want.is_a?(Integer)
|
26
|
+
|
27
|
+
# Optimization by removing 0 values and doing some simple checks
|
28
|
+
values = values.reject{|x| x == 0}
|
29
|
+
values.each{|value| return [value] if value == want}
|
30
|
+
return values if sum(values) == want
|
31
|
+
pos, neg = values.partition{|x| x > 0}
|
32
|
+
sp, sn = sum(pos), sum(neg)
|
33
|
+
return pos if sp == want
|
34
|
+
return neg if sn == want
|
35
|
+
|
36
|
+
# Use the C version if it exists and all values will be inside the machine
|
37
|
+
# limits
|
38
|
+
if respond_to?(:_subset_sum, true) && _subset_sum_supported?(want, sp, sn, max_seconds.to_i)
|
39
|
+
return _subset_sum(values, want, max_seconds.to_i)
|
40
|
+
end
|
41
|
+
|
42
|
+
# The pure ruby version
|
43
|
+
sums = {}
|
44
|
+
start_time = Time.now if max_seconds
|
45
|
+
l = values.length/2
|
46
|
+
subsets(values[0...l]) do |subset|
|
47
|
+
raise(TimeoutError, "timeout expired") if max_seconds and Time.now - start_time > max_seconds
|
48
|
+
sums[sum(subset)] = subset
|
49
|
+
end
|
50
|
+
subsets(values[l..-1]) do |subset|
|
51
|
+
raise(TimeoutError, "timeout expired") if max_seconds and Time.now - start_time > max_seconds
|
52
|
+
if subset2 = sums[want - sum(subset)]
|
53
|
+
return subset2 + subset
|
54
|
+
end
|
55
|
+
end
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
# Yield all subsets of the array to the block.
|
60
|
+
def self.subsets(array, skip = 0, &block)
|
61
|
+
yield(array)
|
62
|
+
(array.length-1).downto(skip){|i| subsets(array[0...i] + array[i+1..-1], i, &block)}
|
63
|
+
end
|
64
|
+
|
65
|
+
# Return the sum of the values.
|
66
|
+
def self.sum(values)
|
67
|
+
values.inject(0){|x,y| x+=y}
|
68
|
+
end
|
69
|
+
|
70
|
+
private_class_method :subsets, :sum
|
71
|
+
end
|
72
|
+
|
73
|
+
begin
|
74
|
+
require 'subset_sum.so'
|
75
|
+
SubsetSum.send(:private_class_method, :_subset_sum)
|
76
|
+
rescue LoadError
|
77
|
+
end
|
metadata
CHANGED
@@ -1,71 +1,49 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: subset_sum
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease: false
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 0
|
9
|
-
- 1
|
10
|
-
version: 1.0.1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.2
|
11
5
|
platform: ruby
|
12
|
-
authors:
|
6
|
+
authors:
|
13
7
|
- Jeremy Evans
|
14
8
|
autorequire:
|
15
9
|
bindir: bin
|
16
10
|
cert_chain: []
|
17
|
-
|
18
|
-
date: 2010-11-20 00:00:00 -08:00
|
19
|
-
default_executable:
|
11
|
+
date: 2016-12-05 00:00:00.000000000 Z
|
20
12
|
dependencies: []
|
21
|
-
|
22
13
|
description:
|
23
14
|
email: code@jeremyevans.net
|
24
15
|
executables: []
|
25
|
-
|
26
|
-
extensions:
|
16
|
+
extensions:
|
27
17
|
- extconf.rb
|
28
18
|
extra_rdoc_files: []
|
29
|
-
|
30
|
-
files:
|
19
|
+
files:
|
31
20
|
- LICENSE
|
32
21
|
- extconf.rb
|
22
|
+
- spec/subset_sum_spec.rb
|
33
23
|
- subset_sum.c
|
34
24
|
- subset_sum.rb
|
35
|
-
-
|
36
|
-
has_rdoc: true
|
37
|
-
homepage: http://subset-sum.rubyforge.org/
|
25
|
+
homepage: http://ruby-subsetsum.jeremyevans.net/
|
38
26
|
licenses: []
|
39
|
-
|
27
|
+
metadata: {}
|
40
28
|
post_install_message:
|
41
29
|
rdoc_options: []
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
none: false
|
47
|
-
requirements:
|
30
|
+
require_paths:
|
31
|
+
- "."
|
32
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
48
34
|
- - ">="
|
49
|
-
- !ruby/object:Gem::Version
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
version: "0"
|
54
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
-
none: false
|
56
|
-
requirements:
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
57
39
|
- - ">="
|
58
|
-
- !ruby/object:Gem::Version
|
59
|
-
|
60
|
-
segments:
|
61
|
-
- 0
|
62
|
-
version: "0"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
63
42
|
requirements: []
|
64
|
-
|
65
|
-
|
66
|
-
rubygems_version: 1.3.7
|
43
|
+
rubyforge_project:
|
44
|
+
rubygems_version: 2.6.8
|
67
45
|
signing_key:
|
68
|
-
specification_version:
|
46
|
+
specification_version: 4
|
69
47
|
summary: Simple Subset Sum Solver with C and Pure Ruby Versions
|
70
|
-
test_files:
|
48
|
+
test_files:
|
71
49
|
- spec/subset_sum_spec.rb
|