Algorithms: How to highlight image difference using rectangles? - algorithm

I need to compare two images and create rectangles of the difference. I can build a difference matrix like this:
0 0 0 0 0 0 0 0
0 0 0 0 0 1 1 1
0 0 0 1 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 1 0 0
0 0 0 0 1 1 1 0
0 0 0 0 1 1 0 0
where 1 is diff pixel. I need to find the way to create rectangles for areas of image diff. In my example, I have three areas to highlight.
# # #
# 1 #
# # #
# # # #
# 1 1 1
# # # #
# # # # #
# 1 0 0 #
# 0 1 0 #
# 1 1 1 #
# 1 1 0 #
So I'm looking for an algorithm to do that in a convenient way.

I'm assuming that the problem is as follows. Given a 0/1-matrix, cover the regions containing 1s with disjoint rectnagles (i.e. the rectangles must no intersect). In particular, non-rectangular shapes---for example, an L-shaped domino---are disallowed.
Here's an idea for an algorithm:
start at the origin, at index (0,0), and expand out until the expanded region contains a single rectangle of 1s that you cannot enlarge by moving to adjacent cells in either direction.
add that rectangle to the collection, and remove the processed region;
recurse on the remaining cells.
The running time should be linear in the number of cells; however, depending on whether there are additional specifications on the type of the output, you may need to change the first step.
I like the problem a lot. Notice that many different solutions may exist for a problem instance. A natural variation would be to require a cover comprised of as few rectangles as possible (i.e. a minimal cover); in this case, too, there may exist many different solutions. (The counting version of the problem looks interesting from the complexity-theoretic viewpoint.)

You could do a two-step algorithm: First, you find 8-connected components in your image, then you calculate the bounding box for each of the component.
This approach may lead to overlapping rectangles (imagine two nearby "L"-shapes), which you could solve by either merging the overlapping rectangles or by zeroing out non-connected components from each rectangle (so that you can sum all the rectangles and reconstruct the difference image appropriately).
If you go with the second choice, you can get the rectangles in Matlab as follows:
%# each element of the struct array rectangles contains a field
%# .Image, which is the rectangle, and
%# .BoundingBox, which is the coordinates of the rectangle.
rectangles = regionprops(differenceImage,'Image','BoundingBox');

Below is some JS demonstration code to find the rectangles, starting each time at the next remaining non-empty cell and exploring all paths in a recursive way. Cells are cleared as they are visited.
This is pretty close to what the the 'fill' tool is doing in MS Paint and the like. More precisely, this is a flood fill algorithm, as mentioned by j-random-hacker in the comments.
This code will find the inner bounds of the rectangles. It'd need to be slightly updated if you want the outer bounds instead.
var W = 8, H = 8;
var matrix = [
// 0 1 2 3 4 5 6 7
[ 0, 0, 0, 0, 0, 0, 0, 0 ], // 0
[ 0, 0, 0, 0, 0, 1, 1, 1 ], // 1
[ 0, 0, 0, 1, 0, 0, 0, 0 ], // 2
[ 0, 0, 0, 0, 0, 0, 0, 0 ], // 3
[ 0, 0, 0, 0, 1, 0, 0, 0 ], // 4
[ 0, 0, 0, 0, 0, 1, 0, 0 ], // 5
[ 0, 0, 0, 0, 1, 1, 1, 0 ], // 6
[ 0, 0, 0, 0, 1, 1, 0, 0 ] // 7
];
var dir = [
[ -1, -1 ], [ 0, -1 ], [ 1, -1 ], [ 1, 0 ], [ 1, 1 ], [ 0, 1 ], [ -1, 1 ], [ -1, 0 ]
];
var x, y, rect;
for(y = 0; y < H; y++) {
for(x = 0; x < W; x++) {
if(diffAt(x, y)) {
rect = { x0:x, y0:y, x1:x, y1:y };
recurse(x, y, rect);
console.log(
'Found rectangle: ' +
'(' + rect.x0 + ',' + rect.y0 + ') -> ' +
'(' + rect.x1 + ',' + rect.y1 + ')'
);
}
}
}
function recurse(x, y, rect) {
rect.x0 = Math.min(rect.x0, x);
rect.y0 = Math.min(rect.y0, y);
rect.x1 = Math.max(rect.x1, x);
rect.y1 = Math.max(rect.y1, y);
matrix[y][x] = 0;
for(var d = 0; d < 8; d++) {
if(diffAt(x + dir[d][0], y + dir[d][1])) {
recurse(x + dir[d][0], y + dir[d][1], rect);
}
}
}
function diffAt(x, y) {
return x < 0 || x >= W || y < 0 || y >= H ? 0 : matrix[y][x];
}

