What is the right way to write ruby code? - ruby

I am solving the pyramid problem, in which an array is reduced to a single element over time by subtracting two consecutive numbers in each iteration.
input: [1, 5, 9, 2, 3, 5, 6]
iterations
[4, 4, -7, 1, 2, 1],
[0, -11, 8, 1, -1],
[-11, 19, -7, -2],
[30, -26, 5],
[-56, 31],
[87]
output: 87
What is the best way or ruby way to solve this problem? This can be done by inheriting array and making a new class, but I don't know how. Please help. I write this code to solve it:
a = [1,5,9,2,3,5,6]
class Array
def pyr
a = self.each_cons(2).to_a.map! { |e| e[1] - e[0] }
a
end
end
while a.length > 1
a = a.pyr
ans = a[0]
end
p ans

I see three ways to approach this.
Reopen the Array class
Sure, if in your particular ruby script/project this is an elementary functionality of an array, reopen the class. But if you are going to re-open a class, at least make sure the name is something meaningful. pyr? Why not write a full name, so no conflicts are possible, something like next_pyramid_iteration (I have never heard of this pyramid problem, so excuse me if I am way of base here).
Make a class inherit from Array
class Pyramid < Array
def next_iteration
self.each_const(2).map! { |e| e[1] - e[o] }
end
end
and then your calculation would become something like
pyramid = Pyramid.new([1,5,9,2,3,5,6])
while pyramid.length > 1
pyramid.next_iteration
end
pyramid[0]
Make a specific class to do the calculation
I am not quite sure what you are trying to achieve, but why not just make a specific class that knows how to calculate pyramids?
class PyramidCalculator
def initialize(arr)
#pyramid = arr
end
def calculate
while #pyramid.length > 1
do_next_iteration
end
#pyramid.first
end
def self.calculate(arr)
PyramidCalculator.new(arr).calculate
end
protected
def do_next_iteration
#pyramid = #pyramid.each_const(2).map! { |e| e[1] - e[o] }
end
end
because I added a convenience class-method, you can now calculate a result as follows:
PyramidCalculator.calculate([1,5,9,2,3,5,6])
My personal preference would be the last option :)

I would just do it as a two-liner.
a = a.each_cons(2).map{|e1, e2| e2 - e1} while a[1]
a.first # => 87

It's certainly easy enough to turn this into a simple function without hacking on the Array class:
def pyr(ary)
return ary[0] if ary.length < 2
pyr(ary.each_cons(2).map { |e| e[1] - e[0] })
end
p pyr [1,5,9,2,3,5,6] # => 87
Use return ary if you want the answer as a one-element array rather than a scalar.
If you prefer iteration to recursion or have a very large array:
def pyr(ary)
ary = ary.each_cons(2).map { |e| e[1] - e[0] } while ary.length > 1
ary
end
By encapsulating this as a function rather than doing it inline, you get the ability to do the operation on any number of arrays plus it's non-destructive on the original input array.

It's not necessary to compute the end value by successive computation of differences, which requires (n*(n-1)/2 subtractions and the same number of additions, where n is the size of the array a. Instead, we can compute that value by summing n terms of the form:
(-1)K+ibin_coeff(n-1,i)*a[i]
for i = 0..(n-1), where:
K equals 0 if the array has an even number of elements, else K equals 1; and
bin_coeff(n,i) is the binomial coefficient for choosing "n items i at a time" (n!/i!*(n-i)!).
I know what you're thinking: the calculation of each binomial coefficient will take some work. True, but that can be done in an efficient way (which I've not done below), by computing bin_coeff(n-1,i+1) from bin_coeff(n-1,i), etc. Of course, that's academic, as no one is likely to actually use the method I'm suggesting.
(I'm hoping nobody will demand a proof, but I'll try to oblige if a request is made.)
Code
class Fixnum
def factorial
(1..self).reduce(1) { |t,i| t*i }
end
def bin_coeff m
self.factorial/(m.factorial*(self-m).factorial)
end
end
def pyramid_sum(a)
n = a.size-1
sign = n.even? ? -1 : 1
(0..n).reduce(0) do |t,i|
sign = -sign
t + sign * n.bin_coeff(i) * a[i]
end
end
Examples
pyramid_sum [1, 5] #=> 4
pyramid_sum [1, 5, 9] # #=> 0
pyramid_sum [1, 5, 9, 2] #=> -11
pyramid_sum [1, 5, 9, 2, 3] #=> 30
pyramid_sum [1, 5, 9, 2, 3, 5] #=> -56
pyramid_sum [1, 5, 9, 2, 3, 5, 6] #=> 87

