How to check if a specific point is within a figure in a matrix? - algorithm

So I need a check function to see if a specific point in a matrix, say arr[3][4], is within a border, or a figure of characters. For clarification, imagine matrix char arr[10][10] below:
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 1 0 0
0 0 0 1 0 0 0 1 0 0
0 0 0 1 0 0 0 1 0 0
0 0 0 1 0 0 0 1 0 0
0 0 0 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
As you can see, the '1' characters form a square of side length 5. I would like a bool function to return that true for arr[5][5] (0-indexed) because it is within the figure, but false for arr[1][1] because it is not. If relevant, the total size of the matrix will always be a constant 100*100, no matter the size of the '1' figure within. Also, please note that the figure will not always be a perfect polygon like the square in the example.
I could not solve this problem because in my example above, clearly both points (arr[5][5] and arr[1][1]) have the same surrounding squares, and the space is large enough so that I cannot just check if the four directions of up, right, down, and left (yes, diagonals can be ignored here) is a '1' because the '0' inside would be next to other '0's.
EDIT: I also want to clarify according to some shortcomings of answers that the thickness of sides may vary. The shape very well could be:
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0 0 0
0 0 0 0 1 1 0 0 0 0
0 0 0 1 1 1 1 1 0 0
0 0 0 0 0 1 1 1 0 0
0 0 0 0 1 0 0 1 1 0
0 0 1 1 1 0 1 1 0 0
0 0 1 0 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
Therefore, counting whether the '1's on top of and to the left of the point is odd would not work.