Related

Matrix construction algorithm

I have a small question related to optimization. I have a json, in which an array of elements describing the path, i.e. from the first one can rise to the second, from it to the right to the third, and so on.
[
{
"number": 1,
"upperLevel": 2
},
{
"number": 2,
"rightLevel": 3,
"leftLevel": 4
},
{
"number": 3,
"lowerLevel": 6
},
{
"number": 4,
"upperLevel": 5
},
{
"number": 5
},
{
"number": 6,
"lowerLevel": 7
},
{
"number": 7
}
]
These paths can be different.
I need to build a matrix out of this.
5 0 0
4 2 3
0 1 6
0 0 7
The problem is that the elements do not have an exact position where they are on the matrix, and it is not clear where to initially place the first element, so as not to go beyond the size of the array later. To avoid this, I specify the size twice as large as the number of elements, and put the first one in the center, so that wherever the path goes, it does not go beyond the boundaries.This is what my algorithm looks like.
val matrix = buildMatrix(
levels = data.map { it.number to it }.toMap(),
matrix = MutableList(data.size*2) { MutableList<Level?>(data.size*2) { null } },
obj = data.first(),
processedLevel = mutableSetOf(),
x = data.size,
y = data.size
)
matrix.reverse()
There is a bit of recursion here.
private fun buildMatrix(
levels: Map<Int, Level>,
matrix: MutableList<MutableList<Level?>>,
obj: Level,
processedLevel: MutableSet<Int>,
x: Int,
y: Int
): MutableList<MutableList<Level?>>{
if(processedLevel.contains(obj.number)) return matrix
matrix[y][x] = obj
processedLevel.add(obj.number)
if(obj.upperLevel != null) levels[obj.upperLevel]?.let { buildMatrix(levels, matrix, it, processedLevel, x, y+1) }
if(obj.rightLevel != null) levels[obj.rightLevel]?.let { buildMatrix(levels, matrix, it, processedLevel, x+1, y) }
if(obj.lowerLevel != null) levels[obj.lowerLevel]?.let { buildMatrix(levels, matrix, it, processedLevel, x, y-1) }
if(obj.leftLevel != null) levels[obj.leftLevel]?.let { buildMatrix(levels, matrix, it, processedLevel, x-1, y) }
return matrix
}
But in this case, there are many empty values. And the more elements there are in json, the more empty spaces there will be.
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
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 5 0 0 0 0 0 0 0
0 0 0 0 0 0 4 2 3 0 0 0 0 0
0 0 0 0 0 0 0 1 6 0 0 0 0 0
0 0 0 0 0 0 0 0 7 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 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 0 0 0 0 0
If someone needs it, then I display the matrix like this. println(matrix.reversed().joinToString("\n") { row -> row.map { it?.number ?: 0 }.joinToString(" ") })
To remove these blank lines, I wrote a function like this.
private fun removeEmptyLines(matrix: MutableList<MutableList<Level?>>): MutableList<MutableList<Level?>>{
matrix.removeIf { row ->
row.all { it == null }
}
val indexForRemove = mutableSetOf<Int>()
matrix.first().forEachIndexed { index, _ ->
if(matrix.all { it[index] == null }) indexForRemove.add(index)
}
indexForRemove.sortedDescending().forEach { index ->
matrix.forEach { it.removeAt(index) }
}
return matrix
}
And as a result, the result was obtained, which I described above, but it seems to me that this is not an ideal option, maybe someone has ideas on how this can be optimized? Thank you in advance.
You could first collect the coordinates of each position in a Map. That will allow you to start at any arbitrary coordinate and have negative coordinates. Then use the Map to find the minimal sized 2D array needed and populate it.
fun buildMatrix(data: List<Level>): List<List<Level?>> {
class Point(val x: Int, val y: Int)
val remaining = data.toMutableList()
val points = mutableMapOf(1 to Point(0, 0))
while (remaining.isNotEmpty()) {
val level = remaining.firstOrNull { it.number in points } ?: error("Invalid input data")
remaining.remove(level)
val point = points[level.number]!!
level.upperLevel?.let { points[it.number] = Point(point.x, point.y + 1) }
level.lowerLevel?.let { points[it.number] = Point(point.x, point.y - 1) }
level.leftLevel?.let { points[it.number] = Point(point.x - 1, point.y) }
level.rightLevel?.let { points[it.number] = Point(point.x + 1, point.y) }
}
val xOffset = -points.values.minBy(Point::x)!!.x
val yOffset = -points.values.minBy(Point::y)!!.y
val xMax = points.values.maxBy(Point::x)!!.x + xOffset
val yMax = points.values.maxBy(Point::y)!!.y + yOffset
val matrix = Array(yMax + 1) { arrayOfNulls<Level>(xMax + 1) }
for ((number, point) in points) {
matrix[point.y + yOffset][point.x + xOffset] = data.find { it.number == number }
}
return matrix.map(Array<Level?>::toList)
}
First of All change your json input to cover two type of information:
upperLevel, leftLevel.
So your input will change to this :
[
{
"number": 1,
"upperLevel": 2
},
{
"number": 2,
"leftLevel": 4
},
{
"number": 3,
"leftLevel": 2
},
{
"number": 4,
"upperLevel": 5
},
{
"number": 5
},
{
"number": 6,
"upperLevel": 3
},
{
"number": 7
"upperLevel": 6
}
]
Now every input has a tuple of index based on x(row) and y(column). When an element has an upperLevel the X coordinate will increase by 1 and y coordinate is equal to upper level. For example i1=(x2+1,y2) because the 1 is below the 2.
So final indexes will be as below:
i1=(x2+1,y2)
i2=(x4,y4+1)
i3=(x2,y2+1)
i4=(x5+1,y5)
i5=(0,0)
i6=(x3+1,y3)
i7=(x6+1,y6)
Since number 5 has no information we set it as (0,0) which is stop point for recursive function.
Now replace x5=0,y5=0 in the indexes and you will have:
i1=(x2+1,y2) = (2,1)
i2=(x4,y4+1) = (1,1)
i3=(x2,y2+1) = (1,2)
i4=(x5+1,y5) = (1,0)
i5=(0,0)
i6=(x3+1,y3) = (2,2)
i7=(x6+1,y6) = (3,2)
Please feel free to ask questions in comment.

