How to find the sum in a matrix with dynamic programming - algorithm

Please, I would like to find the maximum sum with only one value per row. I already made the resolution by brute force and it is O (N^5). Now I would like to find a way with dynamic programming or another way to reduce the complexity.
For example:
Matrix:
100 5 4 3 1
90 80 70 60 50
70 69 65 20 10
60 20 10 5 1
50 45 15 6 1
Solution for 5 sets:
100 + 90 + 70 + 60 + 50 = 370
100 + 90 + 69 + 60 + 50 = 369
100 + 90 + 70 + 60 + 45 = 365
100 + 90 + 65 + 60 + 50 = 365
100 + 90 + 69 + 60 + 45 = 364
Sum: 1833
example for the sum with brute force:
for(int i=0; i<matrix[0].size(); i++) {
for(int j=0; j<matrix[1].size(); j++) {
for(int k=0; k<matrix[2].size(); k++) {
for(int l=0; l<matrix[3].size(); l++) {
for(int x=0; x<matrix[4].size(); x++) {
sum.push_back(matrix[0][i] + matrix[1][j] + matrix[2][k] + matrix[3][l] + matrix[4][x]);
}
}
}
}
}
sort(sum.begin(), sum.end(), mySort);
Thanks!

You can solve it in O(k*log k) time with Dijkstra's algorithm. A node in a graph is represented by a list with 5 indexes of the numbers in the corresponding rows of the matrix.
For example in the matrix
100 5 4 3 1
90 80 70 60 50
70 69 65 20 10
60 20 10 5 1
50 45 15 6 1
the node [0, 0, 2, 0, 1] represents the numbers [100, 90, 65, 60, 45]
The initial node is [0, 0, 0, 0, 0]. Every node has up to 5 outgoing edges increasing 1 of the 5 indexes by 1, and the distance between nodes is the absolute difference in the sums of the indexed numbers.
So for that matrix the edges from the node [0, 0, 2, 0, 1] lead:
to [1, 0, 2, 0, 1] with distance 100 - 5 = 95
to [0, 1, 2, 0, 1] with distance 90 - 80 = 10
to [0, 0, 3, 0, 1] with distance 65 - 20 = 45
to [0, 0, 2, 1, 1] with distance 60 - 20 = 40
to [0, 0, 2, 0, 2] with distance 45 - 15 = 30
With this setup you can use Dijkstra's algorithm to find k - 1 closest nodes to the initial node.

Update I previously used a greedy algorithm, which doesn't work for this problem. Here is a more general solution.
Suppose we've already found the combinations with the top m highest sums. The next highest combination (number m+1) must be 1 step away from one of these, where a step is defined as shifting focus one column to the right in one of the rows of the matrix. (Any combination that is more than one step away from all of the top m combinations cannot be the m+1 highest, because you can convert it to a higher one that is not in the top m by undoing one of those steps, i.e., moving back toward one of the existing combinations.)
For m = 1, we know that the "m highest combinations" just means the combination made by taking the first element of each row of the matrix (assuming each row is sorted from highest to lowest). So then we can work out from there:
Create a set of candidate combinations to consider for the next highest position. This will initially hold only the highest possible combination (first column of the matrix).
Identify the candidate with the highest sum and move that to the results.
Find all the combinations that are 1 step away from the one that was just added to the results. Add all of these to the set of candidate combinations. Only n of these will be added each round, where n is the number of rows in the matrix. Some may be duplicates of previously identified candidates, which should be ignored.
Go back to step 2. Repeat until there are 5 results.
Here is some Python code that does this:
m = [
[100, 5, 4, 3, 1],
[90, 80, 70, 60, 50],
[70, 69, 65, 20, 10],
[60, 20, 10, 5, 1],
[50, 45, 15, 6, 1]
]
n_cols = len(m[0]) # matrix width
# helper function to calculate the sum for any combination,
# where a "combination" is a list of column indexes for each row
score = lambda combo: sum(m[r][c] for r, c in enumerate(combo))
# define candidate set, initially with single highest combination
# (this set could also store the score for each combination
# to avoid calculating it repeatedly)
candidates = {tuple(0 for row in m)}
results = set()
# get 5 highest-scoring combinations
for i in range(5):
result = max(candidates, key=score)
results.add(result)
candidates.remove(result) # don't test it again
# find combinations one step away from latest result
# and add them to the candidates set
for j, c in enumerate(result):
if c+1 >= n_cols:
continue # don't step past edge of matrix
combo = result[:j] + (c+1,) + result[j+1:]
if combo not in results:
candidates.add(combo) # drops dups
# convert from column indexes to actual values
final = [
[m[r][c] for r, c in enumerate(combo)]
for combo in results
]
final.sort(key=sum, reverse=True)
print(final)
# [
# [100, 90, 70, 60, 50]
# [100, 90, 69, 60, 50],
# [100, 90, 70, 60, 45],
# [100, 90, 65, 60, 50],
# [100, 90, 69, 60, 45],
# ]

