Ruby Increment array from certain starting point - arrays

I feel this is a super simple query but I'm having a real tough time with immutable nums in my arrays.
I'd like to have a super simple method, which increments numbers in an array by distributing them from the max value.
eg [1,3,5,1] becomes [1,3,0,1] and then iterates upwards and back through to create [2,4,1,3]
what I currently have is the following
arr = [1,3,5,1]
with a method of
def increment_from_max_value(arr)
max = arr.max
max_index = arr.index(max)
arr[max_index] = 0
while max >= 0
arr[max_index..-1].each do |element|
element = element += 1
max = max -= 1
end
end
end
Currently the array isn't even updating and just returns the original values. Which I believe is due to the immutability of FixNums in Ruby, but with a method like map!, which is able to modify those values, I can't get it to loop back through from a certain starting point like each will.
Many thanks in advance

I'd use divmod to calculate the increase for each element and the leftover.
For a max value of 5 and array size of 4 you'd get:
5.divmod(4) #=> [1, 1]
i.e. each element has to incremented by 1 (first value) and 1 element (second value) has to be incremented by another 1.
Another example for a max value of 23 and 4 elements:
[1, 3, 23, 1]
23.divmod(4) #=> [5, 3]
each element has to be incremented by 5 and 3 elements have to be incremented by another 1:
[ 1, 3, 23, 1]
# +5 +5 +5 +5
# +1 +1 +1
# = [ 7, 9, 5, 7]
Applied to your method:
def increment_from_max_value(arr)
max = arr.max
max_index = arr.index(max)
arr[max_index] = 0
q, r = max.divmod(arr.size)
arr.each_index { |j| arr[j] += q }
r.times { |j| arr[(max_index + j + 1) % arr.size] += 1 }
end
arr.each_index { |j| arr[j] += q } simply adds q to each element.
r.times { |j| arr[(max_index + j + 1) % arr.size] += 1 } is a little more complicated. It distributes the remainder, starting from 1 after max_index. The modulo operation ensures that the index will wrap around:
0 % 4 #=> 0
1 % 4 #=> 1
2 % 4 #=> 2
3 % 4 #=> 3
4 % 4 #=> 0
5 % 4 #=> 1
6 % 4 #=> 2
# ...

I think there is something wrong with the while loop. I did not investigate but you can see that this line arr[max_index] = 0 mutates the array.
I don't know if I've understood the logic, but this should return the desired output:
def increment_from_max_value(arr)
max = arr.max
arr[arr.index(max)] = 0
indexes = (0...arr.size).to_a.reverse
max.times do
arr[indexes.first] += 1
indexes.rotate!
end
end

The value of the block variable element in arr[max_index..-1].each do |element| is changed by element = element += 1 but that has no effect on arr.
You could achieve your objective as follows.
def increment_from_max_value(arr)
mx, mx_idx = arr.each_with_index.max_by(&:first)
sz = arr.size
arr[mx_idx] = 0
arr.rotate!(mx_idx + 1).map!.with_index do |e,i|
begin_nbr = mx - i
e += (begin_nbr <= 0 ? 0 : 1 + ((begin_nbr - 1)/sz))
end.rotate!(-mx_idx - 1)
end
arr = [1,3,5,1]
increment_from_max_value(arr)
arr
#=> [2, 4, 1, 3]
arr = [1,2,3,2,1]
increment_from_max_value(arr)
arr
#=> [2, 2, 0, 3, 2]
After computing the maximum value of arr, mx, and its index, mx_idx, and setting arr[mx_idx] to zero, I rotate the array (counter-clockwise) by mx_idx + 1, making the position of mx last. That way the "allocations" of mx begin with the first element of the rotated array. After performing the allocations I then rotate the array clockwise by the same mx_idx + 1.
begin_nbr equals mx minus the number of indices that precede i; in effect, the portion of mx that remains "unallocated" at index i in the first round of allocations.
I can best explain how this works by salting the method with puts statements.
def increment_from_max_value(arr)
mx, mx_idx = arr.each_with_index.max_by(&:first)
sz = arr.size
puts "mx = #{mx}, mx_idx = #{mx_idx}, sz = #{sz}"
arr[mx_idx] = 0
arr.rotate!(mx_idx + 1).
tap { |a| puts "arr rotated to make mx position last = #{a}" }.
map!.with_index do |e,i|
begin_nbr = mx - i
puts "e before = #{e}, i = #{i}, begin_nbr = #{begin_nbr}"
e += (begin_nbr <= 0 ? 0 : 1 + ((begin_nbr - 1)/sz))
e.tap { |f| puts "e after change = #{f}" }
end.
tap { |a| puts "arr after changes = #{a}" }.
rotate!(-mx_idx - 1).
tap { |a| puts "arr after rotating back = #{a}" }
end
arr = [1,3,5,1]
increment_from_max_value(arr)
mx = 5, mx_idx = 2, sz = 4
arr rotated to make mx position last = [1, 1, 3, 0]
e before = 1, i = 0, begin_nbr = 5
e after change = 3
e before = 1, i = 1, begin_nbr = 4
e after change = 2
e before = 3, i = 2, begin_nbr = 3
e after change = 4
e before = 0, i = 3, begin_nbr = 2
e after change = 1
arr after changes = [3, 2, 4, 1]
arr after rotating back = [2, 4, 1, 3]
#=> [2, 4, 1, 3]