Solve and show the ordered edges in shortest path problem by MiniZinc

I use MiniZinc compute a problem of optimization of shortest path problem based on hakank's model in http://www.hakank.org/minizinc
I input the distance matrix to a symmetric one such that the graph is bidirectional.
int: start = 2; % start node
int: end = 1; % end node
int: M = 999; % large number
array[1..n, 1..n] of 0..M: d = array2d(1..n,1..n,
[ M, 11, 8, 3, 8, 10, 2, 4, % 1-X
11, M, 3, 5, 1, 4, 8, 3, % 2-X
8, 3, M, 5, 7, 7, 11, 4, % 3-X
3, 5, 5, M, 9, 3, 10, 15, % 4-X
8, 6, 7, 9, M, 7, 12, 1, % 5-X
10, 4, 7, 3, 7, M, 6, 9, % 6-X
2, 8, 8, 10, 12, 9, M, 14, % 7-X
4, 3, 4, 15, 1, 9, 14, M % 8-X
]
);
% objective to minimize
var int: total_cost = sum(i in 1..n, j in 1..n where d[i,j] < M) ( d[i,j]*x[i,j] );
array[1..n] of var -1..1: rhs; % indicating start/end nodes
array[1..n, 1..n] of var 0..1: x; % the resulting connection matrix
array[1..n, 1..n] of var 0..n*n: y; % output node matrix
array[1..n] of var 0..1: outFlow; % out flow array
array[1..n] of var 0..1: inFlow; % in flow array
constraint
% set rhs for start/end nodes
forall(i in 1..n) (
if i = start then
rhs[i] = 1
elseif i = end then
rhs[i] = -1
else
rhs[i] = 0
endif
)
/\ % assert that all x values is >= 0
forall(i in 1..n, j in 1..n where d[i,j] < M) (
x[i,j] >= 0 /\ y[i,j] >= 0
)
/\ % calculate out flow
forall(i in 1..n) (
outFlow[i] = sum(j in 1..n where d[i,j] < M) (x[i,j])
)
/\ % calculate in flow
forall(j in 1..n) (
inFlow[j] = sum(i in 1..n where d[i,j] < M) (x[i,j])
)
/\ % outflow = inflow
forall(i in 1..n) (outFlow[i] - inFlow[i] = rhs[i])
/\ % do not loops
forall(i in 1..n) (
x[i,i] = 0
)
/\ % sanity: there can be no connection in x if there is not
% connection in d
forall(i,j in 1..n) (
if d[i,j] = M then
x[i,j] = 0
else
true
endif
)
;
solve minimize total_cost;
output [
if i = 1 /\ j = 1 then
"total_cost: " ++ show(total_cost) ++ "\n" ++
"inFlow: " ++ show(inFlow) ++ "\n" ++ "outFlow: " ++ show(outFlow) ++ "\n" ++
" 1 2 3 4 5 6 7 8\n"
else "" endif ++
if j = 1 then show(i) ++ " : " else "" endif ++
show_int(4,x[i,j]) ++ if j = n then "\n" else " " endif
| i in 1..n, j in 1..n
];
The solution gives an output matrix that indicates which edge of a graph is participating in the solution; however, the solution is directionless. I cannot tell the order of edge to take on a particular solution. In the above example, the shortest path from node 2 to node 1 gives the following solution
total_cost: 6
inFlow: [1, 0, 0, 0, 1, 0, 0, 1]
outFlow: [0, 1, 0, 0, 1, 0, 0, 1]
1 2 3 4 5 6 7 8
1 : 0 0 0 0 0 0 0 0
2 : 0 0 0 0 1 0 0 0
3 : 0 0 0 0 0 0 0 0
4 : 0 0 0 0 0 0 0 0
5 : 0 0 0 0 0 0 0 1
6 : 0 0 0 0 0 0 0 0
7 : 0 0 0 0 0 0 0 0
8 : 1 0 0 0 0 0 0 0
which suggests to the edge 8->1, 2->5, 5->8 are taken but I won't be able to order all edges as 2->5, 5->8, and 8->1.
I was thinking to find the index at where the start node is (here it is 2,5) and search the matrix until x[i,j]>0 and x[j,k]>0 where inFlow[j]=outFlow[j]=1, but it does not work since there may have more than one k satisfying the problem (the output graph is directionless). I wonder if there is any idea how to save the order of edges in solution. Thanks.
One way would be over a variable representing the path:
array[1..n] of var 0..n: path;
Define the path through constraints:
constraint
% start point
path[1] = start
/\ % end point
path[sum(inFlow) + 1] = end
/\ % interior points
forall(p in 2..sum(inFlow))
(path[p] = sum(i in 1..n)(i * x[path[p-1], i]));
Then show the path as part of the output statement:
"path: " ++ show([path[i] | i in 1..sum(inFlow) + 1]) ++ "\n" ++

