Compare sums of elements in an array: Ruby - ruby

I need to check whether the sum of any 2 elements of an array equals to the given number. This is what I came up with, but it doesn't seem to do the comparison
def sum_comparison(int_array, x)
n = int_array.length
(0..n).each do |i|
(1..n).each do |j|
if ((int_array[i].to_i + int_array[j].to_i) == x)
return true
else
return false
end
end
end
end

Your solution seems overly complicated and strongly influenced by the programming style of low-level procedural languages like C. One apparent problem is that you write
n = int_array.length
(0..n).each do |i|
# use int_array[i].to_i inside the loop
end
Now inside the each loop, you will get the numbers i = 0, 1, 2, ..., n, for example for int_array = [3,4,5] you get i = 0, 1, 2, 3. Notice that there are four elements, because you started counting at zero (this is called an off by one error). This will eventually lead to an array access at n, which is one beyond the end of the array. This will again result in a nil coming back, which is probably why you use to_i to convert that back to an integer, because otherwise you would get a TypeError: nil can't be coerced into Fixnum whend doing the addition. What you probably wanted instead was simply:
int_array.each do |i|
# use i inside the loop
end
For the example array [3,4,5] this would actually result in i = 3, 4, 5. To get the combinations of an array in a more Ruby way, you can for example use Array#combination. Likewise, you can use Array#any? to detect if any of the combinations satisfy the specified condition:
def sum_comparison(array, x)
array.combination(2).any? do |a, b|
a + b == x
end
end

When your function compare first element, it's immediately returns false. You need to return only true when iterating and return false at the end if nothing were found, to avoid this issue:
def sum_comparison(int_array, x)
n = int_array.size
(0...n).each do |i|
(1...n).each do |j|
if (int_array[i].to_i + int_array[j].to_i) == x
return true
end
end
end
false
end
To simplify this you can use permutation or combination and any? methods as #p11y suggests. To get founded elements you could use find or detect.
def sum_comparison(a, x)
a.combination(2).any? { |i, j| i + j == x }
end
a.combination(2).detect { |i, j| i + j == x }
# sum_comparison([1,2,3, 4], 6) => [2, 4]

Using an enumerator:
#!/usr/bin/env ruby
def sum_comparison(int_array, x)
enum = int_array.to_enum
loop do
n = enum.next
enum.peek_values.each do |m|
return true if (n + m) == x
end
end
false
end
puts sum_comparison([1, 2, 3, 4], 5)
Output:
true

Problem
Your method is equivalent to:
def sum_comparison(int_array, x)
return int_array[0].to_i + int_array[1].to_i == x
end
Therefore,
int_array = [1,2,4,16,32,7,5,7,8,22,28]
sum_comparison(int_array, 3) #=> true, just lucky!
sum_comparison(int_array, 6) #=> false, wrong!
Alternative
Here is a relatively efficient implemention, certainly far more efficient than using Enumerable#combination.
Code
def sum_comparison(int_array, x)
sorted = int_array.sort
smallest = sorted.first
sorted_stub = sorted.take_while { |e| e+smallest <= x }
p "sorted_stub = #{sorted_stub}"
return false if sorted_stub.size < 2
loop do
return false if sorted_stub.size < 2
v = sorted_stub.shift
found = sorted_stub.find { |e| v+e >= x }
return true if found && v+found == x
end
false
end
Examples
sum_comparison([7,16,4,12,-2,5,8], 3)
# "sorted_stub = [-2, 4, 5]"
#=> true
sum_comparison([7,16,4,12,-2,5,8], 7)
# "sorted_stub = [-2, 4, 5, 7, 8]"
#=> false
sum_comparison([7,16,4,22,18,12,2,41,5,8,17,31], 9)
# "sorted_stub = [2, 4, 5, 7]"
#=> true
Notes
The line p "sorted_stub = #{sorted_stub}" is included merely to display the array sorted_stub in the examples.
If e+smallest > x for any elements f and g in sorted for which g >= e and f < g, f+g >= e+smallest > x. Ergo, sorted_stub.last is the largest value in sorted that need be considered.
For a given value v, the line found = sorted_stub.find { |e| v+e >= x } stops the search for a second value e for which v+e = x as soon as it finds e such that v+e >= x. The next line then determines if a match has been found.

