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.
Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -1
  3. data/spec/subset_sum_spec.rb +20 -19
  4. data/subset_sum.c +12 -0
  5. data/subset_sum.rb +77 -73
  6. metadata +24 -46
@@ -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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2008 Jeremy Evans
1
+ Copyright (c) 2008,2010,2015,2016 Jeremy Evans
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to
@@ -1,47 +1,48 @@
1
- #!/usr/bin/env spec
2
1
  require 'subset_sum'
2
+ require 'minitest/autorun'
3
3
 
4
- context "SubsetSum.subset_sum" do
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)}.should raise_error(TypeError)
7
- proc{SubsetSum.subset_sum(nil, 1)}.should raise_error(TypeError)
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])}.should raise_error(TypeError)
12
- proc{SubsetSum.subset_sum([1,2,3], nil)}.should raise_error(TypeError)
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).should == nil
17
- SubsetSum.subset_sum([1,2,3], 7).should == nil
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).should == []
22
- SubsetSum.subset_sum([1,2,3], 1).should == [1]
23
- SubsetSum.subset_sum([1,2,3], 2).should == [2]
24
- [[1,2], [3]].should include(SubsetSum.subset_sum([1,2,3], 3))
25
- SubsetSum.subset_sum([1,2,3], 4).should == [1,3]
26
- SubsetSum.subset_sum([1,2,3], 5).should == [2,3]
27
- SubsetSum.subset_sum([1,2,3], 6).should == [1,2,3]
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}.should == s
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}}.should raise_error(SubsetSum::TimeoutError)
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.should_not_receive(:_subset_sum)
44
- SubsetSum.subset_sum([2**100, 1, 2,], 1+2**100).should == [2**100, 1]
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
 
@@ -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
  }
@@ -1,73 +1,77 @@
1
- # This module provides both a C and pure ruby simple subset sum problem solver.
2
- # The subset sum problem is, given a set of numbers, can the sum of any subset
3
- # of those numbers equal a given number. This problem is NP-complete.
4
- #
5
- # Both the C and pure ruby versions implement a fairly simple
6
- # meet-in-the-middle algorithm. The C version uses an AVL tree to store the
7
- # data, while the pure ruby version uses a ruby hash. For the C version to be
8
- # used, the sum of the positive numbers and the sum of the negative numbers in
9
- # the set, as well as the wanted number, must all be Fixnums. Additionally,
10
- # max_seconds should be nil or a Fixnum.
11
- module SubsetSum
12
- # Exception raised when timeout expires
13
- class TimeoutError < StandardError
14
- end
15
-
16
- # Return first subset of values that sum to want, using the meet in the
17
- # middle algorithm (O(n * 2^(n/2)).
18
- def self.subset_sum(values, want, max_seconds=nil)
19
- raise(TypeError, "values must be an array of Integers") unless values.is_a?(Array)
20
- raise(TypeError, "want must be an Integer") unless want.is_a?(Integer)
21
-
22
- # Optimization by removing 0 values and doing some simple checks
23
- values = values.reject{|x| x == 0}
24
- values.each{|value| return [value] if value == want}
25
- return values if sum(values) == want
26
- pos, neg = values.partition{|x| x > 0}
27
- sp, sn = sum(pos), sum(neg)
28
- return pos if sp == want
29
- return neg if sn == want
30
-
31
- # Use the C version if it exists and all values will be inside the machine
32
- # limits
33
- return _subset_sum(values, want, max_seconds.to_i) if \
34
- respond_to?(:_subset_sum, true) and want.is_a?(Fixnum) and \
35
- sum(pos).is_a?(Fixnum) and sum(neg).is_a?(Fixnum) and \
36
- max_seconds.to_i.is_a?(Fixnum)
37
-
38
- # The pure ruby version
39
- sums = {}
40
- start_time = Time.now if max_seconds
41
- l = values.length/2
42
- subsets(values[0...l]) do |subset|
43
- raise(TimeoutError, "timeout expired") if max_seconds and Time.now - start_time > max_seconds
44
- sums[sum(subset)] = subset
45
- end
46
- subsets(values[l..-1]) do |subset|
47
- raise(TimeoutError, "timeout expired") if max_seconds and Time.now - start_time > max_seconds
48
- if subset2 = sums[want - sum(subset)]
49
- return subset2 + subset
50
- end
51
- end
52
- nil
53
- end
54
-
55
- # Yield all subsets of the array to the block.
56
- def self.subsets(array, skip = 0, &block)
57
- yield(array)
58
- (array.length-1).downto(skip){|i| subsets(array[0...i] + array[i+1..-1], i, &block)}
59
- end
60
-
61
- # Return the sum of the values.
62
- def self.sum(values)
63
- values.inject(0){|x,y| x+=y}
64
- end
65
-
66
- private_class_method :subsets, :sum
67
- end
68
-
69
- begin
70
- require 'subset_sum.so'
71
- SubsetSum.send(:private_class_method, :_subset_sum)
72
- rescue LoadError
73
- end
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
- hash: 21
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
- - spec/subset_sum_spec.rb
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
- require_paths:
44
- - .
45
- required_ruby_version: !ruby/object:Gem::Requirement
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
- hash: 3
51
- segments:
52
- - 0
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
- hash: 3
60
- segments:
61
- - 0
62
- version: "0"
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
63
42
  requirements: []
64
-
65
- rubyforge_project: subset-sum
66
- rubygems_version: 1.3.7
43
+ rubyforge_project:
44
+ rubygems_version: 2.6.8
67
45
  signing_key:
68
- specification_version: 3
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