Related

How to improve algorithm efficiency for nested loop

Given a list of integers and a single sum value, return the first two values (from the left) that add up to form the sum.
For example, given:
sum_pairs([10, 5, 2, 3, 7, 5], 10)
[5, 5] (at indices [1, 5] of [10, 5, 2, 3, 7, 5]) add up to 10, and [3, 7] (at indices [3, 4]) add up to 10. Among them, the entire pair [3, 7] is earlier, and therefore is the correct answer.
Here is my code:
def sum_pairs(ints, s)
result = []
i = 0
while i < ints.length - 1
j = i+1
while j < ints.length
result << [ints[i],ints[j]] if ints[i] + ints[j] == s
j += 1
end
i += 1
end
puts result.to_s
result.min
end
It works, but is too inefficient, taking 12000 ms to run. The nested loop is the problem of inefficiency. How could I improve the algorithm?
Have a Set of numbers you have seen, starting empty
Look at each number in the input list
Calculate which number you would need to add to it to make up the sum
See if that number is in the set
If it is, return it, and the current element
If not, add the current element to the set, and continue the loop
When the loop ends, you are certain there is no such pair; the task does not specify, but returning nil is likely the best option
Should go superfast, as there is only a single loop. It also terminates as soon as it finds the first matching pair, so normally you wouldn't even go through every element before you get your answer.
As a style thing, using while in this way is very unRubyish. In implementing the above, I suggest you use ints.each do |int| ... end rather than while.
EDIT: As Cary Swoveland commented, for a weird reason I thought you needed indices, not the values.
require 'set'
def sum_pairs(arr, target)
s = Set.new
arr.each do |v|
return [target-v, v] if s.include?(target-v)
s << v
end
nil
end
sum_pairs [10, 5, 2, 3, 7, 5], 10
#=> [3, 7]
sum_pairs [10, 5, 2, 3, 7, 5], 99
#=> nil
I've used Set methods to speed include? lookups (and, less important, to save only unique values).
Try below, as it is much more readable.
def sum_pairs(ints, s)
ints.each_with_index.map do |ele, i|
if ele < s
rem_arr = ints.from(i + 1)
rem = s - ele
[ele, rem] if rem_arr.include?(rem)
end
end.compact.last
end
One liner (the fastest?)
ary = [10, 0, 8, 5, 2, 7, 3, 5, 5]
sum = 10
def sum_pairs(ary, sum)
ary.map.with_index { |e, i| [e, i] }.combination(2).to_a.keep_if { |a| a.first.first + a.last.first == sum }.map { |e| [e, e.max { |a, b| a.last <=> b.last }.last] }.min { |a, b| a.last <=> b.last }.first.map{ |e| e.first }
end
Yes, it's not really readable, but if you add methods step by step starting from ary.map.with_index { |e, i| [e, i] } it's easy to understand how it works.

Add diagonal difference of matrix

