Python Implementation of the Hungarian Algorithm

The Hungarian Algorithm is a classic approach to solve the Maximum Bipartite Matching problem. In a bipartite graph, it identifies the largest set of edges such that each vertex is connected to at most one adjacent vertex.

To illustrate its operation, consider a bipartite graph with left vertices (tasks: A, B, C, D) and right vetrices (workers: 1, 2, 3, 4), where matrix entries represent edge weights:

    1  2  3  4
A  3  2  0  1
B  2  4  1  0
C  1  3  2  1
D  0  2  5  2

Step 1: Initialization

Left vertices start with a label of 0 (task priorities):

A: 0 
B: 0 
C: 0 
D: 0 

Step 2: Finding Augmentinng Paths

We iteratively find paths that increase the match count. Start with vertex A:

  • For A: Edges exist to 3 and 4. First, check A-3 (unmatched) → match A-3. Then check A-4 (unmatched) → match A-4.
  • For B: Edges to 1 and 2. Check B-1 (unmatched) → match B-1.
  • For C: Edge to 3 (unmatched) → match C-3.
  • For D: Edge to 2 (unmatched) → match D-2.

Step 3: Result

The maximum matching includes: A-3, A-4, B-1, C-3, D-2 (all tasks are assigned).

Algorithm Steps (Summary)

  1. Initialize: Set initial labels for left vertices and track unmatched states.
  2. Find Augmenting Paths: For each unmatched left vertex, use DFS to find a path to an unmatched right vertex (or a matched vertex with an augmenting path).
  3. Update Matches: If an augmenting path is found, update the matching and repeat.
  4. Terminate: When no new augmenting paths exist, the maximum matching is found.

Code Implemantation (with Modified Structure/Variable Names)

class BipartiteMatcher:
    def __init__(self, adj_matrix):
        self.matrix = adj_matrix
        self.row_count = len(adj_matrix)
        self.col_count = len(adj_matrix[0]) if self.row_count > 0 else 0
        self.matching = [-1] * self.col_count  # Tracks right-to-left matches
        self.visited_cols = []  # For DFS visit tracking

    def find_max_matching(self):
        match_count = 0
        for row in range(self.row_count):
            self.visited_cols = [False] * self.col_count
            if self._dfs(row):
                match_count += 1
        return match_count, self.matching

    def _dfs(self, current_row):
        for col in range(self.col_count):
            if self.matrix[current_row][col] and not self.visited_cols[col]:
                self.visited_cols[col] = True
                if self.matching[col] == -1 or self._dfs(self.matching[col]):
                    self.matching[col] = current_row
                    return True
        return False

# Example Usage
if __name__ == "__main__":
    # Bipartite graph as adjacency matrix (1 = edge exists)
    task_worker_matrix = [
        [0, 1, 1, 0],  # A: edges to 2, 3
        [1, 0, 0, 1],  # B: edges to 1, 4
        [0, 0, 1, 0],  # C: edge to 3
        [0, 0, 1, 1]   # D: edges to 3, 4
    ]
    matcher = BipartiteMatcher(task_worker_matrix)
    total_matches, match_details = matcher.find_max_matching()
    print("Maximum Matches:", total_matches)
    print("Match Assignment (col → row):", match_details)

In practice, the algorithm adjusts vertex labels and repeats path-finding until no new augmenting paths exist, ensuring the largest possible matching.

Tags: python Hungarian Algorithm Bipartite Matching graph theory algorithm

Posted on Mon, 13 Jul 2026 16:22:53 +0000 by MichaelHe