Related

Given an array, replace each prime number with the next prime number

So
#[11,13,17,23] => [13,17,19,29]
if the number isn't prime, then the function is just returning the number
#[11,8,2,24] => [13,8,3,24]
I'm having so much trouble with the very last number (29), for some reason, it's going into an infinite loop. I feel like there is such a simple solution, but it's just escaping me..
def next_prime(arr)
new_arr = []
arr.each do |num|
#puts num
if prime?(num)
p = false
i = num + 2
while !p
p = prime?(i)
i += 2
end
new_arr << i
else
new_arr << num
end
end
new_arr
end
EDIT: here is the prime function
def prime?(num)
if num ==1
return false
elsif num < 3
return true
elsif num % 2 == 0 || num % 3 == 0
return false
end
i = 5
while i*i <= num
if num % i == 0 || num % (i + 2) == 0
return false
end
i += 6
end
return true
end
The first array works decently for me, even the 29, except for the fact that everything is 2 higher than it should be, because you add 2 after you check if you have a prime which can be fixed by a slight alteration to the code:
if prime?(num)
p = false
i = num
while !p
i += 2
p = prime?(i)
end
new_arr << i
The only infinite loop I encounter is when you hit 2 in the second array, because to check for primes, you just keep incrementing by 2, and so you end up checking every multiple of 2 to see if it's prime. Naturally you never find a prime multiple of 2. To fix this, instead of incrementing by 2 before checking for the next prime, if you increment by 1 would work, you just need to check twice as many numbers:
if prime?(num)
p = false
i = num
while !p
i += 1
p = prime?(i)
end
new_arr << i
your last problem is that your prime? function returns false for 3, which can be fixed by changing:
elsif num <= 3
return true
and now your 2 samples yield:
[11, 13, 17, 23] => [13, 17, 19, 29]
[11, 8, 2, 24] => [13, 8, 3, 24]
Similar to #Cary Swoveland answer, but more idiomatic Ruby IMHO.
require 'prime'
def next_primes(ary)
ary.map { |candidate| candidate.prime? ? next_prime(candidate) : candidate }
end
def next_prime(previous_prime)
all_primes = Prime.each
all_primes.find { |prime| prime > previous_prime }
end
next_primes([11,8,2,24]) # => [13, 8, 3, 24]
next_primes([11,13,17,23]) # => [13, 17, 19, 29]
I've assumed the array arr is sorted. If it isn't, save the original indices of the sorted values, which are then used to reorder the elements of the array of values containing prime and non-prime numbers. For example, if
arr = [57, 13, 28, 6]
then
indices = arr.each_with_index.sort.map(&:last)
#=> [3, 1, 2 0]
The steps of the the main method are as follows.
Step 1: create an empty array a that will be returned by the method.
Step 2: create an enumerator, enum, that generates an infinite sequence of prime numbers (2, 3, 5, 7,..). Generate the first prime m = enum.next #=> 2.
Step 3: create an enumerator, earr, that generate the elements of the given array arr.
Step 4: consider the next element of the array, x = earr.next, which is initially the first element of the array. When there are no more elements of the array, break from the loop and return the array a.
Step 5: if x is not prime save it to an array a (a << x) and repeat step 4; else go to Step 6.
Step 6: (x is prime) if x < m, save m to the array a and go to
Step 4; else (i.e., m <= x), obtain the next prime (m = enum.next) and repeat this step.
require 'prime"
def next_primes(arr)
enum = Prime.each
m = enum.next
earr = arr.to_enum
x = earr.next
a = []
loop do
if x.prime?
if x < m
a << m
x = earr.next
else
m = enum.next
end
else
a << x
x = earr.next
end
end
a
end
next_primes([2, 6, 7, 23, 100])
#=> [3, 6, 11, 29, 100]
next_primes([2, 6, 7, 7, 100])
#=> [3, 6, 11, 11, 100]
Solution to find the next prime number if it is a prime number if not return the same number:
def is_prime(num)
if num<2
return false
end
(2...num).each do |i|
if num%i == 0
return false
end
end
return true
end
def next_prime(arr)
new_arr = []
arr.each do |num|
if is_prime(num)
p = false
i = num
while !p
i += 1
p = is_prime(i)
end
new_arr<<i
else
new_arr<<num
end
end
return new_arr
end
print next_prime([2,3,4,5])
puts
print next_prime([2, 6, 7, 23, 100]) #[3, 6, 11, 29, 100]