HackerRank Diagonal Difference Problem.
Print the absolute difference between the sums of the matrix's two diagonals as a single integer.
Link to problem: https://www.hackerrank.com/challenges/diagonal-difference/problem
I have been trying for hours to solve this problem in Ruby. I happened upon an answer that someone else figured out.
I am now please asking for help in understanding how this method works. I want to understand what it is doing. I am confused by the entirety of the loop.
a.each_with_index do |array, index|
left_right += array[index]
right_left += array[-index-1]
Could someone please explain step-by-step what is happening in this block of code so I can learn and better understand Ruby? Thank you.
def diagonalDifference(a)
left_right=0
right_left=0
a.each_with_index do |array, index|
left_right += array[index]
right_left += array[-index-1]
end
v = right_left - left_right
return v.abs
end
The main skew diagonal (or main secondary diagonal) of a nxn matrix a is comprised of the elements a[n-1][0], a[n-2][1],..., a[1][n-2], a[0][n-1]].
Part of the problem is that the variables have not been given descriptive names. I would write that as follows.
def diagonal_difference(arr)
main_diagonal_sum=0
main_skew_diagonal_sum=0
arr.each_with_index do |row, i|
main_diagonal_sum += row[i]
main_skew_diagonal_sum += row[-i-1]
end
(main_diagonal_sum - main_skew_diagonal_sum).abs
end
I expect row[-i-1] might be the most confusing part of the code. Suppose i = 0, then row[-0-1] #=> row[-1], which is the last element of row. When i = 1, row[-1-1] #=> row[-2], which is the next-to-last element of row, and so on. That could instead be written row[row.size-i-1].
Note return is not needed if, as here, the return value of the last statement executed (before the method returns) is to be returned by the method.
Let's add some puts statements in the method and work through an example.
def diagonal_difference(arr)
puts "arr=#{arr}"
main_diagonal_sum=0
main_skew_diagonal_sum=0
arr.each_with_index do |row, i|
puts "row=#{row}, i=#{i}"
main_diagonal_sum += row[i]
puts " row[#{i}]=#{row[i]}, main_diagonal_sum=#{main_diagonal_sum}"
main_skew_diagonal_sum += row[-i-1]
puts " row[-#{i}-1]=#{row[-i-1]}, main_skew_diagonal_sum=#{main_skew_diagonal_sum}"
end
(main_diagonal_sum - main_skew_diagonal_sum).abs
end
arr = [[1,2,3],
[4,5,6],
[9,8,7]]
The main diagonal sum is 1+5+7 #=> 13 and the main skew diagonal sum is 3+5+9 #=> 17, so we expect the method to return (13-17).abs #=> 4.
diagonal_difference(arr)
#=> 4
prints the following.
arr=[[1, 2, 3], [4, 5, 6], [9, 8, 7]]
row=[1, 2, 3], i=0
row[0]=1, main_diagonal_sum=1
row[-0-1]=3, main_skew_diagonal_sum=3
row=[4, 5, 6], i=1
row[1]=5, main_diagonal_sum=6
row[-1-1]=5, main_skew_diagonal_sum=8
row=[9, 8, 7], i=2
row[2]=7, main_diagonal_sum=13
row[-2-1]=9, main_skew_diagonal_sum=17
This can alternatively be written as follows. We first need a helper method to rotate a square array 90 degrees.
def rotate90(arr)
arr.map(&:reverse).transpose
end
arr = [[1,2,3],
[4,5,6],
[9,8,7]]
arr90 = rotate90(arr)
#=> [[3, 6, 7],
# [2, 5, 8],
# [1, 4, 9]]
Next, I'll add a second helper to sum the main diagonal of a square matrix. That's easiest by using Matrix#trace.
require 'matrix'
def trace(arr)
Matrix[*arr].trace
end
We therefore may write the following.
(trace(arr90) - trace(arr)).abs
#=> (17 - 13).abs => 4.abs => 4
See following
11 2 4
4 5 6
10 8 -12
So, a = [ [11, 2, 4], [4, 5, 6], [10, 8, -12] ]
Now ref each_with_index method for a.each_with_index do |array, index|. during first iteration array will be [11, 2, 4] & index will be 0. array[0] = 11 & array[-0-1] i.e. array[-1] = 4
Similarly for second iteration array[1] = 5 & array[-1-1] i.e. array[-2] = 5& so on.
You'll get
2.3.1 :360 > left_right # 11 + 5 - 12
=> 4
2.3.1 :361 > right_left # 4 + 5 + 10
=> 19
2.3.1 :362 > v = right_left - left_right
=> 15
v.abs is used to return absolute difference in case v is negative, Ref abs method of Numeric class.
Note:- return keyword is optional if it is the last non comment line in a method.
You can use the Matrix library as proposed in this answer.
require 'matrix'
(Matrix[*arr].tr - Matrix[*arr.reverse].tr).abs
Where arr is an array of depth 2 with length n and each sub-array is also of length n e.g. [[a,b],[c,d]].
Sidenote: NB I am posting this as an answer, not a comment, for the sake of formatting; it should not be upvoted.
The more ruby idiomatic version of the snippet you have posted would be:
def diagonal_difference(a)
a.each.with_object([0, 0]).with_index do |(e, left_right), idx|
left_right[0] += array[idx]
right_left[1] += array[-idx-1]
end.reduce(:-).abs
end

idiomatic way to check if array contains ordered (but possibly non-continuous) set of elements

I was wondering if there is a more idiomatic way to get the functionality represented by the code below. Basically I just want to check if the array contains the elements in pattern in the order specified by pattern. It's okay for there to be gaps between these elements.
class Array
def has_pattern?(pattern)
offset = 0
pattern.each do |p|
offset = self[offset..-1].index(p)
return false if offset.nil?
end
return true
end
end
puts [1, 2, 3, 4, 5, 1].has_pattern?([1, 4, 5]) # true
puts [1, 2, 3, 4, 5, 1].has_pattern?([2, 3, 1]) # true
puts [1, 2, 3, 4, 5, 1].has_pattern?([1, 3, 2]) # false
The code above seems to work, but doesn't feel like idiomatic Ruby to me. Is there a nicer way to write this?
Here's my take on it:
class Array
def has_pattern?(ptn)
i = 0
self.each do |elem|
i += 1 if elem == ptn[i]
end
i >= ptn.size
end
end
It passes through the array only once, so it may make a difference when the array's big.
Here's a different way to approach it:
class Array
def has_pattern?(pattern)
(self - (self - pattern))
.each_cons(pattern.length)
.any? { |p| p === pattern }
end
end
But, as I said in the comments above, I think your solution is superior.