Related

Minimum number of operations to make two arrays equal

Given 2 arrays of integers, A and B, an operation on array B is defined as follows:
B[i] = B[i]+2 and B[j] = B[j]-2, where i != j
i and j can be any indices and the above operation can be performed
any number of times such that i and j are not equal
a valid operation consists of both the addition and subtraction steps, both parts are mandatory
The array is considered equal if the frequency of all the elements is same, the array need not be ordered, find the minimum operations required
Input:
A = [ 2, 10, 14 ]
B = [ 6, 2, 18 ]
Output: 2
Explanation :
1st operation: select i=0; j=2;
B[i] += 2 i.e B[0]=8;
B[j] -= 2 i.e B[2] = 16;
B after 1st operation [8,2,16]
2nd operation: select i=0; j=2;
B[i] += 2 i.e B[0]=10;
B[j] -= 2 i.e B[2] = 14;
B after 2nd operation [10,2,14]
Order doesnt matter, so we have made the arrays equal return 2;
I am unable get an approach to solve this and couldn't find any similar questions, so posting this here, thanks in advance.
Assuming the arrays are solvable, then sort the arrays (by parity, and then by value), add up the absolute value of the deltas and divide by 4.
E.g.
A = [ 2, 10, 14 ], already sorted
B = [ 6, 2, 18 ], sorted becomes [2, 6, 18]
Sum of absolute value of deltas = 0 + 4 + 4 = 8. 8/4 = 2 so 2 moves.
A = [2, 10, 14]( % 2 == 0)
B = [2, 6, 18]( % 2 == 0)
another example
A = [1, 2, 5] -> [1, 5]( % 2 == 1) & [2]( % 2 == 0)
B = [1, 3, 4] -> [1, 3]( % 2 == 1) & [4]( % 2 == 0)
Notice that (a + k) mod k == a.
Assuming we already have a sorted array.
We divide the array into k parts, according to the mod k value of the element, then we sum all absolute differences, it's four times the answer.
k = 2
A.sort(key=lambda x: x % k)
B.sort(key=lambda x: x % k)
result = 0
n = len(A)
for i in range(n):
result += abs(A[i] - B[i])
print(result >> 2)
# A = [1, 2, 5]
# B = [1, 3, 4]
# result = 1
# A = [2, 10, 14]
# B = [6, 2, 18]
# result = 2
O(N log N) because of sorting.

circularArrayRotation algorithm ruby

