Find the difference between all integers in an array - arrays

I have an array of integers in Ruby. I want to find the differences between each number and every other number.
I can do it with one of the integers and find the difference between it and all the other numbers but I can't work out how to iterate twice per se.
Here is what I have:
def stock_picker(ary)
ary.map {|a| ary[0] - a }
end
stock_picker [1, 2, 3, 4, 5]
#=> [0, -1, -2, -3, -4]
When I run the above for ary[1] instead of ary[0] I get:
[1, 0, -1, -2, -3]
For ary[2] it's:
[2, 1, 0, -1, -2]
and so on. But How can I generate the differences for all numbers in ary regardless of its size?
The expected result is:
[0, -1, -2, -3, -4, 1, 0, -1, -2, -3, 2, 1, 0, -1, -2, 3, 2, 1, 0, -1, 4, 3, 2, 1, 0]

More compact version:
arr.product(arr).map { |a,b| a - b }

It appears you want the following.
def doit(arr)
arr.flat_map { |n| arr.map { |m| n-m } }
end
doit [1, 2, 3, 4, 5]
#=> [0, -1, -2, -3, -4, 1, 0, -1, -2, -3, 2, 1,
# 0, -1, -2, 3, 2, 1, 0, -1, 4, 3, 2, 1, 0]
See Enumerable#flat_map.

Related

Finding the index of the minimum value in a list of arrays

Let's say I have a list with multiple arrays:
L = [
array([-10, -8, -3, 2, 1]),
array([-9, -4, -1, 3, 5]),
array([-11, -5, -4, 0, 10])
]
How can I find the index of the lowest value the most efficiently?
For my example, the minimum value is -11 and the index is (2, 0), so the output should be (0, 2).
What about the following?
import numpy as np
L = [
np.array([-10, -8, -3, 2, 1]),
np.array([-9, -4, -1, 3, 5]),
np.array([-11, -5, -4, 0, 10])
]
L_2d = np.array(L)
min_index = np.unravel_index(L_2d.argmin(), L_2d.shape)
(min_index[0], min_index[1])

How to optimise code that parses a 2-d array in Ruby

