0

I have a matrix of 0's and 1's:

       1 0 0 0 0
       0 0 1 1 0
  a =  0 0 1 1 0
       1 0 0 0 0
       1 1 0 0 0

and I want to use python to decompose it into a sum of connected components, where by "connected" I mean matrices where each "1" has at least another "1" above/below/left/right of it. Otherwise the "1" must be isolated:

       1 0 0 0 0   1 0 0 0 0   0 0 0 0 0   0 0 0 0 0
       0 0 1 1 0   0 0 0 0 0   0 0 1 1 0   0 0 0 0 0
  a =  0 0 1 1 0 = 0 0 0 0 0 + 0 0 1 1 0 + 0 0 0 0 0
       1 0 0 0 0   0 0 0 0 0   0 0 0 0 0   1 0 0 0 0
       1 1 0 0 0   0 0 0 0 0   0 0 0 0 0   1 1 0 0 0

It might be interesting that in this question (Largest rectangle of 1's in 2d binary matrix) Guy Adini suggests to use BFS decomposition to decompose the matrix in connected components. However I couldn't find a python implementation of it, nor how to use BFS to solve my problem.

Community
  • 1
  • 1
astabada
  • 1,029
  • 4
  • 13
  • 26

2 Answers2

1

An algorithm that works is the following:

  • You keep a visited matrix of the same size with true for elements visited by the algorithm (or, equivalently, a set of the coordinates of the visited elements).

  • You go through all the elements of the matrix one by one:

    • If an element is not visited and is 1, you mark it as visited, and you recursively explore all its neighbors in the same way. The recursive function must return the set of connected ones (a matrix with these ones, a set with their coordinates, etc.).
Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260
1

Here come a custom implementation. I let you modify it to remove duplicates if you want.

import itertools

class Matrix():
    def __init__(self, matrix):
        self.matrix = matrix

    def computeConnectedComponents(self):
        rows_id = list(range(len(self.matrix)))
        cols_id = list(range(len(self.matrix[0])))

        #here are the position of every 1 in the grid. ( row number, column number) indexes start at 0
        positions = [couple for couple in self.generate_pairs(rows_id, cols_id) if self.matrix[couple[0]][couple[1]]==1]

        #here we store all the connected component finded
        allConnectedComponents = []

        #while there is position not affected to any connected component
        while positions != [] :
            #the first element is taken as start of the connected component
            to_explore = [positions.pop(0)]
            currentConnectedComponent = set()
            #while there is node connected to a node connected to the component
            while list(to_explore) != []:
                currentNode = to_explore.pop()
                currentConnectedComponent.add(currentNode)

                to_explore += [coord for coord in self.node_neighbourhood(currentNode) if (self.matrix[coord[0]][coord[1]]==1 and (coord not in to_explore) and (coord not in currentConnectedComponent))]

                allConnectedComponents.append(currentConnectedComponent)
                positions = [position for position in positions if position not in currentConnectedComponent]

        return allConnectedComponents

    #http://stackoverflow.com/questions/16135833/generate-combinations-of-elements-from-multiple-lists
    def generate_pairs(self, *args):
        for i, l in enumerate(args, 1):
            for x, y in itertools.product(l, itertools.chain(*args[i:])):
                yield (x, y)

    def node_neighbourhood(self, coordinates):
        row, column = coordinates[0], coordinates[1]
        result = []
        if (row - 1) >= 0 :
            result.append((row-1, column))
        if (row + 1) < len(self.matrix):
            result.append((row+1, column))
        if (column - 1) >= 0:
            result.append((row, column-1))
        if (column + 1) < len(self.matrix[0]):
            result.append((row, column+1))
        return result


if __name__ == "__main__":
    data = [[1,0,0,0,0],
           [0,0,1,1,0],
           [0,0,1,1,0],
           [1,0,0,0,0],
           [1,1,0,0,0]]

    matrix = Matrix(data)
    for connectedComponent in matrix.computeConnectedComponents():
        print(connectedComponent)
Arthur Vaïsse
  • 1,551
  • 1
  • 13
  • 26