How can I remove duplicates in an array without using `uniq`?

The object of my coding exercise is to get rid of duplicates in an array without using the uniq method. Here is my code:
numbers = [1, 4, 2, 4, 3, 1, 5]
def my_uniq(array)
sorted = array.sort
count = 1
while count <= sorted.length
while true
sorted.delete_if {|i| i = i + count}
count += 1
end
end
return sorted
end
When I run this, I get an infinite loop. What is wrong?
Can I use delete the way that I am doing with count?
How will it execute? Will count continue until the end of the array before the method iterates to the next index?
I did this with each or map, and got the same results. What is the best way to do this using each, delete_if, map, or a while loop (with a second loop that compares against the first one)?
Here is a clearly written example.
numbers = [1, 4, 2, 4, 3, 1, 5]
def remove_duplicates(array)
response = Array.new
array.each do |number|
response << number unless response.include?(number)
end
return response
end
remove_duplicates(numbers)
As others pointed out, your inner loop is infinite. Here's a concise solution with no loops:
numbers.group_by{|n| n}.keys
You can sort it if you want, but this solution doesn't require it.
the problem is that the inner loop is an infinite loop:
while true
sorted.delete_if {|i| i = i + count}
count += 1
end #while
you can probably do what you are doing but it's not eliminating duplicates.
one way to do this would be:
numbers = [1, 4, 2, 4, 3, 1, 5]
target = []
numbers.each {|x| target << x unless target.include?(x) }
puts target.inspect
to add it to the array class:
class ::Array
def my_uniq
target = []
self.each {|x| target << x unless target.include?(x) }
target
end
end
now you can do:
numbers = [1, 4, 2, 4, 3, 1, 5]
numbers.my_uniq
You count use Set that acts like an array with does not allow duplicates:
require 'set'
numbers = [1, 4, 2, 4, 3, 1, 5]
Set.new(numbers).to_a
#=> [1, 4, 2, 3, 5]
Try using Array#& passing the array itself as parameter:
x = [1,2,3,3,3]
x & x #=> [1,2,3]
This is one of the answer. However, I do not know how much of performance issue it takes to return unique
def my_uniq(ints)
i = 0
uniq = []
while i < ints.length
ints.each do |integers|
if integers == i
uniq.push(integers)
end
i += 1
end
end
return uniq
end

What is clearest way to add up all elements in 2 dimensional array by position using Ruby?

What is the clearest and most efficient way to add all the elements in 2D array by their position in Ruby. Example:
2darray = [[1,2,3],[1,2,3]]
result = [2,4,6]
I have the following code
def sum_elements_by_position(array)
total_elements = array.length
result = []
for i in 0...array.first.length
n = 0
array.each { |subarray| n += subarray[i] }
result << n
end
result
end
Assumptions: All primary elements are of the same length
For bonus points it would be great to see a solution that works primary elements of an arbitrary length
You can zip the first row with the rest of them and then do the sum:
def sum_elements_by_position(array)
array[0].zip(*array[1..-1]).map do |col|
col.inject(:+)
end
end
Here's a solution addressing when the rows aren't the same length.
def sum_cols arr
arr.reduce( [] ) do |res,row|
row.each_with_index { |e,i| res[i] ||= 0; res[i] += e }
res
end
end
irb> sum_cols [ [0,1,2], [3,4], [5,6,7,8] ]
=> [8, 11, 9, 8]
#oldergod suggested using zip based on the longest row, but finding the longest row and rejecting nils has a cost. I benchmarked the following against the above method using the example array above and found the reduce+each_with_index method more than 30% faster:
def sum_cols_using_zip arr
max_len = arr.map(&:size).max
([0] * max_len).zip(*arr).map do |col|
col.compact.inject(:+)
end
end
I'd do this:
a.transpose.map {|x| x.reduce(:+)}
Clean, simple, flexible. The .transpose turns this
[[1,2,3,4],[2,3,4,5],[3,4,5,6]]
into this
[[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]]
and then .map applies .reduce to each subarray. And .reduce, in turn, aggregates the subvalues by adding them. Or, more precisely, by applying the + method to them.
I highly recommend reading the doc for these functions until you fully understand this example, as it's a pretty good succinct demonstration of how to think in a Rubyish way!

Resources