How to get diagonal values from specific point? - ruby

Suppose I have 10x10 matrix with the following data:
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 _ 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90
91 92 93 94 95 96 97 98 99 100
My position is in [4][4]. How can I list the diagonal values from this position?
For example, the expected outcome would be:
[56, 67, 78, 89, 100, 1, 12, 23, 34]
[54, 63, 72, 81, 9, 18, 27, 36]
My current solution
def next?(index, row, size)
(((row + index) % size) + 1 ) % size
end
(1...chess_size).each do |l|
next_el, curr_el = next?(l, row, chess_size), (row + l) % chess_size
# this gets me the first diagnonal. Note that it prints out wrong value
tmp[0] << chess[curr_el][curr_el]
# this gets me the values from current row below to up
tmp[1] << chess[(row + l) % chess_size][row]
tmp[2] << chess[-l][l]
tmp[3] << chess[row][(row + l) % chess_size]
end
Our matrix will always have the same number of rows and columns.

Generally to get the diagonal values from i and j, you can iterate over i and j at the same time up to one of them would be zero. Hence, the main diagonal is (i-1, j-1), (i-2, j-2), ... up to i, j >= 0 and (i + 1, j + 1), (i +2, j + 2), ... up to i, j <= n. For the antidiagonal is (i - 1, j + 1), (i - 2, j + 2), ... up to i >= 0 and j <= n, and (i + 1, j-1), (i + 2, j - 2), ... up to i <= n and j >= 0.

This is a solution to the Hackerrank Queen's attack problem.
Code
def count_moves(n, obs, qrow, qcol)
qdiff = qrow-qcol
qsum = qrow+qcol
l = u = -1
r = d = n
ul = qdiff >= 0 ? qrow-qcol-1 : -1
dr = qdiff >= 0 ? n : qrow+n-qcol
ur = qsum < n ? -1 : qrow-n+qcol
dl = qsum < n ? qrow+qcol+1 : n
obs.uniq.each do |i,j|
case i <=> qrow
when -1 # up
case j <=> qcol
when -1 # up-left
ul = [ul,i].max
when 0 # up same col
u = [u,i].max
when 1 # up-right
ur = [ur,i].max
end
when 0 # same row
j < qcol ? (l = [l,j].max) : r = [r,j].min
else # down
case j <=> qcol
when -1 # down-left
dl = [dl,i].min
when 0 # down same col
d = [d,i].min
when 1 # down-right
dr = [dr,i].min
end
end
end
r + dl + d + dr - l - ul -u - ur - 8
end
Example
Suppose the chess board has 9 rows and columns, with the queen's location shown by the character q and each obstruction shown with the letter o. All other locations are represented by the letter x. We see that the queen has 16 possible moves (7 up and down, 6 left and right, 1 on the up-left to down-right diagonal and 2 on the up-right to down-left diagonal.
arr = [
%w| x x x x x x x x x |, # 0
%w| o x x x x x x x x |, # 1
%w| x o x x x x x x x |, # 2
%w| x x o x x x x x o |, # 3
%w| x x x o x x x x x |, # 4
%w| x x x x x x o x x |, # 5
%w| o o x x x q x x x |, # 6
%w| x x x x x x o x x |, # 7
%w| x x x x x o x x x | # 8
# 0 1 2 3 4 5 6 7 8
]
qrow = qcol = nil
obs = []
n = arr.size
arr.each_with_index do |a,i|
a.each_with_index do |c,j|
case c
when 'o'
obs << [i,j]
when 'q'
qrow=i
qcol=j
end
end
end
qrow
#=> 6
qcol
#=> 5
obs
#=> [[1, 0], [2, 1], [3, 2], [3, 8], [4, 3], [5, 6], [6, 0], [6, 1], [7, 6], [8, 5]]
count_moves(n, obs, qrow, qcol)
#=> 16
Explanation
l is the largest column index of an obstruction in the queen's row that is less than the queen's column index;
r is the smalles column index of an obstruction in the queens that is greater than the queen's column index;
u is the largest largest row index of an obstruction in the queen's column that is less than the queen's row index;
d is the smallest row index of an obstruction in the queen's column that is greater than the queen's row index;
ul is the greatest row index of an obstruction on the queen's top-left to bottom-right diagonal that is less than the queen's row index;
dr is the smallest row index of an obstruction on the queen's top-left to bottom-right diagonal that is greater than the queen's row index;
ur is the greatest row index of an obstruction on the queen's top-right to bottom-left diagonal that is less than the queen's row index; and
dl is the smallest row index of an obstruction on the queen's top-right to bottom-left diagonal that is greater than the queen's row index.
For the example above, before obstructions are taken into account, these variables are set to the following values.
l = 0
r = 9
ul = 0
u = -1
ur = 2
dl = 9
d = 9
dr = 9
Note that if the queen has row and column indices qrow and qcol,
i - j = qrow - qcol for all locations [i, j] on the queen's top-left to bottom- right diagonal; and
i + j = grow + gcol for all locations [i, j] on the queen's top-right to bottom- left diagonal
We then loop through all (unique) obstructions, determining, for each, whether it is in the queen's row, queen's column, or one of the queen's diagonals and then replaces the value of the applicable variable with it's row or column index if it is "closer" to the queen than the previously-closest location.
If, for example, the obstruction is in the queen's row and its column index j is less than the queen's column index, the following calculation is made:
l = [l, j].max
Similarly, if the obstruction is on the queen's top-left to bottom-right diagonal and its row index i is less than the queen's row index, the calculation would be:
ul = [ul, i].max
After all obstructions from the above example have been considered the variables have the following values.
l #=> 1
r #=> 9
ul #=> 4
u #=> -1
ur #=> 5
dl #=> 9
d #=> 8
dr #=> 7
Lastly, we compute the total number of squares to which the queen may move.
qcol - l - 1 + # left
r - qcol - 1 + # right
u - qrow - 1 + # up
grow - d - 1 + # down
ul - qrow - 1 + # up-left
ur - qrow - 1 + # up-right
qrow - dl - 1 + # down-left
qrow - dr - 1 # down-right
which simplifies to
r + dl + d + dr - l - ul -u - ur - 8
#=> 9 + 9 + 8 + 7 - 1 - 4 + 1 - 5 - 8 => 16