If you want just maximum sum, then sum maximum value at each row.
That is,
M = [[100, 5, 4, 3, 1],
[90, 80, 70, 60, 50],
[70, 69, 65, 20, 10],
[60, 20, 10, 5, 1],
[50, 45, 15, 6, 1]]
sum(max(row) for row in M)
Edit
It is not necessary to use dynamic programming, etc.
There is simple rule: select next number considering difference between the number and current number.
Here is a code using numpy.
import numpy as np
M = np.array(M)
M = -np.sort(-M, axis = 1)
k = 3
answer = []
ind = np.zeros(M.shape[0], dtype = int)
for _ in range(k):
answer.append(sum(M[list(range(M.shape[0])), ind]))
min_ind = np.argmin(M[list(range(len(ind))), ind] - M[list(range(len(ind))), ind+1])
ind[min_ind] += 1
Result is [370, 369, 365].

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

How to get diagonal values from specific point?

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

Algorithm to efficiently select rows from a matrix such that column totals are equal

The practical application of this problem is group assignment in a psychology study, but the theoretical formulation is this:
I have a matrix (the actual matrix is 27x72, but I'll pick a 4x8 as an example):
1 0 1 0
0 1 0 1
1 1 0 0
0 1 1 0
0 0 1 1
1 0 1 0
1 1 0 0
0 1 0 1
I want to pick half of the rows out of this matrix such that the column totals are equal (thus effectively creating two matrices with equivalent column totals). I cannot rearrange values within the rows.
I have tried some brute force solutions, but my matrix is too large for that to be effective, even having chosen some random restrictions first. It seems to me that the search space could be constrained with a better algorithm, but I haven't been able to think of one thus far. Any ideas? It is also possible that there is no solution, so an algorithm would have to be able to deal with that. I have been working in R, but I could switch to python easily.
Update
Found a solution thanks to ljeabmreosn. Karmarkar-Karp worked great for an algorithm, and converting the rows to base 73 was inspired. I had a surprising hard time finding code that would actually give me the sub-sequences rather than just the final difference (maybe most people are only interested in this problem in the abstract?). Anyway this was the code:
First I converted my rows in to base 73 as the poster suggested. To do this I used the basein package in python, defining an alphabet with 73 characters and then using the basein.decode function to convert to decimel.
For the algorithm, I just added code to print the sub-sequence indices from this mailing list message from Tim Peters: https://mail.python.org/pipermail/tutor/2001-August/008098.html
from __future__ import nested_scopes
import sys
import bisect
class _Num:
def __init__(self, value, index):
self.value = value
self.i = index
def __lt__(self, other):
return self.value < other.value
# This implements the Karmarkar-Karp heuristic for partitioning a set
# in two, i.e. into two disjoint subsets s.t. their sums are
# approximately equal. It produces only one result, in O(N*log N)
# time. A remarkable property is that it loves large sets: in
# general, the more numbers you feed it, the better it does.
class Partition:
def __init__(self, nums):
self.nums = nums
sorted = [_Num(nums[i], i) for i in range(len(nums))]
sorted.sort()
self.sorted = sorted
def run(self):
sorted = self.sorted[:]
N = len(sorted)
connections = [[] for i in range(N)]
while len(sorted) > 1:
bigger = sorted.pop()
smaller = sorted.pop()
# Force these into different sets, by "drawing a
# line" connecting them.
i, j = bigger.i, smaller.i
connections[i].append(j)
connections[j].append(i)
diff = bigger.value - smaller.value
assert diff >= 0
bisect.insort(sorted, _Num(diff, i))
# Now sorted contains only 1 element x, and x.value is
# the difference between the subsets' sums.
# Theorem: The connections matrix represents a spanning tree
# on the set of index nodes, and any tree can be 2-colored.
# 2-color this one (with "colors" 0 and 1).
index2color = [None] * N
def color(i, c):
if index2color[i] is not None:
assert index2color[i] == c
return
index2color[i] = c
for j in connections[i]:
color(j, 1-c)
color(0, 0)
# Partition the indices by their colors.
subsets = [[], []]
for i in range(N):
subsets[index2color[i]].append(i)
return subsets
if not sys.argv:
print "error no arguments provided"
elif sys.argv[1]:
f = open(sys.argv[1], "r")
x = [int(line.strip()) for line in f]
N = 50
import math
p = Partition(x)
s, t = p.run()
sum1 = 0L
sum2 = 0L
for i in s:
sum1 += x[i]
for i in t:
sum2 += x[i]
print "Set 1:"
print s
print "Set 2:"
print t
print "Set 1 sum", repr(sum1)
print "Set 2 sum", repr(sum2)
print "difference", repr(abs(sum1 - sum2))
This gives the following output:
Set 1:
[0, 3, 5, 6, 9, 10, 12, 15, 17, 19, 21, 22, 24, 26, 28, 31, 32, 34, 36, 38, 41, 43, 45, 47, 48, 51, 53, 54, 56, 59, 61, 62, 65, 66, 68, 71]
Set 2:
[1, 2, 4, 7, 8, 11, 13, 14, 16, 18, 20, 23, 25, 27, 29, 30, 33, 35, 37, 39, 40, 42, 44, 46, 49, 50, 52, 55, 57, 58, 60, 63, 64, 67, 69, 70]
Set 1 sum 30309344369339288555041174435706422018348623853211009172L
Set 2 sum 30309344369339288555041174435706422018348623853211009172L
difference 0L
Which provides the indices of the proper subsets in a few seconds. Thanks everybody!
Assuming each entry in the matrix can either be 0 or 1, this problem seems to be in the same family as the Partition Problem which only has a pseudo-polynomial time algorithm. Let r be the number of rows in the matrix and c be the number of columns in the matrix. Then, encode each row to a c-digit number of base r+1. This is to ensure when adding each encoding, there is no need to carry, thus equivalent numbers in this base will equate to two sets of rows whose column sums are equivalent. So in your example, you would convert each row into a 4-digit number of base 9. This would yield the numbers (converted into base 10):
10109 => 73810
01019 => 8210
11009 => 81010
01109 => 9010
00119 => 1010
10109 => 73810
11009 => 81010
01019 => 8210
Although you probably couldn't use the pseudo-polynomial time algorithm with this method, you could use a simple heuristic with some decision trees to try to speed up the bruteforce. Using the numbers above, you could try to use the Karmarkar-Karp heuristic. Implemented below is the first step of algorithm in Python 3:
# Sorted (descending) => 810, 810, 738, 738, 90, 82, 82, 10
from queue import PriorityQueue
def karmarkar_karp_partition(arr):
pqueue = PriorityQueue()
for e in arr:
pqueue.put_nowait((-e, e))
for _ in range(len(arr)-1):
_, first = pqueue.get_nowait()
_, second = pqueue.get_nowait()
diff = first - second
pqueue.put_nowait((-diff, diff))
return pqueue.get_nowait()[1]
Here is the algorithm fully implemented. Note that this method is simply a heuristic and may fail to find the best partition.

Find incremental x amount of numbers in range

I don't even know how to explain this... I've been looking for algos but no luck.
I need a function that would return an array of incrementally bigger numbers (not sure what kind of curve) from two numbers that I'd pass as parameters.
Ex.:
$length = 20;
get_numbers(1, 1000, $length);
> 1, 2, 3, 5, 10, 20, 30, 50, 100, 200, 500... // let's say that these are 20 numbers that add up to 1000
Any idea how I could do this..? I guess I'm not smart enough to figure it out.
How about an exponential curve? Sample Python implementation:
begin = 1
end = 1000
diff = end - begin
length = 10
X = diff**(1.0/(length-1))
seq = []
for i in range(length):
seq.append(int(begin+X**i))
print seq
(note: ** is the Python operator for exponentiation. Other languages may or may not use ^ instead)
Result:
[2, 3, 5, 10, 22, 47, 100, 216, 464, 999]

Strange order of output of linq query

I have a massive of int values.
int[] numbers= {5, 4, 1, 3, 9, 8, 6, 7, 2, 0};
I divide this numbers on the modulo:
var numberGroups=from n in numbers group n by n%5 into g select new { Remainder=g.Key, Numbers=g};
and I have such strange order of results:
// 0, 4, 1, 3, 2
In my honour opinion the order should be:
//0, 4, 3, 1, 2
As the first number which can be divided by 5 is 5. // 0
The second number which can be divided by 5 is 9. // 4
The third number which can be divided by 5 is 8. // 3
The fourth number which can be divided by 5 is 6. // 1
The fifth number which can be divided by 5 is 7. // 2
I have such presentation of work group by. Where am I wrong?
Questions:
Why does linq change order?
Why do I have such strange order?
The groups are added in the order that they are needed. Which is the order of the input to the group clause. Given 5 is the first value, the 0 group is created first... this can been seen with the following code examples.
With this code:
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var numberGroups = from n in numbers group n by n % 5 into g select new { Remainder = g.Key, Numbers = g };
foreach(var ng in numberGroups)
{
Console.Write("{0} - ", ng.Remainder);
foreach (var v in ng.Numbers)
{
Console.Write(" {0}", v);
}
Console.WriteLine();
}
I get
0 - 5 0
4 - 4 9
1 - 1 6
3 - 3 8
2 - 7 2
which makes be believe there is no sort on the group operation, it is rather collecting the number in the order they are found, the first number 5, results in remainder 0 so the first output bucket is 0. We can test this by putting the 9 first and we get
4, 0, 1, 3, 2
To prove they add added to the buckets in the order they are processed, using these numbers
int[] numbers = { 15, 24, 31, 43, 59, 68, 76, 87, 92, 100 };
we get
0 - 15 100
4 - 24 59
1 - 31 76
3 - 43 68
2 - 87 92
15 is added first new slot 0, 24 is new slot 4, 31 net slot 1, 43 is new slot 3, 59 next 4 value, 68 is next 3 slot value, 76 next 1 slot, 87 new slot 2, 92 next 2, 100 next 0
to prove this more use:
int[] numbers = { 15, 25, 35, 45, 55, 65, 75, 85, 95, 105 };
we get
0 - 15 25 35 45 55 65 75 85 95 105
It might not be how you want it, but is makes 100% sense how/why it's happens.

Resources