Lua (trAInsported): trying to implement Wavefront Algorithm, not working

I'm trying to implement a wavefront algorithm and I have a problem with the function, that produces the map with specific gradients.
I've tried several different versions of the code below and none of them worked.
The starting point for the algorithm (the goal) is set to 1 before and from that point on each neighbour's gradient should be increased (similar to every wavefront algorithm), if the gradient hasn't bin altered yet.
originX and originY is the goal, from which the alorithm should start. mapMatrix is a global variable
mapMatrix looks like this:
0 0 0 0 0 0 0
0 0 N 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 N 0 0 N 0 N
N N 0 0 N 0 0
0 0 0 0 0 0 0
(0 for rails, N(nil) for obstacles)
expected output example:
7 6 5 4 3 4 5
6 5 N 3 2 3 4
5 4 3 2 1 2 3
6 5 4 3 2 3 3
7 N 5 4 N 4 N
N N 6 5 N 5 6
9 8 7 6 7 6 7
And with this code for example:
function pathFinder(originX, originY)
northDir = originY - 1
eastDir = originX + 1
southDir = originY + 1
westDir = originX - 1
if northDir > 0 and mapMatrix[originX][northDir] == 0 then
mapMatrix[originX][northDir] = mapMatrix[originX][originY] + 1
pathFinder(originX, northDir)
end
if eastDir <= 7 and mapMatrix[eastDir][originY] == 0 then
mapMatrix[eastDir][originY] = mapMatrix[originX][originY] + 1
pathFinder(eastDir, originY)
end
if southDir <= 7 and mapMatrix[originX][southDir] == 0 then
mapMatrix[originX][southDir] = mapMatrix[originX][originY] + 1
pathFinder(originX, southDir)
end
if westDir > 0 and mapMatrix[westDir][originY] == 0 then
mapMatrix[westDir][originY] = mapMatrix[originX][originY] + 1
pathFinder(westDir, originY)
end
end
I get this mapMatrix:
0 0 0 0 3 4 5
0 0 N 0 2 10 6
0 0 0 0 1 9 7
0 0 0 0 0 0 8
0 N 0 0 N 0 N
N N 0 0 N 0 0
0 0 0 0 0 0 0
And if I switch the if statements arround it produces different version of mapMatrix
After making northDir, etc local the output looks like this: EDIT
33 24 23 22 3 4 5
32 25 N 21 2 11 6
31 26 27 20 1 10 7
30 29 28 19 20 9 8
31 N 29 18 N 10 N
N N 30 17 N 11 12
33 32 31 16 15 14 13
If more code or information is needed, I'd be happy to help
Your code is just wrong at all. As pathFinder is called recursively in the first check, it will be just going in that direction until any obstacle appears, and than going in the next direction, and so on.
BFS is actually a pretty simple algorithm. It can be easily implemented iteratively on a queue without any recursion as follow:
Put initial node to a queue;
Pop first node from the queue and process it;
Push unprocessed adjacent nodes to the end of the queue;
If queue is not empty, go to the step 2.
In Lua on a rectangular matrix it can be implemented in about two or three dozen of lines:
function gradient(matrix, originX, originY)
-- Create queue and put origin position and initial value to it.
local queue = { { originX, originY, 1 } }
repeat
-- Pop first position and value from the queue.
local x, y, value = unpack(table.remove(queue, 1))
-- Mark this position in the matrix.
matrix[y][x] = value
-- Check position to the top.
if y > 1 and matrix[y - 1][x] == 0 then
-- If it is not already processed, push it to the queue.
table.insert(queue, { x, y - 1, value + 1 })
end
-- Check position on the left.
if x > 1 and matrix[y][x - 1] == 0 then
table.insert(queue, { x - 1, y, value + 1 })
end
-- Check position to the bottom.
if y < #matrix and matrix[y + 1][x] == 0 then
table.insert(queue, { x, y + 1, value + 1 })
end
-- Check position on the right.
if x < #matrix[y] and matrix[y][x + 1] == 0 then
table.insert(queue, { x + 1, y, value + 1 })
end
-- Repeat, until queue is not empty.
until #queue == 0
end
-- Just helper function to print a matrix.
function printMatrix(matrix)
for _, row in pairs(matrix) do
for _, value in pairs(row) do
io.write(string.format("%2s", value))
end
io.write('\n')
end
end
local mapMatrix = {
{ 0, 0, 0, 0, 0, 0, 0, },
{ 0, 0, 'N', 0, 0, 0, 0, },
{ 0, 0, 0, 0, 0, 0, 0, },
{ 0, 0, 0, 0, 0, 0, 0, },
{ 0, 'N', 0, 0, 'N', 0, 'N', },
{ 'N', 'N', 0, 0, 'N', 0, 0, },
{ 0, 0, 0, 0, 0, 0, 0, },
}
gradient(mapMatrix, 5, 3)
printMatrix(mapMatrix)
--[[
Produces:
7 6 5 4 3 4 5
6 5 N 3 2 3 4
5 4 3 2 1 2 3
6 5 4 3 2 3 4
7 N 5 4 N 4 N
N N 6 5 N 5 6
9 8 7 6 7 6 7
]]
This is a complete script, runnable in the console.
But although, for illustrative purposes, this code is very simple, it is not very efficient. Each removal of the first item from the queue causes reindexing of the remaining items. For production code you should implement a linked list or something similar for the queue.