I've applied the logic that #OmG provided. Not sure how efficient it would be.
def stackOverflow(matrixSize, *args)
pos, obstacles = *args
chess = (1..(matrixSize * matrixSize)).each_slice(matrixSize).to_a
obstacles.each do |l| chess[l[0]][l[1]] = '_' end
row, col = pos[:row] - 1, pos[:col] - 1
chess[row][col] = '♙'
directions = [[],[],[],[],[],[],[],[]]
(1...matrixSize).each do |l|
directions[0] << chess[row + l][col + l] if (row + l) < matrixSize && (col + l) < chess_size
directions[1] << chess[row - l][col - l] if (row - l) >= 0 && (col - l) >= 0
directions[2] << chess[row + l][col - l] if (row + l) < matrixSize && (col - l) >= 0
directions[3] << chess[row - l][col + l] if (row - l) >= 0 && (col + l) < matrixSize
directions[4] << chess[row + l][col] if row + l < matrixSize
directions[5] << chess[row - l][col] if row - l >= 0
directions[6] << chess[row][col + l] if col + l < matrixSize
directions[7] << chess[row][col - l] if col - l >= 0
end
end
stackOverflow(5, 3, {row: 4, col: 3}, [[4,4],[3,1],[1,2]] )

#CarySwoveland It seems #Jamy is working on another problem from hackerrank queens-attack.
The problem is quite hard because the idea is to never create a matrix in the first place. That is, the test cases become very large, and thus the space complexity will be an issue.
I've changed my implementation, yet still, fail because of timeout issue (this is because test cases become very large). I'm not sure how to make it performant.
Before I show the code. Let me explain what I'm trying to do using illustration:
This is our chess:
---------------------------
| 1 2 3 4 5 |
| 6 7 8 9 10 |
| 11 12 13 14 15 |
| 16 17 18 19 20 |
| 21 22 23 24 25 |
---------------------------
And this is where our queen is located: queen[2][3]
---------------------------
| 1 2 3 4 5 |
| 6 7 8 9 10 |
| 11 12 13 ♙ 15 |
| 16 17 18 19 20 |
| 21 22 23 24 25 |
---------------------------
The queen can attack all 8 directions. I.e:
horizontal(x2):
1. from queen position to left : [13, 12, 11]
2. from queen position to right : [15]
vertical(x2):
1. from queen position to top : [9, 4]
2. from queen position to bottom : [19, 24]
diagonal(x2):
1. from queen position to bottom-right : [20]
2. from queen position to top-left : [8, 2]
diagonal(x2):
1. from queen position to bottom-left : [18, 22]
2. from queen position to top-right : [10]
Because there are no obstacles within those 8 paths, the queen can attack a total of 14 attacks.
Say we have some obstacles:
---------------------------
| 1 2 3 4 5 |
| 6 7 x 9 10 |
| 11 x 13 ♙ 15 |
| 16 17 18 19 x |
| 21 x 23 x 25 |
---------------------------
Now the queen can attack a total of 7 attacks: [13, 18, 19, 15, 10, 9, 4]
Code
MAXI = 10 ** 5
def queens_attack(size, number_of_obstacles, queen_pos, obstacles)
# exit the function if...
# size is negative or more than MAXI. Note MAXI has constraint shown in hackerrank
return if size < 0 || size > MAXI
# the obstacles is negative or more than the MAXI
return if number_of_obstacles < 0 || number_of_obstacles > MAXI
# the queen's position is outside of our chess dimension
return if queen_pos[:row] < 1 || queen_pos[:row] > size
return if queen_pos[:col] < 1 || queen_pos[:col] > size
# the queen's pos is the same as one of the obstacles
return if [[queen_pos[:row], queen_pos[:col]]] - obstacles == []
row, col = queen_pos[:row], queen_pos[:col]
# variable to increment how many places the queen can attack
attacks = 0
# the queen can attack on all directions:
# horizontals, verticals and both diagonals. So let us create pointers
# for each direction. Once the obstacle exists in the path, make the
# pointer[i] set to true
pointers = Array.new(8, false)
(1..size).lazy.each do |i|
# this is the diagonal from queen's pos to bottom-right
if row + i <= size && col + i <= size && !pointers[0]
# set it to true if there is no obstacle in the current [row + i, col + i]
pointers[0] = true unless [[row + i, col + i]] - obstacles != []
# now we know the queen can attack this pos
attacks += 1 unless pointers[0]
end
# this is diagonal from queen's pos to top-left
if row - i > 0 && col - i > 0 && !pointers[1]
# set it to true if there is no obstacle in the current [row - i, col - i]
pointers[1] = true unless [[row - i, col - i]] - obstacles != []
# now we know the queen can attack this pos
attacks += 1 unless pointers[1]
end
# this is diagonal from queen's pos to bottom-left
if row + i <= size && col - i > 0 && !pointers[2]
pointers[2] = true unless [[row + i, col - i]] - obstacles != []
attacks += 1 unless pointers[2]
end
# this is diagonal from queen's pos to top-right
if row - i > 0 && col + i <= size && !pointers[3]
pointers[3] = true unless [[row - i, col + i]] - obstacles != []
attacks += 1 unless pointers[3]
end
# this is verticle from queen's pos to bottom
if row + i <=size && !pointers[4]
pointers[4] = true unless [[row + i, col]] - obstacles != []
attacks += 1 unless pointers[4]
end
# this is verticle from queen's pos to top
if row - i > 0 && !pointers[5]
pointers[5] = true unless [[row - i, col]] - obstacles != []
attacks += 1 unless pointers[5]
end
# this is horizontal from queen's pos to right
if col + i <= size && !pointers[6]
pointers[6] = true unless [[row, col + i]] - obstacles != []
attacks += 1 unless pointers[6]
end
# this is horizontal from queen's pos to left
if col - i > 0 && !pointers[7]
pointers[7] = true unless [[row, col - i]] - obstacles != []
attacks += 1 unless pointers[7]
end
end
p attacks
end
Problem
Now the problem is, I don't know why my code is doing a timeout error from hackerrank. I do know it because of the test case, where the dimension of chess can be 10,000 X 10,000. But dont know what constraint I'm missing.

