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.
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