Pattern recognition in binary numbers (pseudo code or MQL5)

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
. . 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0 0
Recognition starts at 17 and goes backwards to 0.
What can be seen is the most simple pattern.
Pattern starts with at least three 0s or three 1s but could be more of each but not mixed!
First pattern is then followed by at least five 0s or five 1s depending on what came in the first pattern. Since pattern one contains three 0, there must be at least five 1s and vice versa
Then we want to see the first pattern again. At least three 0s or three 1s, again, depending wheather there were 1s or 0s before
Finally we want to see the second pattern again, which means at least five 0s or five 1s, again, depending on which pattern was seen before
I tried using for loops and counters but did not manage to work it out. What is struggling me is the fact, that the pattern is not of fixed size as there can be more than three or five 0s and 1s in succession.
Is anybody able to provide some pseudo code how to implement this or even some MQL5 code?
The following Swift code is everything else than optimal. It should just give you hints how you could implement it.
A function to match a single pattern:
func matchPattern(numbers: [Int], startIndex: Int, number: Int) -> Int {
var actualIndex = startIndex
while numbers[actualIndex] == number && actualIndex > 0 {
actualIndex = actualIndex - 1
}
return startIndex - actualIndex
}
A function to match the 4 patterns:
func match(binNrs: [Int]) -> Bool {
let firstPatternNr = binNrs[17]
let secondPatternNr = firstPatternNr == 0 ? 1 : 0
let pattern1Length = matchPattern(numbers: binNrs,
startIndex: 17,
number: firstPatternNr)
if pattern1Length < 3 { return false }
let pattern2Length = matchPattern(numbers: binNrs,
startIndex: 17 - pattern1Length,
number: secondPatternNr)
if pattern2Length < 5 { return false }
let pattern3Length = matchPattern(numbers: binNrs,
startIndex: 17 - pattern1Length - pattern2Length,
number: firstPatternNr)
if pattern3Length < 3 { return false }
let pattern4Length = matchPattern(numbers: binNrs,
startIndex: 17 - pattern1Length - pattern2Length - pattern3Length,
number: secondPatternNr)
return pattern4Length >= 5
}
Some test patterns with results:
let match1 = match(binNrs: [0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0]) // true
let match2 = match(binNrs: [1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]) // false (4th sequence < 5)
let match3 = match(binNrs: [0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0]) // false (1st sequence < 3)
let match4 = match(binNrs: [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1]) // false (2nd sequence < 5)