Note: This question poses a problem that I have already solved, however I feel my solution is very rudimentary and that other people, like myself, would benefit from a discussion with input from more experienced developers. Different approaches to solving the problem, as well as more sophisticated methods and algorithms would be really appreciated. I feel this is a good place to learn how Ruby can tackle what I consider to be a fairly difficult problem for a beginner.
Given a 6x6 2D Array arr:
1 1 1 0 0 0
0 1 0 0 0 0
1 1 1 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
We define an hourglass in arr to be a subset of values with indices falling in this pattern in arr's graphical representation:
a b c
d
e f g
There are 16 hourglasses in arr and an hourglass sum is the sum of an hourglass' values. Calculate the hourglass sum for every hourglass in arr, then print the maximum hourglass sum.
For example, given the 2D array:
arr = [
[-9, -9, -9, 1, 1, 1],
[ 0, -9, 0, 4, 3, 2],
[-9, -9, -9, 1, 2, 3],
[ 0, 0, 8, 6, 6, 0],
[ 0, 0, 0, -2, 0, 0],
[ 0, 0, 1, 2, 4, 0]
]
We calculate the following hourglass values:
-63, -34, -9, 12,
-10, 0, 28, 23,
-27, -11, -2, 10,
9, 17, 25, 18
Our highest hourglass value is from the hourglass:
0 4 3
1
8 6 6
My solution is:
def hourglass_sum(arr)
hourglasses = []
arr.each_with_index do |row, i|
# rescue clause to prevent iterating outside the array
unless arr[i].nil?
arr[i].length.times do |iteration|
# generate n 3x3 arrays
r1 = arr[i][iteration...iteration+3]
r2 = arr[i+1][iteration...iteration+3] if arr[i+1] != nil
r3 = arr[i+2][iteration...iteration+3] if arr[i+2] != nil
# rescue clause to stop creating 3x3 arrays that fall outside given input array
if arr[i+1] != nil && arr[i+2] != nil
# take all values except indices 0 and 5 from the 9 element array
result = r1 + [r2[1]] + r3
hourglasses << result.sum unless result.include? nil
end
end
end
end
p hourglasses.max
end
arr = [[-9, -9, -9, 1, 1, 1], [0, -9, 0, 4, 3, 2], [-9, -9, -9, 1, 2, 3], [0, 0, 8, 6, 6, 0], [0, 0 ,0, -2, 0, 0], [0, 0, 1, 2, 4, 0]]
hourglass_sum(arr)
# => 28
One option is to use Matrix methods.
require 'matrix'
ma = Matrix[*arr]
#=> Matrix[[-9, -9, -9, 1, 1, 1],
# [ 0, -9, 0, 4, 3, 2],
# [-9, -9, -9, 1, 2, 3],
# [ 0, 0, 8, 6, 6, 0],
# [ 0, 0, 0, -2, 0, 0],
# [ 0, 0, 1, 2, 4, 0]]
mi = Matrix.build(6-3+1) { |i,j| [i,j] }
#=> Matrix[[[0, 0], [0, 1], [0, 2], [0, 3]],
# [[1, 0], [1, 1], [1, 2], [1, 3]],
# [[2, 0], [2, 1], [2, 2], [2, 3]],
# [[3, 0], [3, 1], [3, 2], [3, 3]]]
def hourglass_val(r,c,ma)
mm = ma.minor(r,3,c,3)
mm.sum - mm[1,0] - mm[1,2]
end
max_hg = mi.max_by { |r,c| hourglass_val(r,c,ma) }
#=> [1,2]
hourglass_val(*max_hg,ma)
#=> 28
[1,2] are the row and column indices of the top-left corner of an optimal hourglass in arr.
Here is an option I came up with.
def width_height(matrix)
[matrix.map(&:size).max || 0, matrix.size]
end
def sum_with_weight_matrix(number_matrix, weight_matrix)
number_width, number_height = width_height(number_matrix)
weight_width, weight_height = width_height(weight_matrix)
width_diff = number_width - weight_width
height_diff = number_height - weight_height
0.upto(height_diff).map do |y|
0.upto(width_diff).map do |x|
weight_height.times.sum do |ry|
weight_width.times.sum do |rx|
weight = weight_matrix.dig(ry, rx) || 0
number = number_matrix.dig(y + ry, x + rx) || 0
number * weight
end
end
end
end
end
arr = [
[-9, -9, -9, 1, 1, 1],
[ 0, -9, 0, 4, 3, 2],
[-9, -9, -9, 1, 2, 3],
[ 0, 0, 8, 6, 6, 0],
[ 0, 0, 0, -2, 0, 0],
[ 0, 0, 1, 2, 4, 0],
]
weights = [
[1, 1, 1],
[0, 1, 0],
[1, 1, 1],
]
sum_matrix = sum_with_weight_matrix(arr, weights)
#=> [
# [-63, -34, -9, 12],
# [-10, 0, 28, 23],
# [-27, -11, -2, 10],
# [ 9, 17, 25, 18]
# ]
max_sum = sum_matrix.flatten.max
#=> 28
This solution uses the width_diff and height_diff to create an output matrix (4x4 for the sample data 0.upto(6 - 3).to_a #=> [0, 1, 2, 3]). The indexes of the weight_matrix (rxand ry) will be used as relative index compared to the larger number_matrix.
If your 2d array always has the same number of elements for each sub-array you can replace matrix.map(&:size).max with matrix[0]&.size || 0 to speed up determining the matrix width. The current solution uses the maximum size of the sub-arrays. Sub-arrays having a smaller size will use 0 for the missing elements thus not effecting the sum.
My solution might be a bit variable heavy. I've done this to have descriptive variable names, that hopefully tell you most you need to know about the solution. You can shorten variable names, or remove them completely when you feel like you don't need them.
If something isn't clear just ask away in the comments.
Without using the Matrix class, here's how I've done it for any arbitrary rectangular array:
offsets = [[-1, -1], [-1, 0], [-1, 1], [0, 0], [1, -1], [1, 0], [1, 1]]
sums = 1.upto(arr.length - 2).flat_map do |i|
1.upto(arr[0].length - 2).map do |j|
offsets.map {|(x, y)| arr[i+x][j+y] }.sum
end
end
puts sums.max
The values we're interested in are just offsets from a current position. We can map out the values in the array relative to the current position by some row and column offset, sum them, then select the max of the sums.

Create an array containing values oscillating between two boundaries

I am wondering if there is a way to generate an array which, for example, would start from 0, increase by 1 until it reaches 3, and then decreases by 1 until it reaches 0 again, eg
[0,1,2,3,2,1,0]
and if I could specify the number of values in the array ahead of time, that would be great. For example, if I could set the lower bound(0), upper bound (3), increment(1), and length of array (9):
[].oscillate(0,3,1,9) would give me this:
[0,1,2,3,2,1,0,1,2]
As of now, the best thing I can come up with is this:
values = []
until values.count >= 9
values.pop
x=0
values << x && x+=1 while x < 3
values << x && x-=1 while x >= 0
end
Fun exercise!
You're looking for a triangle wave.
The formulas on Wikipedia are for the standard shape (between -1 and 1), but here's an adapted version for any wave position, period and amplitude :
def triangle_wave(min, max, increment, length, offset = 0)
amplitude = max - min
period = 2 * amplitude
Array.new(length) do |i|
min + ((increment * (i + offset) - amplitude) % period - amplitude).abs
end
end
puts triangle_wave(0, 3, 1, 9) == [0, 1, 2, 3, 2, 1, 0, 1, 2]
# true
p triangle_wave(-3, 3, 1, 20)
# => [-3, -2, -1, 0, 1, 2, 3, 2, 1, 0, -1, -2, -3, -2, -1, 0, 1, 2, 3, 2]
p triangle_wave(5, 9, 2, 9)
# => [5, 7, 9, 7, 5, 7, 9, 7, 5]
p triangle_wave(0, 1, 0.25, 9)
# => [0.0, 0.25, 0.5, 0.75, 1.0, 0.75, 0.5, 0.25, 0.0]
p triangle_wave(-3, 0, 1, 9, 3)
# => [0, -1, -2, -3, -2, -1, 0, -1, -2]
p triangle_wave(0, 1, 1, 9)
# => [0, 1, 0, 1, 0, 1, 0, 1, 0]
min should be lower than max, increment should be positive and max-min should be divisible by increment. Those are restrictions on the input but not on the output : any wave can be generated.
This problem could be a textbook example of the use of Ruby's flip-flop operator.
As the question only makes sense when there is a non-negative integer steps such that high = low + steps * increment, I've replaced the method's argument high with steps.
def oscillate(low, steps, increment, length)
high = low + steps * increment
n = low
length.times.each_with_object([]) do |_,a|
a << n
n += (n==low)..(n==high-increment) ? increment : -increment
end
end
oscillate(0,3,1,9)
#=> [0, 1, 2, 3, 2, 1, 0, 1, 2]
oscillate(-1, 4, 2, 16)
#=> [-1, 1, 3, 5, 7, 5, 3, 1, -1, 1, 3, 5, 7, 5, 3, 1]
To show what's happening here I will modify the code a little and add some puts statements, then run it with the first example.
def oscillate(low, steps, increment, length)
high = low + steps * increment
puts "high = #{high}"
n = low
length.times.each_with_object([]) do |_,a|
a << n
diff = (n==low)..(n==high-increment) ? increment : -increment
print "n=#{n}, a<<n=#{a}, diff=#{diff}, "
n += diff
puts "n+=diff=#{n}"
end
end
oscillate(0,3,1,9)
high = 3
n=0, a<<n=[0], diff= 1, n+=diff=1
n=1, a<<n=[0, 1], diff= 1, n+=diff=2
n=2, a<<n=[0, 1, 2], diff= 1, n+=diff=3
n=3, a<<n=[0, 1, 2, 3], diff=-1, n+=diff=2
n=2, a<<n=[0, 1, 2, 3, 2], diff=-1, n+=diff=1
n=1, a<<n=[0, 1, 2, 3, 2, 1], diff=-1, n+=diff=0
n=0, a<<n=[0, 1, 2, 3, 2, 1, 0], diff= 1, n+=diff=1
n=1, a<<n=[0, 1, 2, 3, 2, 1, 0, 1], diff= 1, n+=diff=2
n=2, a<<n=[0, 1, 2, 3, 2, 1, 0, 1, 2], diff= 1, n+=diff=3
#=> [0, 1, 2, 3, 2, 1, 0, 1, 2]
Try this
def oscillate(a, b, step, num)
ramp_up = a.step(b, step).entries
ramp_down = ramp_up.drop(1).reverse.drop(1)
ramp_up.concat(ramp_down).cycle.take(num)
end
How does this work?
creates the ramp_up and ramp_down arrays
concatenates the two arrays
cycle returns an ever-repeating enumerator
take materializes num elements from that enumerator. Other than suggested in a comment, this does not recalculate anything. It just materializes entries from the enumerator.

Efficient way of doing numerical integration on a 2d array with variable integration length

I asked a question here about numerically integrating on a 2d array with fixed length. Now what if the integration length is not fixed? For each cell as the starting point, I want to keep integrating until it encounters a cell with value of the opposite sign. So suppose in a column from bottom to top it is [1,2,5,4,-2,-3,2], if I do the integral for the first element, it will integrate the first four elements (they are all positive). If I start from the fifth element, it will just integrate -2 and -3. Are there any ways to vectorize it or speed it up instead of using a double for loop to first find the integration length for each cell and then do the integral?
Or a simplified problem is just to integrate the positive elements:
example:
data = [
-2, -1, 4, -2,-1;
1, 2, 3, 4, 5;
5, -4, -3, 2, 5;
3, -3, -9, 5, 7;
2, -2, 7, -5, 1;
2, 3, 1, -3, -3]
integrated_data = [
0, 0, 7, 0, 0;
13, 2, 3, 11, 18;
12, 0, 0 7, 13;
7, 0, 0, 5, 8;
4, 0, 8, 0, 1;
2, 3, 1, 0, 0]
A vectorization solution in MATLAB
data = [
-2, -1, 4, -2,-1;
1, 2, 3, 4, 5;
5, -4, -3, 2, 5;
3, -3, -9, 5, 7;
2, -2, 7, -5, 1;
2, 3, 1, -3, -3];
data1 = [-ones(1,size(data,2)) ;flipud(data)]
df = find([-1 ;diff((data1(:))>=0)] == 1)-1;
data1(data1<0) =0;
c1 = cumsum(data1(:));
data1(df) = data1(df) - [0 ;diff(c1(df))];
c2 = cumsum(data1(:));
c2(data1==0)=0;
c2=reshape(c2,size(data1));
result = flipud(c2(2:end,:))

Python Array append vectors and then sum the elements of the array positionwise (not elementwise)

first of all I explain what I would like to do. I have a function which gives me some lists. These lists have the same number of elements and they contain numbers, which represents positions on the x-axis. For example one of them is [-11, -6, -5, -4, -1, 1, 3, 4, 6, 7], another one is [-11, -6, -5, -3, -1, 1, 2, 4, 5, 7]. The entries will always be integers and in ascending order.
I want to run this function many times and at the end "sum-up" all these vectors in a particular way. Imagine that each vector shows the position of a person in the x-axis. I want to know, at the end of say q experiments, how many people there are in each position. However, they do not all start from -11 or end at 7.
For example [-13, -8, -3, -1, 0, 1, 2, 4, 5, 7] or [-12, -7, -2, -1, 0, 1, 3, 4, 5, 6] are other two valid output from the function.
How can I do that?
My idea was to create a loop, compute the function, and store these lists into an array and then use some weird matrix operation. However I am absolutely stuck, this is my attempt, where rep_assign_time2(n,p,m) is the function that gives me the lists:
def many_experiments(n,p,m,q):
jj = 0
vector_min = []
vector_max = []
a = np.array([])
while jj < q:
s = rep_assign_time2(n,p,m)
a = np.concatenate((a,s), axis = 0) # I add s as an element of a
for k in range(a.shape):
ma = max(a[k])
mi = min(a[k])
vector_min.append(mi)
vector_max.append(ma)
minimum = min(vector_min)
maximum = max(vector_max)
And then I have NO IDEA on how to create an operation that does what I want. I've been thinking for an hour and still no clue. Do you have any idea?
You are in luck with NumPy, as there's a built-in for it as np.unique. It gives us both such unique labels (axis positions in this case) and their counts at each such label. So, let's say you have the lists stored as a list, thus a list of lists as A, you could simply do -
unq,counts = np.unique(A,return_counts=True)
Sample run -
In [33]: A = [[-11, -6, -5, -4, -1, 1, 3, 4, 6, 7], \
...: [-11, -6, -5, -3, -1, 1, 2, 4, 5, 7],\
...: [-13, -8, -3, -1, 0, 1, 2, 4, 5, 7],\
...: [-12, -7, -2, -1, 0, 1, 3, 4, 5, 6]]
In [34]: unq,counts = np.unique(A,return_counts=True)
In [35]: unq
Out[35]:
array([-13, -12, -11, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1,
2, 3, 4, 5, 6, 7])
In [36]: counts
Out[36]: array([1, 1, 2, 1, 1, 2, 2, 1, 2, 1, 4, 2, 4, 2, 2, 4, 3, 2, 3])
In [40]: import matplotlib.pyplot as plt
In [41]: # Plot the results
...: plt.bar(unq, counts, align='center')
...: plt.grid()
...: plt.show()
...:

Resources