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
Related
I've been using a piece of Ruby code that I found here.
Here's the code:
a = [1, 4, 7, 13]
def add(ary, idx, sum)
(idx...ary.length).each do |i|
add(ary, i+1, sum + ary[i])
end
puts sum
end
add(a, 0, 0)
Thing is, I don't need it to spit out the results of adding all the sums. I need the min, max, median, and average of the sums.
How do I modify this code in order to get them? I'm a total beginner at Ruby. I've been using this code, and then transferring the results to Excel to get the values I want. But it feels like my methods could be more efficient.
Thank you for your help.
EDIT: Expected results - Currently the code spits this out on my screen:
25
12
18
5
21
8
14
1
24
11
17
4
20
7
13
0
I want it to spit out the min, average, median, and max instead:
0
12.5
12.5
25
a = [1, 4, 7, 13]
def all_sums(array)
combination_lengths = (0..array.length)
all_combinations = combination_lengths.flat_map do |c|
array.combination(c).to_a
end
all_combinations.map(&:sum)
end
def print_min_max_avg_med(array)
puts array.min
puts array.max
puts array.sum.to_f / array.length
sorted_arr = array.sort
puts sorted_arr[(array.length - 1) / 2] + sorted_arr[array.length / 2] / 2.0
end
print_min_max_avg_med(all_sums(a))
Ok, instead of outputting the values we can store them in an arrary and use that array for the values you need.
(edited after chewing out by Stefan Pochmann)
a = [1, 4, 7, 13]
def add(ary, idx, sum, results = nil)
unless results
results = []
first_run = true
end
(idx...ary.length).each do |i|
add(ary, i+1, sum + ary[i], results)
end
results << sum
if first_run
puts results.min
puts results.inject(&:+).to_f / results.size
puts (results.sort[((results.size - 1) / 2)] + results.sort[(results.size / 2)]) / 2.0
puts results.max
end
end
add(a, 0, 0)
Alright, after seeing the examples from Pochmann and Bronca, I put this together after googling for a better way to get the median.
a = [1, 4, 7, 13]
def all_sums(array)
combination_lengths = (0..array.length)
all_combinations = combination_lengths.flat_map do |c|
array.combination(c).to_a
end
all_combinations.map(&:sum)
end
def median(array)
sorted = array.sort
len = sorted.length
(sorted[(len - 1) / 2] + sorted[len / 2]) / 2.0
end
def print_min_max_avg_med(array)
puts array.min
puts array.empty? ? 0 : array.sum.to_f / array.length
puts median(array)
puts array.max
end
print_min_max_avg_med(all_sums(a))
I've run a few tests, and it seems to work for both odd and even arrays. Hope this is useful to the future somebody else stuck in my present position.
Thank you everyone who helped.
Min and Max
The min and max are easy.
def min_and_max_of_sums a
return [nil, nil] if a.empty?
negs, nonnegs = a.partition { |n| n < 0 }
[negs.any? ? negs.sum : nonnegs.min, nonnegs.any? ? nonnegs.sum : negs.max]
end
min_and_max_of_sums [1, 4, -5, 7, -8, 13]
#=> [-13, 25]
min_and_max_of_sums [1, 2, 3]
#=> [1, 6]
min_and_max_of_sums [-1, -2, -3]
#=> [-6, -1]
min_and_max_of_sums []
#=> [nil, nil]
Mean
Now consider the calculation of the mean.
If n is the size of the array a, there are 2n combinations of elements of a that contain between 0 and n elements.1 Moreover, there is a 1-1 mapping between each of those combinations and an n-vector of zeros and ones, where the ith element of the n-vector equals 1 if and only if the element ai is included in the combination. Note that there are 2n such n-vectors, one-half containing a 1 in the ith position. This means that one-half of the combinations contain the element ai. As i is arbitrary, it follows that each element of a appears in one-half of the combinations.
The mean of the sums of all elements of all combinations equals T/2n, where T is the sum of the sums of the elements of each combination. Each element ai appears in 2n/2 combinations, so its contribution to T equals (in Ruby terms)
a[i] * 2**(n)/2
As this hold for every element of a, the mean equals
a.sum * (2**(n)/2)/2**(n)
=> a.sum/2
Here's an example. For the array
a = [1, 4, 8]
the mean of the sums would be
a.sum/2
#=> 13/2 => 6.5
If we were to calculate the mean by its definition we would perform the following calculation (and of course get the same return value).
(0 + (1) + (4) + (8) + (1+4) + (1+8) + (4+8) + (1=4+8))/2**3
#=> (4*1 + 4*4 + 4*8)/8
#=> (1 + 4 + 8)/2
#=> 6.5
I will leave the calculating of the median to others.
1 Search for "Sums of the binomial coefficients" here.
def incrementer(num)
num.map.with_index(1) do |row, index|
if row > 9
row.to_s.reverse.chop.to_i
else
index + row
end
end
end
Hi,
I have a method which adds the number to the according index it is positioned in. However I have two rules:
The index starts at (1)
If the number is a multiple, remove the first integer off the end of it. 12 would become 2 for example.
The problem is I am unsure how to include an 'if' statement inside a block and I believe I am doing it wrong. I know there is a much better way to write this statement but I am finding it hard.
Example:
incrementer([4,6,9,1,3]) => [5,8,2,5,8]
- 9 + 3 (position of 9 in array) = 12
- Only its last digit 2 should be returned
Fixing your code
Use map followed by with_index(1), the argument offsetting the initial index by +1.
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
if row > 9
row.digits.first
else
row
end
end
end
incrementer [3, 2, 4, 10] #=> [4, 4, 7, 4]
incrementer [4, 6, 9, 1, 3] #=> [5, 8, 2, 5, 8]
Negative numbers
Numbers can be negative and for which we can use abs.digits but it may be better to use Integer#remainder.
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
case row
when -9..9
row
else
row.remainder 10
end
end
end
incrementer [3, 2, 4, 10]
#=> [4, 4, 7, 4]
incrementer [4, 6, 9, 1, 3]
#=> [5, 8, 2, 5,8]
incrementer [3, 2, 4,-17]
#=> [4, 4, 7, -3]
incrementer [4, 6, -22, 1, 3]
#=> [5, 8, -9, 5, 8]
Why not use % instead of Integer#remainder?
a % b returns a modulo b which is not the same thing as the remainder. This has consequences for negative numbers:
-12 % 10 #=> 8 not the last digit
So we need to use Integer#remainder, so:
-12.remainder 10 #=> -2
Which is what we need, i.e. the last digit (parity included). It can be noted in other languages such as JavaScript, a % b returns the remainder.
As noted by #moveson the above code can be refactored to:
num.map.with_index(1) { |row, index| (index + row).remainder 10 }
The #digits method requires Rails or ActiveSupport (or Ruby 2.4+), and it's not necessary. Here is a pure Ruby solution that works with any Ruby version:
def incrementer(array)
array.map.with_index(1) { |integer, index| (integer + index) % 10 }
end
This code above says: For each element of the array, add its index (starting from 1), divide the sum by 10, and return the remainder.
The % (modulo) operator divides the number before it by the number after it and returns the remainder. For example, 22 % 7 returns 1. It's an extremely useful tool and can often help avoid the use of conditionals entirely, such as in your example. Using %, you can get the last digit of a number n (whether or not n is greater than 9) by simply taking n % 10.
Now you can do:
>> incrementer([3,2,4,10])
=> [4, 4, 7, 4]
>> incrementer([4,6,9,1,3])
=> [5, 8, 2, 5, 8]
You've got two separate problems. The first problem is your use of each_with_index. Ruby functions return the value of the last expression they execute, so if you look at your function:
def incrementer(num)
num.each_with_index do |row, index|
# do some stuff
end
end
It essentially calls each_with_index and returns the value. The issue here is that each_with_index iterates over an array and then returns the original array. What you want to do is change each item in the array and return a new array with the new values. For this, you can use map:
def incrementer(num)
num.map.with_index(1) do |row, index|
# do some stuff
end
end
In this case, you can even conveniently pass in the parameter 1 to tell the iterator to start at index 1.
Now the second problem is that your if-else-statement either iterates a number or wraps it around. But what you actually want to do is iterate a number and wrap it around if it's bigger than 9. You can do that like so:
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
if row > 9
row.to_s.reverse.chop.to_i
else
row
end
end
end
What we're doing here is iterating the number first, saving it into row and then checking to see if it's over 9. If it is, we return the last digit; otherwise, we just return the value row.
This code will do what you want, but let's make one more change for the sake of clarity. This code:
row.to_s.reverse.chop.to_i
Is a little bit confusing. Ruby 2.4 has a convenient method for getting at the digits of an integer:
row.digits.first
This is easy to read, but it's a bit slow since it turns an integer into an array of integers. A better way is to use modulus % to get the remainder of your number, divided by 10. This is fast and easy to read for most programmers, since it's a common trick.
row % 10
Putting it all together, you get:
def incrementer(num)
num.map.with_index(1) do |row, index|
row = index + row
if row > 9
row % 10
else
row
end
end
end
Special thanks to (#sagarpandya84) and (#moveson) for allowing me to build on their answers.
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
def peel array
output = []
while ! array.empty? do
output << array.shift
mutate! array
end
output.flatten
end
I have not included the mutate! method, because I am only interested in removing the output variable. The mutate! call is important because we cannot iterate over the array using each because array is changing.
EDIT: I am getting an array as output, which is what I want. The method works correctly, but I think there is a way to collect the array.shift values without using a temp variable.
EDIT #2: OK, here is the mutate! method and test case:
def mutate! array
array.reverse!
end
a = (1..5).to_a
peel( a ).should == [ 1, 5, 2, 4, 3 ]
It doesn't matter if peel modifies array. I guess it should be called peel!. Yes, mutate! must be called after each element is removed.
All this reversing makes me dizzy.
def peel(array)
indices = array.size.times.map do |i|
i = -i if i.odd?
i = i/2
end
array.values_at(*indices) # indices will be [0, -1, 1, -2, 2] in the example
end
a = (1..5).to_a
p peel(a) #=>[1, 5, 2, 4, 3]
Another approach:
def peel(array)
mid = array.size/2
array[0..mid]
.zip(array[mid..-1].reverse)
.flatten(1)
.take(array.size)
end
Usage:
peel [1,2,3,4,5,6]
#=> [1, 6, 2, 5, 3, 4]
peel [1,2,3,4,5]
#=> [1, 5, 2, 4, 3]
Here's a way using parallel assignment:
def peel array
n = array.size
n.times {|i| (n-2-2*i).times {|j| array[n-1-j], array[n-2-j] = array[n-2-j], array[n-1-j]}}
array
end
peel [1,2,3,4,5] # => [1,5,2,4,3]
peel [1,2,3,4,5,6] # => [1,6,2,5,3,4]
What I'm doing here is a series of pairwise exchanges. By way of example, for [1,2,3,4,5,6], the first 6-2=4 steps (6 being the size of the array) alter the array as follows:
[1,2,3,4,6,5]
[1,2,3,6,4,5]
[1,2,6,3,4,5]
[1,6,2,3,4,5]
The 1, 6 and the 2 are in now the right positions. We repeat these steps, but this time only 6-4=2 times, to move the 5 and 3 into the correct positions:
[1,6,2,3,5,4]
[1,6,2,5,3,4]
The 4 is pushed to the end, it's correct position, so we are finished.
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!