Algorithm to find the total number of connected sets in a matrix

i wanted to know which algorithm should i apply here. Would a DFS do?
Given a 2–d matrix. Find the total number of connected sets in that matrix.
Connected set can be defined as group of cell(s) which has 1 mentioned on it and have at least one other cell in that set with which they share the neighbor relationship. A cell with 1 in it and no surrounding neighbor having 1 in it can be considered as a set with one cell in it. Neighbors can be defined as all the cells adjacent to the given cell in 8 possible directions (i.e. N, W, E, S, NE, NW, SE, SW direction). A cell is not a neighbor of itself.
For example:
1 0 0 1
0 0 1 0
0 0 1 0
1 0 0 1
number of connected sets is 3
0 0 1 0 0 1 0 0
1 0 0 0 0 0 0 1
0 0 1 0 0 1 0 1
0 1 0 0 0 1 0 0
1 0 0 0 0 0 0 0
0 0 1 1 0 1 1 0
1 0 1 1 0 1 1 0
0 0 0 0 0 0 0 0
number of connected set is 9.
I don't think you will need to think of it as a general graph problem and apply any algorithm such as BFS or DFS.
You will need to do three scans of the matrix.
scan 1:
start from the top
give every number each 1 with 1..n, in you example the first row would after that step would look like
1 0 0 2
go to the next line and for every 1 in the row check if the neighbor to your left is non-0
if non-0 take on the value to the left
if 0 check for non-0 neighbors in the previous line and take on the value of the left most one
if all of those are 0 that simply add 1 to the maximum number given so far
repeat 2 until last line has been processed
and your example should look like follows
1 0 0 2
0 0 2 0
0 0 2 0
3 0 0 2
scan 2:
start from the bottom
check if each neighbor has the same number as the left most neighbor as well as the same number as the neighbor in the row below it
basically if you have a matrix like this
1 0 2
1 0 2
0 1 0
to check ensure that a set has really the same number
scan 3:
count the number of unique non-0 entries in the matrix
Connected-component labeling algorithm is intended to mark out connected groups of elements (both for 4-connectivity and for 8-connectivity)
Pythonic Implementation, More understandable code:
# sea is 2 D array of 0 and 1s we have to find 1's group surrounded by 0's
def dfs(sea, i, j, b, h, visited):
surround = ((-1, -1), (0, -1), (1, -1),
(-1, 0), (1, 0),
(-1, 1), (0, 1), (1, 1)
)
if can_visit(sea, i, j, b, h, visited):
for s in surround:
visited[(i, j)] = 1
dfs(sea, i + s[0], j + s[1], b, h, visited)
def can_visit(sea, i, j, b, h, visited):
if i >= 0 and j >= 0 and i < b and j < h:
if (i, j) not in visited and sea[i][j] == 1:
return True
def find_island(sea):
visited = {}
h = len(sea)
count = 0
for i, row in enumerate(sea):
b = len(row)
for j, item in enumerate(row):
if can_visit(sea, i, j, b, h, visited):
count += 1
dfs(sea, i, j, b, h, visited)
return count
sea = [[1, 1, 0, 0, 0],
[0, 1, 0, 0, 1],
[1, 0, 0, 1, 1],
[0, 0, 0, 0, 0],
[1, 0, 1, 0, 1]
]
print find_island(sea)
You want to use a disjoint set datastructure and algorithm. This will pick a unique representative for each connected component, which you can count at the end.
To efficiently evaluate which elements are neighbors, you can scan the matrix line by line, maintaining a list of segments (of consecutive 1's) from the previous line, while determining which segments on the current line are adjacent to them.
There are 3 connected sets. All 1 which are neighbors of each other are considered as one single set. All 1 at a[1,4], a[2,3], a[3,3] and a[4,4] form one set and one at a[1,1] form one set and one at a[4,1] forms one set.
Scan the matrix for 1s. When you find one, call a recursive function that marks off its connected component if it is not already identified as being in one. Use recursion to find connected components. Have a quick lookup somewhere that tells you whether a given node has already been identified as being in a connected component to avoid identifying connected components 2x, and to avoid infinite loops while traversing a connected component.
If you want do it just by your matrix (without extra memory), do it as follow:
Set scanner position as [0,0]
Set a counter to zero.
Scan matrix from current scanner position row by row (and cell by cell) and find one 1 and set scanner position to next element after this 1, if there isn't any 1 go to step 6.
Set related one to counter+2 and recursively find all of its 1 neighbors and also set them to count + 2.
count = count + 1
Go to step 2.
output count.
PS: It's clear if the scanner position is greater than matrix size your algorithm will finishes (I didn't wrote this to prevent confusion).
This isn't nearly as hard as it looks. In fact, this feels very strongly like something a professor would assign for an assignment in first year Computer Science. So if this is homework, you should tag it as such.
However, the solution is fairly easy.
for (int y = 0; y < arr.height(); y++)
{
for (int x = 0; x < arr.width(); x++)
{
if (arr[x][y] == 1)
{
if (CheckIfConnected(x, y, arr))
{
connectedPositionsX.Add(x);
connectedPositionsY.Add(y);
}
}
}
}
Where connectedPositions would be a linked list or whatever you want to store sets with.
arr is a 2D array containing a matrix of the type you specified above.
CheckIfConnected can be implemented fairly simply as well.
bool CheckIfConnected(int x, int y, int[][]arr)
{
if (arr.width() >= 2) || (arr.height() >= 2)
{
if ((x < arr.width()) && (x >= 0) && (y < arr.height()) && (y >= 0))
{
if ((x-1) >= 0) //West
{
if (arr[x-1][y] == 1)
{
adjCount[x-1][y] += 1;
return true;
}
}
if (((x-1) >= 0) && ((y-1) >= 0)) //Northwest
{
if (arr[x-1][y-1] == 1)
{
adjCount[x-1][y-1] += 1;
return true;
}
}
if ((y-1) >= 0) //North
{
if (arr[x][y-1] == 1)
{
adjCount[x][y-1] += 1;
return true;
}
}
if (((x+1) < arr.width()) && ((y-1) >= 0)) //Northeast
{
if (arr[x+1][y-1] == 1)
{
adjCount[x+1][y-1] += 1;
return true;
}
}
if ((x+1) < arr.width()) //East
{
if (arr[x+1][y] == 1)
{
adjCount[x+1][y] += 1;
return true;
}
}
//I'll let you implement Southeast to Southwest on your own,
//the pattern is clear now.
}
}
return false;
}
From there, you know how many times you found a pairing on each position in the grid. This helps you keep track of your connections.
The counts in the 2D array adjCount keeps track of this for you.
You could also go through and modify Dijkstra's Algorithm to do it recursively for you. Since you mentioned DFS (Depth First Search) I'm assuming your professor or teacher wants you to go about solving it that way.
In that case:
Here is Dijkstra's Algorithm in Pseudo Code:
http://en.wikipedia.org/wiki/Dijkstra's_algorithm
Hope that helps! Cheers!
Just keep searching in East, SouthEast, South and SouthWest direction at one go recursively for each node having value as 1.
If the call to visit function is a fresh call and not from recursion increase the connected components.
import java.util.Scanner;
public class Solution {
public static void visit(int[][] ar, boolean[][] v,int i, int j){
int size = ar.length;
if(ar[i][j] == 1){
v[i][j] = true;
if(j>0 && i<size-1){
visit(ar,v,i+1,j-1); // SouthWest
}
if(i<size-1){
visit(ar,v,i+1,j); // South
if(j < size-1)
visit(ar,v,i+1,j+1); // SouthEast
}
if(j<size-1)
visit(ar,v,i,j+1); // East
}
}
public static void main(String[] args) {
int[][] ar;
int count = 0;
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
ar = new int[n][n];
boolean[][] v = new boolean[n][n];
for(int i=0; i<n ; i++) {
for(int j=0; j<n; j++){
ar[i][j] = sc.nextInt();
v[i][j] = false;
}
}
for(int i=0; i<n ; i++) {
for(int j=0; j<n; j++){
if(ar[i][j] == 1 && !v[i][j]){
count++;
visit(ar,v,i,j);
}
}
}
System.out.println(count);
}
}
I have a class to help you find the total number of connected components in your 2D array. My class not only gives you the total number, but also gives you the clusters and visualize them for you. You can comment out the parts that you don't need. Please see this class in (java): https://github.com/m-vahidalizadeh/foundations/blob/master/src/algorithms/ConnectedComponetns.java
If you use python, there is a function in scipy to find this number.
from scipy.ndimage import label
data = [[0, 0, 1, 0, 0, 1, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 1, 0, 0, 1, 0, 1],
[0, 1, 0, 0, 0, 1, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 0, 1, 1, 0],
[1, 0, 1, 1, 0, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0]]
connection_structure = [[1,1,1],
[1,0,1],
[1,1,1]]
_, N = label(data, connection_structure)
print(N)
>>> 9

Resources