Welcome to our deep dive into the fascinating world of Bipartite Matching! This lesson is designed for beginners and intermediates alike, so let's get started. š
Bipartite Matching is a technique used in Graph Theory that helps find matching between the nodes of a graph when those nodes are divided into two disjoint sets (also known as bipartitions). It's a powerful tool used in various real-world applications, such as job assignment, course timetabling, and network design.
Bipartite Matching provides a solution for problems where we have a set of items (like jobs or courses) and a set of candidates (like applicants or students), and we want to find the best possible matching for each item with a candidate.
A bipartite graph is a graph where the vertices (nodes) can be divided into two disjoint sets, such that every edge connects a vertex in one set to a vertex in the other set.
Which of the following graphs is a bipartite graph?
A matching in a bipartite graph is a set of edges such that no two edges share a common vertex. In other words, each vertex in the graph is either part of an edge or unmatched.
A maximum matching in a bipartite graph is a matching that covers the maximum number of vertices. In other words, a maximum matching is a matching that can't be extended by adding any more edges without violating the "no two edges sharing a common vertex" rule.
To find a maximum matching in a bipartite graph, we can use the concept of augmenting paths. An augmenting path is a path that starts and ends on unmatched vertices and contains an edge from a matched vertex to an unmatched vertex.
The Hungarian Algorithm is a powerful technique for finding a maximum matching in a bipartite graph. It was developed by Hungarian mathematician Denes Koenig in 1951.
Let's see some practical examples of Bipartite Matching in Python:
def bipartite_matching(bipartite_graph):
matched = {}
unmatched_vertices = set(bipartite_graph.keys())
while unmatched_vertices:
unmatched_vertices_set = list(unmatched_vertices)
for vertex in unmatched_vertices_set:
if all(vertex not in edge for edge in bipartite_graph[vertex]):
matched[vertex] = None
unmatched_vertices.remove(vertex)
for neighbor in bipartite_graph[vertex]:
if neighbor in unmatched_vertices:
unmatched_vertices.remove(neighbor)
bipartite_graph[neighbor].remove(vertex)
break
return matched
bipartite_graph = {
'A': ['B', 'C'],
'B': ['A', 'D'],
'C': ['A', 'E'],
'D': [],
'E': []
}
print(bipartite_matching(bipartite_graph))from copy import deepcopy
def min_sum(matrix, n):
row_min = [float('inf')] * n
col_min = [float('inf')] * n
for i in range(n):
for j in range(n):
row_min[i] = min(row_min[i], matrix[i][j])
col_min[j] = min(col_min[j], matrix[i][j])
return row_min, col_min
def augment(matrix, n, matched, row_min, col_min):
u = 0
for i in range(1, n):
if row_min[i] + col_min[matched[i]] == matrix[i][matched[i]]:
u = i
break
v = matched[u]
w = matrix[u][v]
for i in range(1, n):
if i != u and matched[i] != v:
matrix[i][v] += w
matrix[u][matched[i]] -= w
matched[u] = v
matched[v] = u
def hungarian_algorithm(matrix):
n = len(matrix)
if n < 2:
return matrix
matrix_copy = deepcopy(matrix)
row_min, col_min = min_sum(matrix_copy, n)
unmatched_rows = [i for i in range(1, n) if matched[i] is None]
unmatched_cols = [i for i in range(1, n) if matrix[0][i] == 0]
for _ in range(n - 1):
augment(matrix_copy, n, matched, row_min, col_min)
return matched
matrix = [
[0, 6, 0, 0],
[0, 0, 0, 6],
[0, 0, 3, 0],
[0, 0, 0, 0]
]
matched = hungarian_algorithm(matrix)
print(matched)In the next lesson, we will dive deeper into the Hungarian Algorithm, exploring its inner workings and optimizations. Stay tuned! š
Keep coding, keep learning! š»š