I am using hacker rank and I do not understand why my ruby code only works for one test case out of like 20. Here is the question:
John Watson knows of an operation called a right circular rotation on
an array of integers. One rotation operation moves the last array
element to the first position and shifts all remaining elements right
one. To test Sherlock's abilities, Watson provides Sherlock with an
array of integers. Sherlock is to perform the rotation operation a
number of times then determine the value of the element at a given
position.
For each array, perform a number of right circular rotations and
return the values of the elements at the given indices.
Function Description
Complete the circularArrayRotation function in the editor below.
circularArrayRotation has the following parameter(s):
int a[n]: the array to rotate
int k: the rotation count
int queries[1]: the indices to report
Returns
int[q]: the values in the rotated a as requested in m
Input Format
The first line contains 3 space-separated integers, n, k, and q, the number of elements in the integer array, the rotation count and the number of queries. The second line contains n space-separated integers,
where each integer i describes array element a[i] (where 0 <= i < n). Each of the q subsequent lines contains a single integer, queries[i], an index of an element
in a to return.
Constraints
Sample Input 0
3 2 3
1 2 3
0
1
2
Sample Output 0
2
3
1
Here is my code :
def circularArrayRotation(a, k, queries)
q = []
while k >= 1
m = a.pop()
a.unshift m
k = k - 1
end
for i in queries do
v = a[queries[i]]
q.push v
end
return q
end
It only works for the sample text case but I can't figure out why. Thanks for any help you can provide.
Haven't ran any benchmarks, but this seems like a job for the aptly named Array.rotate() method:
def index_at_rotation (array, num_rotations, queries)
array = array.rotate(-num_rotations)
queries.map {|q| array[q]}
end
a = [1, 2, 3]
k = 2
q = [0,1, 2]
index_at_rotation(a, k, q)
#=> [2, 3, 1]
Handles negative rotation values and nil results as well:
a = [1, 6, 9, 11]
k = -1
q = (1..4).to_a
index_at_rotation(a, k, q)
#=> [9, 11, 1, nil]
I don't see any errors in your code, but I would like to suggest a more efficient way of making the calculation.
First observe that after q rotations the element at index i will at index (i+q) % n.
For example, suppose
n = 3
a = [1,2,3]
q = 5
Then after q rotations the array will be as follows.
arr = Array.new(3)
arr[(0+5) % 3] = a[0] #=> arr[2] = 1
arr[(1+5) % 3] = a[1] #=> arr[0] = 2
arr[(2+5) % 3] = a[2] #=> arr[1] = 3
arr #=> [2,3,1]
We therefore can write
def doit(n,a,q,queries)
n.times.with_object(Array.new(n)) do |i,arr|
arr[(i+q) % n] = a[i]
end.values_at(*queries)
end
doit(3,[1,2,3],5,[0,1,2])
#=> [2,3,1]
doit(3,[1,2,3],5,[2,1])
#=> [1, 3]
doit(3,[1,2,3],2,[0,1,2])
#=> [2, 3, 1]
p doit(3,[1,2,3],0,[0,1,2])
#=> [1,2,3]
doit(20,(0..19).to_a,25,(0..19).to_a.reverse)
#=> [14, 13, 12, 11, 10, 9, 8, 7, 6, 5,
# 4, 3, 2, 1, 0, 19, 18, 17, 16, 15]
Alternatively, we may observe that after q rotations the element at index j was initially at index (j-q) % n.
For the earlier example, after q rotations the array will be
[a[(0-5) % 3], a[(1-5) % 3], a[(2-5) % 3]]
#=> [a[1], a[2], a[0]]
#=> [2,3,1]
We therefore could instead write
def doit(n,a,q,queries)
n.times.map { |j| a[(j-q) % n] }.values_at(*queries)
end

how to shrink an array if two consecutive numbers in an array are equal then remove one and increment other

How to shrink an array if two consecutive numbers in an array are equal then remove one and increment other
Example 1:
int a[6]={2,2,3,4,4,4};
// Output: 6
Example 2:
int b[7]={1,2,2,2,4,2,4};
// Output: {1,3,2,4,2,4}
lst = [2,2,3,4,4,4]
def shrink(lst):
idx = 0
while len(lst) > idx+1:
a, b = lst.pop(idx), lst.pop(idx)
if a == b:
lst.insert(idx, a+1)
idx = 0
else:
lst.insert(idx, b)
lst.insert(idx, a)
idx += 1
shrink(lst)
print(lst)
Prints:
[6]
For [5, 5, 5, 1] prints [6, 5, 1]
This can be done in near-linear time like so:
a = [2, 2, 3, 4, 4, 4]
b = [1, 2, 2, 2, 4, 2, 4]
c = [5, 5, 5, 1]
def shrink_array(a):
res = []
for i in range(1, len(a)+1):
if i < len(a) and a[i] == a[i-1]: # if equal to previous
a[i] += 1 # increment and move on
else:
if len(res) > 0 and res[-1] == a[i-1]: # if equal to last in res
res[-1] += 1 # increment last in res
else:
res.append(a[i-1]) # add to res
while len(res) > 1 and res[-1] == res[-2]: # shrink possible duplicates
res[-2] += 1
del res[-1]
return(res)
for arr in [a, b, c]:
print(shrink_array(arr))
Output:
[6]
[1, 3, 2, 4, 2, 4]
[6, 5, 1]

How do I account for an array that doesn't exist?