I've just learned from a comment posted by the OP that I've solved the wrong problem, despite the fact that the OP's question seems quite clear, especially the example, and is consistent with my interpretation. I will leave this solution to the following problem: "Given an array arr such that Matrix(*arr) is an NxM matrix, and a matrix location i,j, return an array [d,a], where elements d and a are elements on the diagonal and antidiagonal that pass through [d,a] but do not include [d,a] and are each rotated so that row index of the first element is i+1 if i < arr.size-1 and is 0 otherwise.
Code
def diagonals(arr, row_idx, col_idx)
ncols = arr.first.size
sum_idx = row_idx+col_idx
diff_idx = row_idx-col_idx
a = Array.new(arr.size * arr.first.size) { |i| i.divmod(ncols) } -[[row_idx, col_idx]]
[a.select { |r,c| r-c == diff_idx }, a.select { |r,c| r+c == sum_idx }].
map do |b| b.sort_by { |r,_| [r > row_idx ? 0:1 , r] }.
map { |r,c| arr[r][c] }
end
end
All elements of the array arr must be equal in size but there is no requirement that arr.size = arr.first.size.
Example
arr = [
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
[21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
[31, 32, 33, 34, 35, 36, 37, 38, 39, 40],
[41, 42, 43, 44, 45, 46, 47, 48, 49, 50],
[51, 52, 53, 54, 55, 56, 57, 58, 59, 60],
[61, 62, 63, 64, 65, 66, 67, 68, 69, 70],
[71, 72, 73, 74, 75, 76, 77, 78, 79, 80],
[81, 82, 83, 84, 85, 86, 87, 88, 89, 90],
[91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
]
diagonals(arr, 4, 4)
#=> [[56, 67, 78, 89, 100, 1, 12, 23, 34],
# [54, 63, 72, 81, 9, 18, 27, 36]]
Explanation
Suppose
arr = (1..16).each_slice(4).to_a
#=> [[ 1, 2, 3, 4],
# [ 5, 6, 7, 8],
# [ 9, 10, 11, 12],
# [13, 14, 15, 16]]
row_idx = 2
col_idx = 1
The steps are as follows.
a = Array.new(arr.size) { |i| Array.new(arr.first.size) { |j| [i,j] } }
#=> [[[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]]]
ncols = arr.first.size
#=> 4
sum_idx = row_idx+col_idx
#=> 3
diff_idx = row_idx-col_idx
#=> 1
a = Array.new(arr.size * arr.first.size) { |i| i.divmod(ncols) } - [[row_idx, col_idx]]
#=> [[0, 0], [0, 1], [0, 2], [0, 3], [1, 0], [1, 1], [1, 2], [1, 3],
# [2, 0], [2, 2], [2, 3], [3, 0], [3, 1], [3, 2], [3, 3]]
Select and sort the locations [r, c] on the top-left to bottom-right diagonal that passes through [row_idx, col_idx].
b = a.select { |r,c| r-c == diff_idx }
#=> [[1, 0], [3, 2]]
c = b.sort_by { |r,_| [r > row_idx ? 0:1 , r] }
#=> [[3, 2], [1, 0]]
Select and sort the locations [r, c] on the top-right bottom-left diagonal that passes through [row_idx, col_idx].
d = a.select { |r,c| r+c == sum_idx }
#=> [[0, 3], [1, 2], [3, 0]]
e = d.sort_by { |r,c| [r > row_idx ? 0:1 , r] }
#=> [[3, 0], [0, 3], [1, 2]]
[c, e].map { |f| f.map { |r,c| arr[r][c] }
#=> [c, e].map { |f| f.map { |r,c| arr[r][c] } }
#=> [[15, 5], [13, 4, 7]]

I've just learned from a comment posted by the OP that I've solved the wrong problem, despite the fact that the OP's question seems quite clear, especially the example, and is consistent with my interpretation. I will leave this solution to the following problem: "Given an array arr such that Matrix(*arr) is an NxM matrix, and a matrix location i,j, return an array [d,a], where elements d and a are elements on the diagonal and antidiagonal that pass through [d,a] but do not include [d,a] and are each rotated so that row index of the first element is i+1 if i < arr.size-1 and is 0 otherwise.
The following approach uses methods from the Matrix class.
Code
require 'matrix'
def diagonals(arr, row_idx, col_idx)
[diag(arr, row_idx, col_idx),
diag(arr.map(&:reverse).transpose, arr.first.size-1-col_idx, row_idx)]
end
def diag(arr, row_idx, col_idx)
nrows, ncols = arr.size, arr.first.size
lr = [ncols-col_idx, nrows-row_idx].min - 1
ul = [col_idx, row_idx].min
m = Matrix[*arr]
[*m.minor(row_idx+1, lr, col_idx+1, lr).each(:diagonal).to_a,
*m.minor(row_idx-ul, ul, col_idx-ul, ul).each(:diagonal).to_a]
end
Example
arr = [
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
[21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
[31, 32, 33, 34, 35, 36, 37, 38, 39, 40],
[41, 42, 43, 44, 45, 46, 47, 48, 49, 50],
[51, 52, 53, 54, 55, 56, 57, 58, 59, 60],
[61, 62, 63, 64, 65, 66, 67, 68, 69, 70],
[71, 72, 73, 74, 75, 76, 77, 78, 79, 80],
[81, 82, 83, 84, 85, 86, 87, 88, 89, 90],
[91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
]
diagonals arr, 4, 4
#=> [[56, 67, 78, 89, 100, 1, 12, 23, 34], [54, 63, 72, 81, 9, 18, 27, 36]]
diagonals arr, 4, 5
#=> [[57, 68, 79, 90, 2, 13, 24, 35], [55, 64, 73, 82, 91, 10, 19, 28, 37]]
diagonals arr, 0, 9
#=> [[], [19, 28, 37, 46, 55, 64, 73, 82, 91]]
Explanation
Suppose the array and target location were as follows.
arr = (1..30).each_slice(6).to_a
#=> [[ 1, 2, 3, 4, 5, 6],
# [ 7, 8, 9, 10, 11, 12],
# [13, 14, 15, 16, 17, 18],
# [19, 20, 21, 22, 23, 24],
# [25, 26, 27, 28, 29, 30]]
row_idx = 2
col_idx = 3
Note arr[2][3] #=> 16. We obtain the diagonal with negative slope by computing the diagonals of two matrix minors:
[[23, 24],
[29, 30]]
and
[[2, 3],
[8, 9]]
giving us
[*[23, 30], *[2, 9]]
#=> [23, 30, 2, 9]
To obtain the other diagonal we rotate the array anti-clockwise 90 degrees, adjust row_idx and col_idx and repeat the above procedure.
arr.map(&:reverse).transpose
#=> [[6, 12, 18, 24, 30],
# [5, 11, 17, 23, 29],
# [4, 10, 16, 22, 28],
# [3, 9, 15, 21, 27],
# [2, 8, 14, 20, 26],
# [1, 7, 13, 19, 25]]
ncols = arr.first.size
#=> 6
row_idx, col_idx = ncols-1-col_idx, row_idx
#=> [2, 2]
We now extract the diagonals from the matrix minors
[[21, 27],
[20, 26]]
and
[[6, 12],
[5, 11]]
to obtain the second diagonal:
[21, 26, 6, 11]

def possible_moves(val):
# val is a value between 0 and n*n-1
for i in range(n*n):
if i == val:
board[i // n][i % n] = 'Q'
continue
#mark row and column with a dot
if i % n == val % n or i // n == val // n:
board[i//n][i%n] = '.'
# mark diagonals with a dot
if i % (n + 1) == val % (n + 1) and abs(i % n - val % n) == abs(i // n - val // n):
board[i//n][i%n] = '.'
if i % (n - 1) == val % (n - 1) and abs(i % n - val % n) == abs(i // n - val // n):
board[i//n][i%n] = '.'
n = 10 #board size = n x n
board = [['0' for x in range(n)] for y in range(n)] #initialize board with '0' in every row and col
possible_moves(40)
At the end you will have a 'Q' where the queen s positioned, '0' where the Q cannot movea and '.' where she can moves

Related

algorithm to map weighted objects to their amount

need algorithm to map weighted objects to their amount and need to make amount minimal for each object with keeping ratio between weights
example 1:
input: object1: 40, object2: 60, object3: 80
output: object1: 2, object2: 3, object3: 4
this can be solved by dividing object weight with gcd of weights of all objects
example 2:
input: object1: 3, object2: 15
output: object1: 1, object2: 5
example 3:
input: object1: 13, object2: 97, object3: 20
output: object1: 1, object2: 7, object3: 2
example 4:
input: object1: 1, object2: 17, object3: 97
output: object1: 0, object2: 1, object3: 5
gcd is not applicable for example 3 and 4, what's the algorithm can be used, is there any idea?
limitations: range of weights 0-99, maximum sum of all amounts is 32
As I mentioned in comments, dividing by the GCD is the best you can do if you need integers that exactly preserve the ratio.
For floats that are very close to the ratio, divide everything by the min.
Ruby example:
def f(weights)
min_wt = weights.min
ans = []
weights.each do |wt|
ans.append(wt.to_f/min_wt)
end
return ans
end
> f([40, 60, 80])
=> [1.0, 1.5, 2.0]
> f([13, 97, 20])
=> [1.0, 7.461538461538462, 1.5384615384615385]
Alternate approach to get integers: Check every scaling factor in your range (final sum 1-32). I'm taking 1 as the floor for each integer since dividing by 0 is undefined.
Ruby code (not beautifully written):
def f(unsorted_weights)
weights = unsorted_weights.sort!
orig_sum_of_wts = weights.sum
best_error = Float::INFINITY
best_sum_of_wts = 0
1.upto(32) do |new_sum_of_wts|
error = 0.0
new_wts = []
0.upto(weights.length - 1) do |i|
new_wts[i] = [1, weights[i] * new_sum_of_wts / orig_sum_of_wts].max
end
0.upto(weights.length - 2) do |i|
new_wt_i = weights[i] * new_sum_of_wts / orig_sum_of_wts
(i+1).upto(weights.length - 1) do |j|
new_wt_j = weights[j] * new_sum_of_wts / orig_sum_of_wts
error += (new_wts[j].to_f / [new_wts[i], 1.0].max - weights[j].to_f / [weights[i], 1.0].max).abs
end
if error < best_error
best_sum_of_wts = new_sum_of_wts
best_error = error
end
end
end
ans = []
0.upto(weights.length - 1) do |i|
ans.append([1, weights[i] * best_sum_of_wts / orig_sum_of_wts].max)
end
puts "#{ans.to_s}"
end
Results:
> f([40, 60, 80])
[2, 3, 4]
> f([40, 60])
[2, 3]
> f([13, 97, 20])
[2, 3, 15]
> f([1, 17, 97])
[1, 4, 26]
For 13, 20, 97, I get 2,3,15 vs your 1,2,7.
Ratios: 20/13 = 1.538, 3/2 = 1.500, 2/1 = 2.000
97/13 = 7.462, 15/2 = 7.500, 7/1 = 7.000
97/20 = 4.850, 15/3 = 5.000, 7/2 = 3.500
Cumulative error for 2,3,15: 0.038 + 0.038 + 0.150 = 0.226
Cumulative error for 1,2,7: 0.038 + 0.462 + 1.350 = 2.274

Generate numbers from n to m from a list

I'll start with an example; given n = 1 and m = 100 and a list [1, 2, 3]
generate all numbers with 1 digit and two digits and so on but they need to be less then 100 in this case.
Output:
- 1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33
Then we stop because the next numbers will over 100, e.g:
- 111, 112, 113, 121, 122, 123, 131, 132, 133, 21..,. 22..., 23..., 31, 32, 33
As you noticed I am appending 1, 2, 3, 4 to a number created before, to do this I am using a recursive function, which is started in a for loop for each number in my list, and they it runs till the generated numbers are greater then my limit.
def x(str, finish, d, c)
return if d >= finish
[1, 2, 3, 4].each do |e|
x(str, end, d*c+e)
end
# do something if d >= str
end
This works fine if I need to start from 1, but if my starting number is a lot bigger, I still need to start to create this sequence.
Can somebody help me with a solution that will produce the same sequences, but from any starting point rather then 1, so if for example the starting point was 100 and end 200 the output will be:
111, 112, 113, 114, 121, 122, 123, 124, 131, 132, 132 [...]
A solution in any programming language would be nice, but please not builtin core libraries.
Code
def generate_em(minimum, maximum, list)
digits_min = minimum.to_s.size
digits_min += 1 if minimum > (list.max.to_s*digits_min).to_i
digits_max = maximum.to_s.size
digits_max -= 1 if maximum < (list.min.to_s*digits_max).to_i
(digits_min..digits_max).each_with_object([]) { |n,arr|
arr.concat(list.repeated_permutation(n).to_a.map { |a| a.join.to_i }) }.
uniq.
select { |n| (minimum..maximum).cover?(n) }
end
Examples
#1
minimum = 1
maximum = 100
list = [1, 2, 3]
generate_em(minimum, maximum, list)
#=> [1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33]
#2
minimum = 78
maximum = 3332
list = [3, 4, 5, 6, 7]
generate_em(minimum, maximum, list)
#=> [333, 334, 335, 336, 337, 343, 344, 345, 346, 347, 353, 354, 355, 356,
# 357, 363, 364, 365, 366, 367, 373, 374, 375, 376, 377, 433, 434, 435,
# 436, 437, 443, 444, 445, 446, 447, 453, 454, 455, 456, 457, 463, 464,
# 465, 466, 467, 473, 474, 475, 476, 477, 533, 534, 535, 536, 537, 543,
# 544, 545, 546, 547, 553, 554, 555, 556, 557, 563, 564, 565, 566, 567,
# 573, 574, 575, 576, 577, 633, 634, 635, 636, 637, 643, 644, 645, 646,
# 647, 653, 654, 655, 656, 657, 663, 664, 665, 666, 667, 673, 674, 675,
# 676, 677, 733, 734, 735, 736, 737, 743, 744, 745, 746, 747, 753, 754,
# 755, 756, 757, 763, 764, 765, 766, 767, 773, 774, 775, 776, 777]
#3
minimum = 0
maximum = 100
list = [0, 1, 2]
generate_em(minimum, maximum, list)
#=> [0, 1, 2, 10, 11, 12, 20, 21, 22, 100]
Explanation
Example #1
The steps for the first example above are as follows.
digits_min = minimum.to_s.size
#=> 1
Increase digits_min by one if mimimum is larger than the largest digits_min digits from list.
digits_min += 1 if minimum > (list.max.to_s*digits_min).to_i
digits_min
#=> 1
digits_max = maximum.to_s.size
#=> 3
Decrease digits_max by one if maximum is smaller than the smallest digits_max digits from list.
digits_max -= 1 if maximum < (list.min.to_s*digits_max).to_i
digits_max
#=> 2
We improve efficiency by having reduced digits_max from 3 to 2
c = digits_min..digits_max
#=> 1..2
d = c.each_with_object([])
#=> #<Enumerator: 1..2:each_with_object([])>
We can see the elements that will be generated by this enumerator by invoking Enumerable#entries (or Enumerable#to_a) on it.
d.entries
#=> [[1, []], [2, []]]
n, arr = d.next
#=> [1, []]
n #=> 1
arr
#=> []
e = list.permutation(n)
#=> #<Enumerator: [1, 2, 3]:permutation(2)>
f = e.to_a
#=> [[1], [2], [3]]
arr.concat f
#=> [[1], [2], [3]]
n, arr = d.next
#=> [2, [[1], [2], [3]]]
n #=> 2
arr
#=> [[1], [2], [3]]
e = list.permutation(n)
#=> #<Enumerator: [1, 2, 3]:permutation(2)>
f = e.to_a
#=> [[1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]]
arr.concat f
#=> [[1], [2], [3], [1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]]
arr is returned by each_with_object's block.
g = arr.map { |a| a.join.to_i }
#=> [1, 2, 3, 12, 13, 21, 23, 31, 32]
h = g.uniq
#=> [1, 2, 3, 12, 13, 21, 23, 31, 32]
h.select { |n| (minimum..maximum).cover?(n) }
#=> [1, 2, 3, 12, 13, 21, 23, 31, 32]
Example #2
In the second example no two-digit combinations are generated because
78 > (list.max.to_s*2).to_i
#=> 78 > 77 => true
and no four-digit combinations are generated because
3332 < (list.min.to_s*4).to_i
#=> 3332 < 3333 => true
Example #3
Without uniq, the method would have returned duplicate values:
[0, 1, 2, 0, 1, 2, 10, 11, 12, 20, 21, 22, 0, 1, 2, 10, 11, 12, 20, 21, 22, 100]
Since you wrote your example code in Ruby, you could use repeated_permutation :
def possible_numbers(arr, min, max)
min_digits = min.to_s.size
max_digits = max.to_s.size
(min_digits..max_digits).flat_map do |i|
arr.repeated_permutation(i)
.map { |digits| digits.join.to_i }
.select { |number| number >= min && number <= max }
.uniq
end
end
p possible_numbers([1, 2, 3], 100, 200)
# => [111, 112, 113, 121, 122, 123, 131, 132, 133]
So, this takes an input of the digits provided, makes all the digit combinations repeatedly until it reaches your max. You'll want to adjust them to include recovery and saves in the event non-integers are entered or if it's otherwise impossible to hit the max for some reason - but this is a solid framework.
digits = [1,2,3]
max = 100
def possible_numbers(arr, max)
loop do
arr.product(arr).each do |combo|
return arr.uniq if combo.join("").to_i > max
arr << combo.join("").to_i
end
end
end
possible_numbers(digits, max)
Output:
=> [1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33]
You can proceed from "right" (least significant digit) to "left" (most significant digit), keeping track of two values:
min, the least valid integer that has the same number of digits as what you've processed so far. So, for example, if you've processed a trailing 27, then the least valid two-digit integer is 11.
max, the least valid integer that has the same number of digits as what you've processed so far and is greater than or equal to what you've processed so far. So, for example, if you've processed a trailing 27, then the least valid two-digit integer greater than or equal to 27 is 31.
Note that max won't always exist. For example, there's no valid two-digit integer greater than or equal to 70.
The reason you need min is that if you encounter a digit that's not in your list of allowed digits, then the new max will incorporate the previous min instead of the previous max. (For example, the least valid two-digit integer greater than or equal to 02 is 11, not 12.)
At the end, you return max, if it exists; otherwise, you return min, but prepending the least digit from your list.
For example, if this.allowedDigits is a collection of allowed digits, then we can write (in Java):
private Integer getAllowedDigitGreaterThanOrEqualTo(final int digit) {
for (int result = digit; result < 10; ++result) {
if (this.allowedDigits.contains(result)) {
return result;
}
}
// digit is bigger than anything in the list:
return null;
}
private int getAllowedNumberGreaterThanOrEqualTo(int n) {
int minResult = 0;
Integer maxResult = 0;
int powerOfTen = 1;
while (n > 0) {
final int digit = n % 10;
n /= 10;
minResult = getAllowedDigitGreaterThanOrEqualTo(0) * powerOfTen + minResult;
if (maxResult != null && this.allowedDigits.contains(digit)) {
maxResult = digit * powerOfTen + maxResult;
} else {
final Integer newDigit = getAllowedDigitGreaterThanOrEqualTo(digit + 1);
if (newDigit == null) {
maxResult = null;
} else {
maxResult = newDigit * powerOfTen + minResult;
}
}
powerOfTen *= 10;
}
if (maxResult == null) {
return getAllowedDigitGreaterThanOrEqualTo(1) * powerOfTen + minResult;
} else {
return maxResult;
}
}

Is there a way to specify a multi-step in Ruby?

I'm working with some lazy iteration, and would like to be able to specify a multiple step for this iteration. This means that I want the step to alternate between a and b. So, if I had this as a range (not lazy just for simplification)
(1..20).step(2, 4)
I would want my resulting range to be
1 # + 2 =
3 # + 4 =
7 # + 2 =
9 # + 4 =
13 # + 2 =
15 # + 4 =
19 # + 2 = 21 (out of range, STOP ITERATION)
However, I cannot figure out a way to do this. Is this at all possible in Ruby?
You could use a combination of cycle and Enumerator :
class Range
def multi_step(*steps)
a = min
Enumerator.new do |yielder|
steps.cycle do |step|
yielder << a
a += step
break if a > max
end
end
end
end
p (1..20).multi_step(2, 4).to_a
#=> [1, 3, 7, 9, 13, 15, 19]
Note that the first element is 1, because the first element of (1..20).step(2) is also 1.
It takes exclude_end? into account :
p (1...19).multi_step(2, 4).to_a
#=> [1, 3, 7, 9, 13, 15]
And can be lazy :
p (0..2).multi_step(1,-1).first(20)
#=> [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
p (0..Float::INFINITY).multi_step(*(1..100).to_a).lazy.map{|x| x*2}.first(20)
#=> [0, 2, 6, 12, 20, 30, 42, 56, 72, 90, 110, 132, 156, 182, 210, 240, 272, 306, 342, 380]
Here's a variant of FizzBuzz, which generates all the multiples of 3 or 5 but not 15 :
p (3..50).multi_step(2,1,3,1,2,6).to_a
#=> [3, 5, 6, 9, 10, 12, 18, 20, 21, 24, 25, 27, 33, 35, 36, 39, 40, 42, 48, 50]
Ruby doesn't have a built-in method for stepping with multiple values. However, if you don't actually need a lazy method, you can use Enumerable#cycle with an accumulator. For example:
range = 1..20
accum = range.min
[2, 4].cycle(range.max) { |step| accum += step; puts accum }
Alternatively, you could construct your own lazy enumerator with Enumerator::Lazy. That seems like overkill for the given example, but may be useful if you have an extremely large Range object.

Ruby, Linearize an array with a sorted number series by adding items in order to keep the differentials lower or equal to X

I need a function that transforms an array of integers in descending order, not allowing any integer in position i to be X times greater than the following in position i+1, by adding 1 or more elements in between, and keeping the original numbers intact.
The resulting sorted array will meet the criteria:
array[i] <= array[i+1]*1.5
For every i.
Examples:
x = 1.5
Transformation over a
a = [5, 3]
func(a, x) = [5,4,3]
a[0] > a[1]*1.5, so func adds 4 = (a[0].to_f/1.5).ceil and sorts a
a is now [5,4,3]
Transformation over b
b = [50, 4]
func(b, x) = [50, 34, 23, 16, 11, 8, 6, 4]
b[0] > b[1]*1.5, so func adds 34 = (b[0].to_f/1.5).ceil and sorts b
b is now [50,34,4]
b[1] > b[2]*1.5, so func adds 23 = (b[1].to_f/1.5).ceil and sorts b
b is now [50,34,23,4]
b[2] > b[3]*1.5, so func adds 16 = (b[2].to_f/1.5).ceil and sorts b
b is now [50,34,23,16,4]
b[3] > b[4]*1.5, so func adds 11 = (b[3].to_f/1.5).ceil and sorts b
b is now [50,34,23,16,11,4]
b[4] > b[5]*1.5, so func adds 8 = (b[4].to_f/1.5).ceil and sorts b
b is now [50,34,23,16,11,8,4]
b[5] > b[6]*1.5, so func adds 6 = (b[5].to_f/1.5).ceil and sorts b
b is now [50,34,23,16,11,8,4]
func returns [50, 34, 23, 16, 11, 8, 6, 4]
Transformation over c
c = [50, 20, 10, 4, 3, 2]
func(c, x) = [50, 34, 23, 20, 14, 10, 7, 5, 4, 3, 2]
c[0] > c[1]*1.5, so func adds 34 = (c[0].to_f/1.5).ceil and sorts c
c is now [50,34,20,10,4,3,2]
c[1] > c[2]*1.5, so func adds 23 = (c[1].to_f/1.5).ceil and sorts c
c is now [50,34,23,20,10,4,3,2]
c[3] > c[4]*1.5, so func adds 14 = (c[3].to_f/1.5).ceil and sorts c
c is now [50,34,23,20,14,10,4,3,2]
c[5] > c[6]*1.5, so func adds 7 = (c[5].to_f/1.5).ceil and sorts c
c is now [50,34,23,20,14,10,7,4,3,2]
c[6] > c[7]*1.5, so func adds 5 = (c[6].to_f/1.5).ceil and sorts c
c is now [50,34,23,20,14,10,7,5,4,3,2]
func returns [50, 34, 23, 20, 14, 10, 7, 5, 4, 3, 2]
How can this be done in a functional and clean way?
A pure functional way:
def func(a, x, i = 0)
if i == a.size - 1
a
else
if a[i] <= a[i + 1] * x
func a, x, i + 1
else
func a.take(i + 1) + [(a[i].to_f / x).ceil] + a.drop(i + 1), x, i + 1
end
end
end
I'm getting the exact same output as your first and third examples, but not for the second -- your sample output seems to be incorrect.
Test:
p func [5, 3], 1.5
p func [50, 4], 1.5
p func [50, 20, 10, 4, 3, 2], 1.5
Output:
[5, 4, 3]
[50, 34, 23, 16, 11, 8, 6, 4]
[50, 34, 23, 20, 14, 10, 7, 5, 4, 3, 2]
This may be useful. It divides intervals geometrically so that each subdivision has (as near as is possible) the same multiplier as the others, instead of using 1.5 for all but the last and then whatever's left over.
include Math
def geometric_interpolation(arr, ratio)
log_ratio = log(ratio)
result = []
arr.each_cons(2) do |pair|
logs = pair.map { |x| log(x) }
log_interval = logs[0] - logs[1]
num = (log_interval / log_ratio).round(12).ceil
result += [ pair[0] ] + (1...num).map { |n| exp(logs[0] - log_interval * n / num).round }
end
result + [ arr[-1] ]
end
a = [5, 3]
b = [50, 4]
c = [50, 20, 10, 4, 3, 2]
p geometric_interpolation(a, 1.5)
p geometric_interpolation(b, 1.5)
p geometric_interpolation(c, 1.5)
output
[5, 4, 3]
[50, 35, 24, 17, 12, 8, 6, 4]
[50, 37, 27, 20, 14, 10, 7, 5, 4, 3, 2]

Finding continuous number sequence

How to find the longest continuous number sequence in array of number arrays? Each array of numbers represent one or zero numbers in resulting sequence.
Example ([] - represents array (like in javascript)):
[
[1, 5, 6],
[7],
[22, 34],
[500, 550],
[60, 1],
[90, 100],
[243],
[250, 110],
[150],
[155],
[160]
]
Correct output would be: [1, 7, 22, 60, 90, 110, 150, 155, 160]
Detailed output:
1, -- index 1 all 1, 5 and 6 would match here, pick the smallest
7, -- index 2
22, -- index 3
-- index 4 skipped, the sequence would end here or wouldn't be the longest possible
60, -- index 5 picked 60, because 1 wouldn't continue in the sequence
90, -- index 6
-- index 7 skipped, the sequence would end here or wouldn't be the longest possible
110, -- index 8
150, -- index 9
155, -- index 10
160 -- index 11
A possible approach is to use dynamic programming using as parameters the last value and the index of first sub-array to consider.
This is a solution in Python based on recursion with memoization
data = [[1, 5, 6],
[7],
[22, 34],
[500, 550],
[60, 1],
[90, 100],
[243],
[250, 110],
[150],
[155],
[160]]
def longest(x0, i, _cache=dict()):
if i == len(data):
return []
try:
return _cache[x0, i]
except KeyError:
best = longest(x0, i+1)
for x in data[i]:
if x >= x0:
L = [x] + longest(x, i+1)
if len(L) > len(best):
best = L
_cache[x0, i] = best
return best
print longest(0, 0)

Resources