Ruby code looping infinitely

class Triplet
def initialize(array,sum)
#array = array.sort()
#array_size = array.size()
#sum = sum
#result = []
end
def get_triplet
#array[0..-3].each_with_index do |arr, ind|
pointer_one = ind + 1
pointer_two = #array_size - 1
while (pointer_one < pointer_two)
temp_sum = #array[pointer_one] + #array[pointer_two] + arr
if(temp_sum == #sum)
#result.push([#array[pointer_one], #array[pointer_two], arr])
elsif temp_sum < #sum
pointer_one = pointer_one +1
else
pointer_two = pointer_two -1
end
end
end
end
def get_result
#result.each do |res|
puts res
end
end
end
puts "Enter the array of numbers"
array = gets.chomp
array = array.split(' ')
array_integer = array.map{|a| a.to_i}
puts array_integer
puts "Enter the sum"
sum = gets.chomp
puts sum
t1 = Triplet.new(array_integer,sum.to_i)
t1.get_triplet
t1.get_result
Can anyone suggest me the fix so that it doesn't loop infinitly. It is program to find triplet in array whose sum is #sum. Its looping in get_triplet method. Initialize method sets the array,array size. get_triplet method should store all three number whose sum is #sum in result array.
Usually a tangle of code like this is a sign something's not right, and in this case the source of the problem is not knowing about the combination method. Here's a functionally equivalent solution:
def triplet(list, target)
list.combination(3).find do |a,b,c|
a + b + c == target
end
end
For example:
arr = [ 1, 2, 3, 4, 5, 6, 7, 8 ]
p triplet(arr, 6)
# => [1, 2, 3]
p triplet(arr, 4)
# => nil
p triplet(arr, 10)
# => [1, 2, 7]
The algorithm used in your code looks problematic, or at least implemented incorrectly, and is also strictly limited to triplets. This code is far more generic and uses a proven, tested algorithm, so it's probably better suited to solving your particular problem.

Get max consecutive occurrences of value in array

Is there a more elegant way to achieve this below:
Input:
array = [1, 1, 1, 0, 0, 1, 1, 1, 1, 0]
Output:
4
My algo:
streak = 0
max_streak = 0
arr.each do |n|
if n == 1
streak += 1
else
max_streak = streak if streak > max_streak
streak = 0
end
end
puts max_streak
Similar to w0lf's answer, but skipping elements by returning nil from chunk:
array.chunk { |x| x == 1 || nil }.map { |_, x| x.size }.max
Edit: Another way to do this (that is less generic than Stefan's answer since you would have to flatten and split again if there was another number other than 0 and 1 in there, but easier to use in this case):
array.split(0).max.count
You can use:
array.chunk { |n| n }.select { |a| a.include?(1) }.map { |y, ys| ys.count}.max
ref: Count sequential occurrences of element in ruby array
You can use Enumerable#chunk:
p array.chunk{|x| x}.select{|x, xs| x == 1}.map{|x, xs| xs.size }.max
This is more concise, but if performance was important, I'd use your approach.
Edit: If you're in Ruby 2.2.2, you can also use the new Enumerable#slice_when method (assuming your input array consists of only 0s and 1s):
array.slice_when{|x,y| x < y }.map{|slice| slice.count 1 }.max
How about
array = [1, 1, 1, 0, 0, 1, 1, 1, 1, 0]
array.split(0).group_by(&:size).max.first #=> 4
The only bad thing - split(0)
Note: This only works with rails's ActiveSupport(extends Array with #split)
For ruby-only implementation
array.join.split("0").group_by(&:size).max.first #=> 4

Using hash to tell positive, odd, even, and negative numbers

I have an array:
ak = [10, 20, 3, 4, 5, -5, 28, 27]
I want a solution like this:
#even:4
#odd:3
#positive:7
#negative:1
How do I use hash to do that?
You could do this in a fairly general (reusable) way as follows.
Code
def analyze_array(ak, ops)
ops.each_with_object({}) { |(k,m),h| h.update(k=>ak.count(&m)) }
end
Example
ak = [10, 20, 3, 4, 5, -5, 28, 27]
ops = [[:even, :even? ],
[:odd, :odd? ],
[:positive, ->(n) { n>0 }],
[:negative, ->(n) { n<0 }]]
analyze_array(ak, ops)
#=> {:even=>4, :odd=>4, :positive=>7, :negative=>1}
Explanation
For the example above:
enum = ops.each_with_object({})
#=> #<Enumerator: [[:even, :even?], [:odd, :odd?],
# [:positive, #<Proc:0x007fe90395aaf8#(irb):9 (lambda)>],
# [:negative, #<Proc:0x007fe90395aaa8#(irb):10 (lambda)>]]
# :each_with_object({})>
Note that :even? and :odd? are symbols (not to be confused with methods).
The elements of enum are passed into the block by Enumerator#each, which calls Array#each. We can see what the elements of enum are by converting it to an array:
enum.to_a
#=> [[[:even, :even?], {}], [[:odd, :odd?], {}],
# [[:positive, #<Proc:0x007fe90395aaf8#(irb):9 (lambda)>], {}],
# [[:negative, #<Proc:0x007fe90395aaa8#(irb):10 (lambda)>], {}]]
and simulate the passing of the (4) elements of enum into the block with Enumerator#next. The first element of enum ([[:even, :even?], {}]) is passed to the block and assigned to the block variables:
(k,m),h = enum.next
#=> [[:even, :even?], {}]
k #=> :even
m #=> :even?
h #=> {}
Next, we use Hash#update (aka merge!) to merge a one-key hash into h and return the new value of h:
h.update(k=>ak.count(&m))
#=> {}.update(:even=>[10, 20, 3, 4, 5, -5, 28, 27].count(&:even?))
#=> {:even=>4}
(Ruby allows us to write (k=>ak.count(&m)) as shorthand for ({k=>ak.count(&m)})).
As usual, & invokes Symbol#to_proc to convert the symbol :even? to a proc and then converts the proc to a block for count.
The next value of enum is then passed to the block ("odd"), similar calculations are performed and the hash { :odd=>4 } is merged into h, resulting in:
h #=> { :even=>4, :odd=>4 }
The third and fourth values of enum are then passed to the block. The only difference is that m in ak.count(&m) is already a proc (a lambda, actually), so & just converts it to a block for count.
h = Hash.new
h["even"] = ak.select {|x| x.even? && x > 0}.count
h["odd"] = ak.select {|x| x.odd? && x > 0}.count
h["positive"] = ak.select {|x| x > 0}.count
h["negative"] = ak.select {|x| x < 0}.count
and add
put h
Another solution:
ak = [10, 20, 3, 4, 5, -5, 28, 27]
akp = ak.select{ |n| n > 0 }
h = {
even: akp.count(&:even?),
odd: akp.count(&:odd?),
positive: akp.count,
negative: ak.count{ |n| n < 0 }
}
puts ak, h
Assuming (based on the output that you expect) that you only want the positive even or odd numbers:
h = Hash.new
h["even"] = ak.select {|x| x.even? && x > 0}.count
h["odd"] = ak.select {|x| x.odd? && x > 0}.count
h["positive"] = ak.select {|x| x > 0}.count
h["negative"] = ak.select {|x| x < 0}.count
puts h
You can iterate over you array and test each value like this:
def evaluate(array)
response = { even: 0, odd: 0, positive: 0, negative: 0 }
array.each do |item|
response[:even] += 1 if item.even?
response[:odd] += 1 if item.odd?
...
end
response
end
Or something like that. You can optimize it after.
def calculate(arr)
even = arr.select {|e| e.even?}.size
odd = arr.select {|e| e.odd?}.size
pos = arr.select {|e| e >= 0}.size
neg = arr.select {|e| e < 0}.size
hash = {even: even, odd: odd, positive: pos, negative:neg}
end

Checking to see if 2 numbers in array sum to 0 in Ruby

I've been going at this problem for a few hours, and I can't see why I can't get it to run properly. The end game to this method is having 2 numbers in an array equaling zero when added together. Here is my code:
def two_sums(nums)
i = 0
j = -1
while i < nums.count
num_1 = nums[i]
while j < nums.count
num_2 = nums[j]
if num_1 + num_2 == 0
return "There are 2 numbers that sum to zero & they are #{num_1} and #{num_2}."
else
return "Nothing adds to zero."
end
end
i += 1
j -= 1
end
end
The problem I'm having is unless the first and last number in the array are the positive and negative of the same number, this will always return false.
For example, if I had an array that was [1, 4, 6, -1, 10], it should come back true. I'm sure my 2 while statement is the cause of this, but I can't think of a way to fix it. If someone could point me in the right direction, that would be helpful.
You can find the first pair that adds up to 0 like this:
nums.combination(2).find { |x, y| x + y == 0 }
#=> returns the first matching pair or nil
Or if you want to select all pairs that add up to 0:
nums.combination(2).select { |x, y| x + y == 0 }
#=> returns all matching pairs or an empty array
Therefore you can implement your method like this:
def two_sums(nums)
pair = nums.combination(2).find { |x, y| x + y == 0 }
if pair
"There are 2 numbers that sum to zero & they are #{pair.first} and #{pair.last}."
else
"Nothing adds to zero."
end
end
Or if you want to find all pairs:
def two_sums(nums)
pairs = nums.combination(2).select { |x, y| x + y == 0 }
if pairs.empty?
"Nothing adds to zero."
else
"The following pairs sum to zero: #{pairs}..."
end
end
Here's another way:
Code
def sum_to_zero(arr)
arr.group_by { |e| e.abs }
.values
.select { |a| (a.size > 1 && a.first == 0) || a.uniq.size > 1 }
end
Examples
sum_to_zero [1, 4, 6, -1, 10] #=> [[1, -1]]
sum_to_zero [1, 4, 1, -2, 10] #=> []
sum_to_zero [1, 0, 4, 1, 0, -1] #=> [[1, 1, -1], [0, 0]]
This method is relatively fast. Let's try it with an array of 200,000 elements, each a random number between -500,000 and 500,000.
require 'time'
t = Time.now
arr = Array.new(200_000) { rand(1_000_001) - 500_000 }
arr.size #=> 200000
sum_to_zero(arr).size #=> 16439
Time.now - t
#=> 0.23 (seconds)
sum_to_zero(arr).first(6)
#=> [[-98747, 98747],
# [157848, -157848],
# [-459650, 459650],
# [176655, 176655, -176655],
# [282101, -282101],
# [100886, 100886, -100886]]
If you wish to group the non-negative and negative values that sum to zero:
sum_to_zero(arr).map { |a| a.partition { |e| e >= 0 } }.first(6)
#=> [[[98747], [-98747]],
# [[157848], [-157848]],
# [[459650], [-459650]],
# [[176655, 176655], [-176655]],
# [[282101], [-282101]],
# [[100886, 100886], [-100886]]]
If you only want a single value for each group (a non-negative value, say):
sum_to_zero(arr).map { |a| a.first.abs }.first(6)
#=> [98747, 157848, 459650, 176655, 282101, 100886]
I think the most Ruby way would be:
nums.combination(2).any? { |x,y| (x+y).zero? }
Here's a way that should work well for large arrays. The methods above which go through every possible combination of two numbers are perfectly fine for small cases but will be very slow and memory hungry for arrays with lots of elements.
def two_sums nums
h = Hash.new
nums.each do |n|
return true if h[-n]
h[n] = true
end
false
end
Well, given it's tagged as #ruby, here's the most "ruby way" I could think of tackling this problem:
def two_sums(arr)
numbers = arr.combination(2).select { |a| a.reduce(:+) == 0 }.flatten
if numbers.empty?
"Nothing adds to zero."
else
"There are 2 numbers that sum to zero & they are #{numbers.first} and #{numbers.last}."
end
end
array.combination(2).select{|x|x[0] + x[1] == 0}

Resources