So a flood fill would work, but it is quite heavy unless you want to know all the encased points. If you just want to check one point then you could do:
Count the number of ones in the vertical segment between points (x,0) and (x,y)
Count the number of ones in the horizontal segment between points (0,y) and (x,y)
If both are odd then you are inside.
Keep in mind that overlapping shapes or shapes with holes will not work with this algorithm.
So the function would look like this:
int inside(int x, int y)
{
int x_count = 0;
for(int i=0;i<x;i++)
if(matrix[y][i])
x_count++;
int y_count = 0;
for(int i=0;i<y;i++)
if(matrix[i][x])
y_count++;
return x_count%2 && y_count%2;
};
A full test program looks like:
#include <stdio.h>
int matrix1[10][10] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
int matrix2[10][10] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 1, 1, 1, 1, 0, 0},
{0, 1, 0, 0, 0, 0, 0, 1, 0, 0},
{0, 1, 0, 1, 1, 1, 1, 1, 0, 0},
{0, 1, 0, 1, 0, 0, 0, 0, 0, 0},
{0, 1, 0, 1, 1, 1, 1, 1, 0, 0},
{0, 1, 0, 0, 0, 0, 0, 1, 0, 0},
{0, 1, 0, 0, 0, 0, 0, 1, 0, 0},
{0, 1, 1, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
int inside(int matrix[10][10],int x, int y)
{
int x_count = 0;
for(int i=0;i<x;i++)
if(matrix[y][i])
x_count++;
int y_count = 0;
for(int i=0;i<y;i++)
if(matrix[i][x])
y_count++;
return x_count%2 && y_count%2;
};
int main()
{
printf("2,2 is %s matrix1\n",inside(matrix1,2,2)?"inside":"outside");
printf("5,5 is %s matrix1\n",inside(matrix1,5,5)?"inside":"outside");
printf("8,8 is %s matrix1\n",inside(matrix1,8,8)?"inside":"outside");
printf("3,3 is %s matrix2\n",inside(matrix2,3,3)?"inside":"outside");
printf("5,5 is %s matrix2\n",inside(matrix2,5,5)?"inside":"outside");
printf("7,7 is %s matrix2\n",inside(matrix2,7,7)?"inside":"outside");
return 0;
}
Try it online https://onlinegdb.com/UkkaA3vWZ

The standard algorithm only needs to scan left to right along the row you wish to check.
First, check if the element is a 1. If it is you are “inside or on the edge”.
Otherwise, scanning from 0 to x:
If you count an odd number of edges, you are inside.
If you count an even number of edges, you are outside.
You must be careful how you count edges. An edge is one where you have a 1 both above and below in the surrounding 8 elements. Otherwise you have not crossed an edge (you have passed a point).
Likewise, if you hit a run of 1s, you must still apply the above and below for both the left and right side of the run.
BTW, the only sure way to check is the flood-fill algorithm explained by Jonathan S.. Everything else can be tricked.

Here's a simple algorithm that'll do that:
Iterate over all elements at the edges of the matrix.
Change all 0 elements at the edge of the matrix to 2. (Leave any 1 elements intact.)
Within the entire matrix, whenever a 0 borders a 2, change that 0 to a 2 as well. Repeat this until there are no 0 elements left that are adjacent to a 2.
Any elements that are still 0 now are encased by 1 elements.
This is a flood fill starting at the edges of the matrix. It gives you all "encased" elements at once.

Related

Fast approximation of simple cases of relaxed bipartite dimension of graph problem

Given boolean matrix M, I need to find a set of submatrices A = {A1, ..., An} such that matrices in A contain all True values in matrix M and only them. Submatrices don't have to be continuous, i.e. each submatrix is defined by the two sets of indices {i1, ..., ik}, {j1, ..., jt} of M. (For example submatrix could be something like [{1, 2, 5}, {4, 7, 9, 13}] and it is all cells in intersection of these rows and columns.) Optionally submatrices can intersect if this results in better solution. The total number of submatrices n should be minimal.
Size of the matrix M can be up to 10^4 x 10^4, so I need an effective algorithm. I suppose that this problem may not have an effective exact algorithm, because it reminds me some NP-hard problems. If this is true, then any good and fast approximation is OK. We can also suggest that the amount of true values is not very big, i.e. < 1/10 of all values, but to not have accidental DOS in prod, the solution not using this fact is better.
I don't need any code, just a general idea of the algorithm and justification of its properties, if it's not obvious.
Background
We are calculating some expensive distance matrices for logistic applications. Points in these requests are often intersecting, so we are trying do develop some caching algorithm to not calculate parts of some requests. And to split big requests into smaller ones with only unknown submatrices. Additionally some distances in the matrix may be not needed for the algorithm. On the one hand the small amount of big groups calculates faster, on the other hand if we include a lot of "False" values, and our submatrices are unreasonably big, this can slow down the calculation. The exact criterion is intricate and the time complexity of "expensive" matrix requests is hard to estimate. As far as I know for square matrices it is something like C*n^2.5 with quite big C. So it's hard to formulate a good optimization criterion, but any ideas are welcome.
About data
True value in matrix means that the distance between these two points have never been calculated before. Most of the requests (but not all) are square matrices with the same points on both axes. So most of the M is expected to be almost symmetric. And also there is a simple case of several completely new points and the other distances are cached. I deal with this cases on preprocessing stage. All the other values can be quite random. If they are too random we can give up cache and calculate the full matrix M. But sometimes there are useful patterns. I think that because of the nature of the data it is expected to contain more big sumbatrices then random data. Mostly True values are occasional, but form submatrix patterns, that we need to find. But we cannot rely on this completely, because if algorithm gets too random matrix it should be able to at least detect it to not have too long and complex calculations.
Update
As stated in wikipedia this problem is called Bipartite Dimension of a graph and is known to be NP-hard. So we can reformulate it info finding fast relaxed approximations for the simple cases of the problem. We can allow some percentage of false values and we can adapt some simple, but mostly effective greedy heuristic.
I started working on the algorithm below before you provided the update.
Also, in doing so I realised that while one is looking for blocks of true values, the problem is not one of a block transformation, as you have also now updated.
The algorithm is as as follows:
count the trues in each row
for any row with the maximum count of trues, sort the columns in the
matrix so that the row's trues all move to the left
sort the matrix rows in descending order of congruent trues on the
left (there will now be an upper left rough triangle of congruent trues)
get the biggest rectangle of trues cornered at the upper left
store the row ids and column ids for that rectangle (this is a sub-matrix definition)
change the the sub-matrix's trues to falses
repeat from the top until the upper left triangle has no trues
This algorithm will produce a complete cover of the boolean matrix consisting of row-column intersection sub-matrices containing only true values.
I am not sure if allowing some falses in a sub-matrix will help. While it will allow bigger sub-matrices to be found and hence reduce the number of passes of the boolean matrix to find a cover, it will presumably take longer to find the biggest such sub-matrices because there will be more combinations to check. Also, I am not sure how one might stop falsey sub-matrices from overlapping. It might need the maintenance of a separate mask matrix rather than using the boolean matrix as its own mask, in order to ensure disjoint sub-matrices.
Below is a first cut implementation of the above algorithm in python.
I ran it on Windows 10 on a Intel Pentium N3700 # 1.60Ghz with 4GB RAM
As is, it will do, with randomly generated ~10% trues:
100 rows x 1000 columns < 7 secs
1000 rows x 100 columns < 6 secs
300 rows x 300 columns < 14 secs
3000 rows x 300 columns < 3 mins
300 rows x 3000 columns < 15 mins
1000 rows x 1000 columns < 8 mins
I have not tested it on approximately symmetric matrices, nor have I tested it on matrices with relatively large sub-matrices. It might perform well with relatively large sub-martrices, eg, in the extreme case, ie, the entire boolean matrix is true, only two passes of the algorithm loop are required.
One area I think there can be considerable optimisation is in the row sorting. The implementation below uses the in-built phython sort with a comparator function. A custom crafted sort function will probably do much better, and possibly especially so if it is a virtual sort similar to the column sorting.
If you can try it on some real data, ie, square, approximately symmetric matrix, with relatively large sub-matrices, it would be good to know how it goes.
Please advise if you would like to me to try some optimisation of the python. I presume to handle 10^4 x 10^4 boolean matrices it will need to be a lot faster.
from functools import cmp_to_key
booleanMatrix0 = [
( 0, 0, 0, 0, 1, 1 ),
( 0, 1, 1, 0, 1, 1 ),
( 0, 1, 0, 1, 0, 1 ),
( 1, 1, 1, 0, 0, 0 ),
( 0, 1, 1, 1, 0, 0 ),
( 1, 1, 0, 1, 0, 0 ),
( 0, 0, 0, 0, 0, 0 )
]
booleanMatrix1 = [
( 0, )
]
booleanMatrix2 = [
( 1, )
]
booleanMatrix3 = [
( 0, 0, 0, 0, 0, 0 ),
( 0, 0, 0, 0, 0, 0 ),
( 0, 0, 0, 0, 0, 0 ),
( 0, 0, 0, 0, 0, 0 ),
( 0, 0, 0, 0, 0, 0 ),
( 0, 0, 0, 0, 0, 0 ),
( 0, 0, 0, 0, 0, 0 )
]
booleanMatrix4 = [
( 1, 1, 1, 1, 1, 1 ),
( 1, 1, 1, 1, 1, 1 ),
( 1, 1, 1, 1, 1, 1 ),
( 1, 1, 1, 1, 1, 1 ),
( 1, 1, 1, 1, 1, 1 ),
( 1, 1, 1, 1, 1, 1 ),
( 1, 1, 1, 1, 1, 1 )
]
booleanMatrix14 = [
( 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0 ),
( 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 ),
( 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1 ),
( 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0 ),
( 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0 ),
( 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1 ),
( 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1 ),
( 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1 ),
( 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 ),
( 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1 ),
( 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1 ),
( 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
( 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ),
( 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 )
]
booleanMatrix15 = [
( 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
( 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
( 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
( 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
( 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
( 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
( 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
( 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ),
( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ),
( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ),
]
booleanMatrix16 = [
( 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ),
( 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ),
( 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ),
( 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ),
( 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ),
( 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ),
( 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ),
( 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ),
( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 ),
( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1 ),
( 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1 ),
]
import random
booleanMatrix17 = [
]
for r in range(11):
row = []
for c in range(21):
if random.randrange(5) == 1:
row.append(random.randrange(2))
else:
row.append(0)
booleanMatrix17.append(tuple(row))
booleanMatrix18 = [
]
for r in range(21):
row = []
for c in range(11):
if random.randrange(5) == 1:
row.append(random.randrange(2))
else:
row.append(0)
booleanMatrix18.append(tuple(row))
booleanMatrix5 = [
]
for r in range(50):
row = []
for c in range(200):
row.append(random.randrange(2))
booleanMatrix5.append(tuple(row))
booleanMatrix6 = [
]
for r in range(200):
row = []
for c in range(50):
row.append(random.randrange(2))
booleanMatrix6.append(tuple(row))
booleanMatrix7 = [
]
for r in range(100):
row = []
for c in range(100):
row.append(random.randrange(2))
booleanMatrix7.append(tuple(row))
booleanMatrix8 = [
]
for r in range(100):
row = []
for c in range(1000):
if random.randrange(5) == 1:
row.append(random.randrange(2))
else:
row.append(0)
booleanMatrix8.append(tuple(row))
booleanMatrix9 = [
]
for r in range(1000):
row = []
for c in range(100):
if random.randrange(5) == 1:
row.append(random.randrange(2))
else:
row.append(0)
booleanMatrix9.append(tuple(row))
booleanMatrix10 = [
]
for r in range(317):
row = []
for c in range(316):
if random.randrange(5) == 1:
row.append(random.randrange(2))
else:
row.append(0)
booleanMatrix10.append(tuple(row))
booleanMatrix11 = [
]
for r in range(3162):
row = []
for c in range(316):
if random.randrange(5) == 1:
row.append(random.randrange(2))
else:
row.append(0)
booleanMatrix11.append(tuple(row))
booleanMatrix12 = [
]
for r in range(316):
row = []
for c in range(3162):
if random.randrange(5) == 1:
row.append(random.randrange(2))
else:
row.append(0)
booleanMatrix12.append(tuple(row))
booleanMatrix13 = [
]
for r in range(1000):
row = []
for c in range(1000):
if random.randrange(5) == 1:
row.append(random.randrange(2))
else:
row.append(0)
booleanMatrix13.append(tuple(row))
booleanMatrices = [ booleanMatrix0, booleanMatrix1, booleanMatrix2, booleanMatrix3, booleanMatrix4, booleanMatrix14, booleanMatrix15, booleanMatrix16, booleanMatrix17, booleanMatrix18, booleanMatrix6, booleanMatrix5, booleanMatrix7, booleanMatrix8, booleanMatrix9, booleanMatrix10, booleanMatrix11, booleanMatrix12, booleanMatrix13 ]
def printMatrix(matrix, colOrder):
for r in range(rows):
row = ""
for c in range(cols):
row += str(matrix[r][0][colOrder[c]])
print(row)
print()
def rowUp(matrix):
rowCount = []
maxRow = [ 0, 0 ]
for r in range(rows):
rowCount.append([ r, sum(matrix[r][0]) ])
if rowCount[-1][1] > maxRow[1]:
maxRow = rowCount[-1]
return rowCount, maxRow
def colSort(matrix):
# For a row with the highest number of trues, sort the true columns to the left
newColOrder = []
otherCols = []
for c in range(cols):
if matrix[maxRow[0]][0][colOrder[c]]:
newColOrder.append(colOrder[c])
else:
otherCols.append(colOrder[c])
newColOrder += otherCols
return newColOrder
def sorter(a, b):
# Sort rows according to leading trues
length = len(a)
c = 0
while c < length:
if a[0][colOrder[c]] == 1 and b[0][colOrder[c]] == 0:
return -1
if b[0][colOrder[c]] == 1 and a[0][colOrder[c]] == 0:
return 1
c += 1
return 0
def allTrues(rdx, cdx, matrix):
count = 0
for r in range(rdx+1):
for c in range(cdx+1):
if matrix[r][0][colOrder[c]]:
count += 1
else:
return
return rdx, cdx, count
def getBiggestField(matrix):
# Starting at (0, 0) find biggest rectangular field of 1s
biggestField = (None, None, 0)
cStop = cols
for r in range(rows):
for c in range(cStop):
rtn = allTrues(r, c, matrix)
if rtn:
if rtn[2] > biggestField[2]:
biggestField = rtn
else:
cStop = c
break;
if cStop == 0:
break
return biggestField
def mask(matrix):
maskMatrix = []
for r in range(rows):
row = []
for c in range(cols):
row.append(matrix[r][0][c])
maskMatrix.append([ row, matrix[r][1] ])
maskRows = []
for r in range(biggestField[0]+1):
maskRows.append(maskMatrix[r][1])
for c in range(biggestField[1]+1):
maskMatrix[r][0][colOrder[c]] = 0
maskCols= []
for c in range(biggestField[1]+1):
maskCols.append(colOrder[c])
return maskMatrix, maskRows, maskCols
# Add a row id to each row to keep track of rearranged rows
rowIdedMatrices = []
for matrix in booleanMatrices:
rowIdedMatrix = []
for r in range(len(matrix)):
rowIdedMatrix.append((matrix[r], r))
rowIdedMatrices.append(rowIdedMatrix)
import time
for matrix in rowIdedMatrices:
rows = len(matrix)
cols = len(matrix[0][0])
colOrder = []
for c in range(cols):
colOrder.append(c)
subMatrices = []
startTime = time.thread_time()
loopStart = time.thread_time()
loop = 1
rowCount, maxRow = rowUp(matrix)
ones = 0
for row in rowCount:
ones += row[1]
print( "_________________________\n", "Rows", rows, "Columns", cols, "Ones", str(int(ones * 10000 / rows / cols) / 100) +"%")
colOrder = colSort(matrix)
matrix.sort(key=cmp_to_key(sorter))
biggestField = getBiggestField(matrix)
if biggestField[2] > 0:
maskMatrix, maskRows, maskCols = mask(matrix)
subMatrices.append(( maskRows, maskCols ))
while biggestField[2] > 0:
loop += 1
rowCount, maxRow = rowUp(maskMatrix)
colOrder = colSort(maskMatrix)
maskMatrix.sort(key=cmp_to_key(sorter))
biggestField = getBiggestField(maskMatrix)
if biggestField[2] > 0:
maskMatrix, maskRows, maskCols = mask(maskMatrix)
subMatrices.append(( maskRows, maskCols) )
if loop % 100 == 0:
print(loop, time.thread_time() - loopStart)
loopStart = time.thread_time()
endTime = time.thread_time()
print("Sub-matrices:", len(subMatrices), endTime - startTime)
for sm in subMatrices:
print(sm)
print()
input("Next matrix")
LOOP over true values
Can you grow the submatrix containing the true value in any direction
( i.e can you go from
t
to
tt
tt
)
Keep growing for as long as possible
Set all cells in M that are in the new submatrix to false
Repeat until every cell in M is false.
Here is a simple example of how it works
The top picture shows the large Matrix M containing a few true values
The bottom rows show the first few iteration, with the blus submatric growing as it finds more adjacent cells with true values. In this case I have stopped because it cannot grow any durther without including false cells. If a few cells in a submatrix can be false, then you could continue a bit further.
Let's say M is an s by t matrix. The trivial (but possibly useful) solution is just to take all the non-empty columns (or rows) as your submatrices. This will result in at most min(s,t) submatrices.

How does this Integer pool code work

I've been trying to understand how this integer pool works. It is a lot of bit fiddling stuff I can't wrap my head around. I'm assuming there is a concept I'm missing with the m2id array and how it is or'ed with index 'n' that I don't know and would clear up a lot of my confusion. Are there are any general concepts/CS-theory that explain this seemingly-looking-simple code. I've put comments in the code to try and state my current understanding and where I am totally confused.
// Copyright 2009 The Go9p Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//Original source: https://github.com/rminnich/go9p/blob/master/clnt_pool.go
package go9p
import "sync"
var m2id = [...]uint8{ // I think this is where the magic is.
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 5,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 6,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 5,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 7,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 5,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 6,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 5,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 4,
0, 1, 0, 2, 0, 1, 0, 3,
0, 1, 0, 2, 0, 1, 0, 0,
}
type pool struct {
sync.Mutex
need int
nchan chan uint32
maxid uint32
imap []byte
}
func newPool(maxid uint32) *pool {
p := new(pool)
p.maxid = maxid
p.nchan = make(chan uint32)
return p
}
func (p *pool) getId() uint32 {
var n uint32 = 0
var ret uint32
p.Lock()
for n = 0; n < uint32(len(p.imap)); n++ {
// it looks like every 0...n position of imap will be incremented to 255.
if p.imap[n] != 0xFF {
break
}
}
if int(n) >= len(p.imap) {
// This seems to be just growing the imap slice as needed.
// I don't quite understand the constant of '8' here.
m := uint32(len(p.imap) + 32)
if uint32(m*8) > p.maxid {
m = p.maxid/8 + 1
}
b := make([]byte, m)
copy(b, p.imap)
p.imap = b
}
if n >= uint32(len(p.imap)) {
// If you get here the I'm assuming all the ID's are used up and putId will return you the next released ID.
p.need++
p.Unlock()
ret = <-p.nchan
} else {
// This part I'm having a hard time grasping.
// It seems that each index of imap is incremented
// from 0 to 255 and magically or'd with ret to increment to the next number?
ret = uint32(m2id[p.imap[n]])
p.imap[n] |= 1 << ret
ret += n * 8
p.Unlock()
}
return ret
}
func (p *pool) putId(id uint32) {
p.Lock()
if p.need > 0 {
p.nchan <- id
p.need--
p.Unlock()
return
}
// This doesn't play well with what I thought was going on. I though that.
// I was thinking that imap[0] would always somehow magically return all the
// values from 0 to 255 and imap[1] would return 256 += 255 and so on.
// How does this work?
p.imap[id/8] &= ^(1 << (id % 8))
p.Unlock()
}
Optimization often leads to obscurity. Start with the basic concept. The pool of available Ids is represented by the underlying bit array of a slice of bytes. Id 19 is represented by left-to-right byte 2 (19 / 8) and right-to-left bit 3 (19 % 8).
Here's a simple implementation, ignoring details like locking and growing the bit array.
package main
import "fmt"
// The Id pool is represented by the underlying bit array of a slice of bytes.
var idPool = make([]byte, 4)
// Get the next available Id from the pool.
func getId() int {
// Get next available byte
for i := 0; i < len(idPool); i++ {
b := idPool[i]
if b != 0xFF {
// Get next available bit in the byte
for j := 0; j < 8; j++ {
if b&(1<<uint(j)) == 0 {
// Mark Id bit as unavailable.
idPool[i] |= 1 << uint(j)
// Return Id.
return 8*i + j
}
}
}
}
panic("Insufficient Ids")
}
// Put the Id back in the pool.
func putId(id int) {
if 0 > id || id >= 8*len(idPool) {
panic("Invalid Id")
}
i := id / 8
j := id % 8
// Mark Id bit as available.
idPool[i] &^= 1 << uint(j)
}
func main() {
for i := 0; i < 16; i++ {
getId()
}
fmt.Printf("%x\n", idPool)
for i := 10; i < 12; i++ {
putId(i)
}
fmt.Printf("%x\n", idPool)
fmt.Println(getId())
fmt.Printf("%x\n", idPool)
}
Output:
ffff0000
fff30000
10
fff70000
We can optimize this loop
// Get next available bit in the byte
for j := 0; j < 8; j++ {
if b&(1<<uint(j)) == 0 {
// Mark Id bit as unavailable.
idPool[i] |= 1 << uint(j)
// Return Id.
return 8*i + j
}
}
by replacing it with a table (m2id) lookup for the bit shift value.
// Get next available bit in the byte
j := int(m2id[idPool[i]])
// Mark Id bit as unavailable.
idPool[i] |= 1 << uint(j)
// Return Id.
return 8*i + j
The m2idInit() function shows how the m2id table bit shift values are calculated.
func m2idInit() (m2id [256]uint8) {
// For all byte values.
for i := uint(0); i < 256; i++ {
// Find an unused id
for j := uint(0); j < 8; j++ {
if i&(1<<j) == 0 {
// Bit shift value
m2id[i] = uint8(j)
break
}
}
}
return m2id
}
For example,
package main
import "fmt"
// The Id pool is represented by the underlying bit array of a slice of bytes.
var idPool = make([]byte, 4)
// Get the next available Id from the pool.
func getId() int {
// Get next available byte
for i := 0; i < len(idPool); i++ {
b := idPool[i]
if b != 0xFF {
// Get next available bit in the byte
j := int(m2id[idPool[i]])
// Mark Id bit as unavailable.
idPool[i] |= 1 << uint(j)
// Return Id.
return 8*i + j
}
}
panic("Insufficient Ids")
}
// Put the Id back in the pool.
func putId(id int) {
if 0 > id || id >= 8*len(idPool) {
panic("Invalid Id")
}
i := id / 8
j := id % 8
// Mark Id bit as available.
idPool[i] &^= 1 << uint(j)
}
var m2id = m2idInit()
func m2idInit() (m2id [256]uint8) {
// For all byte values.
for i := uint(0); i < 256; i++ {
// Find an unused id
for j := uint(0); j < 8; j++ {
if i&(1<<j) == 0 {
// Bit shift value
m2id[i] = uint8(j)
break
}
}
}
return m2id
}
func main() {
for i := 0; i < 16; i++ {
getId()
}
fmt.Printf("%x\n", idPool)
for i := 10; i < 12; i++ {
putId(i)
}
fmt.Printf("%x\n", idPool)
fmt.Println(getId())
fmt.Printf("%x\n", idPool)
fmt.Println()
fmt.Println(m2id)
}
Output:
ffff0000
fff30000
10
fff70000
[0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 4
0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 5
0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 4
0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 6
0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 4
0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 5
0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 4
0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 7
0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 4
0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 5
0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 4
0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 6
0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 4
0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 5
0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 4
0 1 0 2 0 1 0 3
0 1 0 2 0 1 0 0]
There is no magic.
References:
Bit manipulation
The Go Programming Language Specification, Arithmetic operators

Animate map movement on bash

I have a 2d map which is an array of arrays:
map = [[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 1],
[0, 0, 0, 0, 0]]
I also have a list with moves:
moves = [[0,0], [0, 1], [1, 1]]
I want to print the movement on console (but I want every time to overwrite previous output, like this)
So the expected output should be something like this
* 0 0 0 0 0 * 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 * 0 0 0
0 1 1 1 1 --> 0 1 1 1 1 --> 0 1 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
I have tried some things but I can't get close to my desired output.
To clear the screen and wait for ENTER, try this:
map = [[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 1],
[0, 0, 0, 0, 0]]
loc = [0,0]
moves = [[0,0], [0, 1], [1, 1]]
def display_map(map,loc)
system 'clear'
loc.first.times { puts }
map.each { |row| print ' '*loc.last; p row }
end
moves.each do |x,y|
loc[0] += x
loc[1] += y
display_map(map,loc)
gets
end
This works on a Mac. For other OS's you may have to replace system 'clear' with system 'cls'.
[Edit: I see I misunderstood the question. I think this is what you want:
moves.each do |x,y|
system 'clear'
nrows.times do |i|
ncols.times do |j|
print (i==x && j==y) ? '*' : map[i][j]
print ' ' if j < ncols-1
end
puts
end
gets
end
You can use ANSI terminal escape codes.
Example:
# Save initial cursor position
puts "\033[s"
puts <<EOF
* 0 0 0 0
0 0 0 0 0
0 1 1 1 1
0 0 0 0 0
EOF
sleep 1
# Restore initial cursor position
puts "\033[u"
puts <<EOF
0 * 0 0 0
0 0 0 0 0
0 1 1 1 1
0 0 0 0 0
EOF
sleep 1
# Restore initial cursor position
puts "\033[u"
puts <<EOF
0 0 0 0 0
0 * 0 0 0
0 1 1 1 1
0 0 0 0 0
EOF
Following #Cary_Swoveland's solution with clearing the console, I manage to do it like this:
map = [[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 1],
[0, 0, 0, 0, 0]]
moves = [[0,0], [0, 1], [1, 1]]
def display_map(map,loc)
system "clear" or system "cls"
# loc.first.times { puts }
map.each { |row| p row }
end
moves.each do |x,y|
map[x][y] = 8
display_map(m,loc)
map[x][y] = 0
gets
end

Square matrix from file in a two-dimensional array

I'm a newbie in ruby, just began to study. Can't find a solution to read from a file a square matrix in a two-dimensional array.
file graph.txt:
0 3 0 0 10 0 0
0 0 9 0 0 0 0
0 0 0 3 0 0 15
0 0 0 0 0 0 10
0 0 0 0 0 8 0
0 0 0 0 0 0 0
0 0 0 0 15 0 0
My code:
n=7
Arr = Array.new(n).map!{Array.new(n)}
text = ''
tx = File.readlines("graph.txt")
text = tx.join
i=0
text.each_line do |line|
Arr[i] = line.split(/\n/)
i+=1
end
p Arr
result:
[["0 3 0 0 10 0 0"], ["0 0 9 0 0 0 0"], ["0 0 0 3 0 0 15"], ["0 0 0 0 0 0 10"], ["0 0 0 0 0 8 0"], ["0 0 0 0 0 0 0"], ["0 0 0 0 15 0 0"]]
need result:
[[0, 3, 0, 0, 10, 0, 0], [0, 0, 9, 0, 0, 0, 0], [0, 0, 0, 3, 0, 0, 15], [0, 0, 0, 0, 0, 0, 10], [0, 0, 0, 0, 0, 8, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 15, 0, 0]]
# Replace DATA.each_line with IO.readlines('graph.txt') to use the file as a data source
matrix = DATA.each_line.map { |line| line.split.map(&:to_i) }
puts matrix.inspect
__END__
0 3 0 0 10 0 0
0 0 9 0 0 0 0
0 0 0 3 0 0 15
0 0 0 0 0 0 10
0 0 0 0 0 8 0
0 0 0 0 0 0 0
0 0 0 0 15 0 0
# => [[0, 3, 0, 0, 10, 0, 0], [0, 0, 9, 0, 0, 0, 0], [0, 0, 0, 3, 0, 0, 15], [0, 0, 0, 0, 0, 0, 10], [0, 0, 0, 0, 0, 8, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 15, 0, 0]]

How to generate a Binary Tree from a Labyrinth?

A matrix of 150x150 size will describe our labyrinth, so for example if the matrix were only 10x10 we would have something like this:
1 1 1 1 1 1 1 1 1 1
1 0 0 0 0 0 0 1 0 0<-F
1 0 1 1 0 1 0 1 0 1
1 1 1 1 0 1 0 0 0 1
1 1 1 1 0 1 1 1 1 1
1 0 0 0 0 1 1 1 1 1
1 0 1 1 0 1 1 1 1 1
1 0 1 0 0 0 0 1 1 1
S->0 0 1 1 1 1 0 1 1 1
1 1 1 1 1 1 1 1 1 1
Where S marks the starting point and F the exit of the labyrinth.
The purpose of this program is to generate a Binary Tree that will describe all the paths we traveled while trying to find the exit.
How would you acomplish that? I'm really lost this time, I don't really know where to start that's why I'm not posting anything I've tried but if you could please give me a direction I would be really really grateful.
John Smith.
you might want to try backtracking
here's a complete example of how to solve this problem... however: it can't operate on a maze with "islands" in it as it would be necessary to aditionally track where you have been already. but i think you can figure out how to do this as well...
the output should be:
right, up, up, up, right, right, right, up, up, up, up, right, right, down, down, right, right, up, up, right, finish!
import java.awt.Point;
public class Maze {
enum Direction {
up, right, down, left
}
static Direction[] dirs = Direction.values();
int[][] maze = { { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, { 1, 0, 1, 1, 0, 1, 0, 1, 0, 1 },
{ 1, 1, 1, 1, 0, 1, 0, 0, 0, 1 }, { 1, 1, 1, 1, 0, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 1, 1, 1, 1, 1 }, { 1, 0, 1, 1, 0, 1, 1, 1, 1, 1 },
{ 1, 0, 1, 0, 0, 0, 0, 1, 1, 1 }, { 0, 0, 1, 1, 1, 1, 0, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } };
Point start = new Point(0, 8);
Point finish = new Point(9, 1);
Point go(Direction dir, Point from) {
Point result = new Point(from);
switch (dir) {
case up:
if ((from.y == 0) || (maze[from.y - 1][from.x] != 0))
return null;
result.translate(0, -1);
break;
case right:
if ((from.x == maze[0].length) || (maze[from.y][from.x + 1] != 0))
return null;
result.translate(1, 0);
break;
case down:
if ((from.y == maze.length) || (maze[from.y + 1][from.x] != 0))
return null;
result.translate(0, 1);
break;
case left:
if ((from.x == 0) || (maze[from.y][from.x - 1] != 0))
return null;
result.translate(-1, 0);
break;
}
return result;
}
String tryToGo(Direction dir, Point from) {
String result;
Point newPosition = go(dir, from);
if (newPosition == null)
return null;
else if (newPosition.equals(start))
return null;
else if (newPosition.equals(finish))
return "finish!";
else {
for (Direction newDir : dirs) {
switch (newDir) {
case up:
if (dir == Direction.down)
continue;
break;
case down:
if (dir == Direction.up)
continue;
break;
case left:
if (dir == Direction.right)
continue;
break;
case right:
if (dir == Direction.left)
continue;
break;
}
result = tryToGo(newDir, newPosition);
if (result == null)
continue;
else
return newDir + ", " + result;
}
return null;
}
}
public static void main(String[] args) {
Maze maze = new Maze();
System.out.println("right" + maze.tryToGo(Direction.right, maze.start));
}
}

Resources