Reputation: 1437
I'm doing a ruby problem and my solution works. However, it does not work on bigger test cases because it takes too long to process.
The method takes two inputs, an array of numbers and a number.
The goal is to find the first two numbers in the array whose sum is the number provided in the input.
The first pair means that the highest index of the pair is lower than the highest index of any other pairs in the array that add to the number.
Example - ([10, 5, 2, 3, 7, 5], 10)
The answer should be [3, 7] while [5, 5] also sums to 10, the index of the last 5 is 5 and the index of 7 is 4, so [3, 7] comes first.
def sum_pairs(ints, s)
return nil if ints.empty? || ints.nil?
x = ints.combination(2).select {|x, y| x + y == s}
return nil if x.nil? || x.empty?
ints.rindex(x[0][1]) > ints.rindex(x[-1][1]) ? x.last : x.first
end
My code worked for all test cases besides those with large arrays.
I was wondering if there was a built in method to help or if I needed to loop through the array differently.
Upvotes: 2
Views: 3190
Reputation: 54223
With a Set
, only one pass is needed for this problem :
require 'set'
def sum_pairs(ints, s)
already_seen = Set.new
ints.each do |int|
return [s - int, int] if already_seen.include?(s - int)
already_seen.add(int)
end
nil
end
sum_pairs([10, 5, 2, 3, 7, 5], 10)
# [3, 7]
sum_pairs([10, 5, 2, 3, 7, 5], 20)
# nil
It outputs the result in the correct order and will be much faster than the other solutions.
Upvotes: 10
Reputation: 5213
An alternative approach might be to make sure your combinations are always in the correct order of preference, and then use #find
instead of #select
. The standard Ruby #combination(2)
pairs element 0 with all later elements ([10,5],[10,2],[10,3],[10,7],[10,5]) then pairs index 1 with all later elements ([5,2],[5,3],[5,7],[5,5]) and so forth.
You could write a new modified_combination
method that pairs element 1 with all earlier elements ([5,10]) then pairs element 2 with all earlier elements ([2,10],[2,5]), then element 3 ([3,10],[3,5],[3,2]) and so forth. This will ensure the pair with the smallest larger index number is always first in the list.
Now you can do:
x = ints.modified_combination.find { |x, y| x, y == s }.reverse
Remember, this will work most efficiently if modified_combination
returns an Enumerator rather than an Array (though it will be much more efficient than your original approach in any case).
If you don't want to dig into creating a method that returns an Enumerator, here's a quick-and-dirty solution that achieves the same thing:
def sum_pairs(ints, s)
(1...ints.size).each do |x|
(0...x).each do |y|
return [ints[y], ints[x]] if ints[x] + ints[y] == s
end
end
nil
end
Upvotes: 4