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