So the code works fine if I'm trying to find the size of the land in the middle, but it fails when calculating the size of the small land in the corner. How do I return 0 if it starts checking for land outside of the given arrays?
M = 'land'
o = 'water'
world = [[o,o,o,o,o,o],
[o,M,M,M,o,o],
[o,o,M,M,o,o],
[o,o,o,o,o,M]]
def continent_size world, x, y
if world[x][y] != 'land'
return 0
end
size = 1
world[x][y] = 'counted land'
size = size + continent_size(world, x-1, y-1)
size = size + continent_size(world, x , y-1)
size = size + continent_size(world, x+1, y-1)
size = size + continent_size(world, x-1, y )
size = size + continent_size(world, x+1, y )
size = size + continent_size(world, x-1, y+1)
size = size + continent_size(world, x , y+1)
size = size + continent_size(world, x+1, y+1)
size
end
puts continent_size(world, 3, 5)
How about some guard clauses at the top of your method like:
# make sure we don't attempt to index into an array at less than zero
return 0 if x < 0
return 0 if y < 0
# make sure there is a value in the requested slot
return 0 unless world[x]
return 0 unless world[x][y]
def continent_size(world, row, col)
rows = ([row-1, 0].max..[row+1, world.size-1].min).to_a
cols = ([col-1,0].max..[col+1, world.first.size-1].min).to_a
rows.product(cols).count { |r,c| world[r][c] == M }
end
world.size.times { |r| world.first.size.times { |c|
puts "[#{r},#{c}] -> #{continent_size(world, r, c)}" } }
[0,0] -> 1
[0,1] -> 2
[0,2] -> 3
[0,3] -> 2
[0,4] -> 1
[0,5] -> 0
[1,0] -> 1
[1,1] -> 3
[1,2] -> 5
[1,3] -> 4
[1,4] -> 2
[1,5] -> 0
[2,0] -> 1
[2,1] -> 3
[2,2] -> 5
[2,3] -> 4
[2,4] -> 3
[2,5] -> 1
[3,0] -> 0
[3,1] -> 1
[3,2] -> 2
[3,3] -> 2
[3,4] -> 2
[3,5] -> 1
For
row = 0
col = 3
the steps are as follows. (Note world_size #=> 4 and world.first.size #=> 6.)
rows = ([row-1, 0].max..[row+1, world.size-1].min).to_a
#=> ([-1,0].max..[1,3].min).to_a
#=> (0..1).to_a
#=> [0, 1]
cols = ([col-1,0].max..[col+1,world[0].size-1].min).to_a
#=> ([2,0].max..[4,5].min).to_a
#=> (2..4).to_a
#=> [2,3,4]
a = rows.product(cols)
#=> [[0, 2], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4]]
a.count { |r,c| world[r][c] == M }
#=> 2

Iterate through array forwards then backwards

[1,2,3,4,5]
=>1,2,3,4,5,4,3,2,1
=>1,2,3,2,3,4,5,4,3 #I need to be able to reverse the iteration at certain points
I first tried something like:
a = [1,2,3,4,5]
a.each {|i|
if i % 9 == 0
a.reverse!
}
but that just reverses the entire array and starts counting from the index it left off on. I need to to shift the direction of each, so to speak.
i, counter = 0, 1 # initialize index to 0, counter to 1
while(i < a.length && i >= 0) do
puts a[i]
i+= counter # increment counter
counter*= -1 if(condition) # Multiply counter with -1 to reverse it
end
Well, here's a moving "cursor" for your array:
module Cursor
def current_index
#current_index ||= 0
end
def step
#current_index = current_index + direction
handle_boundary
end
def step_back
#current_index = current_index + (direction * -1)
handle_boundary
end
def handle_boundary
if current_index == length || current_index == 0
turn_around
end
end
def direction
#direction ||= 1
end
def turn_around
#direction = direction * -1
end
def current
self[current_index]
end
end
And here's how you use it:
array = [1,2,3,4,5]
arary.extend Cursor
array.current # returns the item in current position
array.step # moves a step forward, turns around when it reaches either end of the array
array.step_back # moves a step backward without switching the direction
array.turn_around # switch the direction
Now you can travel around as you want :D
You can make use of Enumerator class to create custom enumerable that can providing custom iteration through the array. In below code, I am monkey-patching Array class for convenience (also due to resemblance of the method to Array#cycle), though solution can be done without monkey-patching as well.
class Array
def reversible_cycle
Enumerator.new do |y|
index = 0
direction = :forward
loop do
direction = :backward if index + 1 >= size
direction = :forward if index <= 0
y << self[index]
index += (direction == :forward ? +1 : -1)
end
end
end
end
p [1,2,3,4,5].reversible_cycle.take(9)
#=> [1, 2, 3, 4, 5, 4, 3, 2, 1]
p [1,2,3,4,5].reversible_cycle.take(13)
#=> [1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5]
p [1,2,3,4,5].reversible_cycle.take(17)
#> [1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1]
p [1,2,3,4,5].reversible_cycle.take(21)
#=> [1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1, 2, 3, 4, 5]
For scenarios where you are changing direction without iterating the array fully in one direction, you will have to give some examples so that one can see how to modify the above code to accommodate that
You could use Ruby's under-appreciated flip-flop operator.
arr = [1,2,3,4,5]
sz = arr.size
(2*sz-1).times { |i| puts i==0..i==arr.size-1 ? arr[i] : arr[sz-i-2] }
1
2
3
4
5
4
3
2
1

Resources