mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-24 05:21:09 +00:00
84 lines
2.6 KiB
Python
84 lines
2.6 KiB
Python
|
"""
|
||
|
An implementation of Karger's Algorithm for partitioning a graph.
|
||
|
"""
|
||
|
|
||
|
import random
|
||
|
from typing import Dict, List, Set, Tuple
|
||
|
|
||
|
|
||
|
# Adjacency list representation of this graph:
|
||
|
# https://en.wikipedia.org/wiki/File:Single_run_of_Karger%E2%80%99s_Mincut_algorithm.svg
|
||
|
TEST_GRAPH = {
|
||
|
'1': ['2', '3', '4', '5'],
|
||
|
'2': ['1', '3', '4', '5'],
|
||
|
'3': ['1', '2', '4', '5', '10'],
|
||
|
'4': ['1', '2', '3', '5', '6'],
|
||
|
'5': ['1', '2', '3', '4', '7'],
|
||
|
'6': ['7', '8', '9', '10', '4'],
|
||
|
'7': ['6', '8', '9', '10', '5'],
|
||
|
'8': ['6', '7', '9', '10'],
|
||
|
'9': ['6', '7', '8', '10'],
|
||
|
'10': ['6', '7', '8', '9', '3']
|
||
|
}
|
||
|
|
||
|
|
||
|
def partition_graph(graph: Dict[str, List[str]]) -> Set[Tuple[str, str]]:
|
||
|
"""
|
||
|
Partitions a graph using Karger's Algorithm. Implemented from
|
||
|
pseudocode found here:
|
||
|
https://en.wikipedia.org/wiki/Karger%27s_algorithm.
|
||
|
This function involves random choices, meaning it will not give
|
||
|
consistent outputs.
|
||
|
|
||
|
Args:
|
||
|
graph: A dictionary containing adacency lists for the graph.
|
||
|
Nodes must be strings.
|
||
|
|
||
|
Returns:
|
||
|
The cutset of the cut found by Karger's Algorithm.
|
||
|
|
||
|
>>> graph = {'0':['1'], '1':['0']}
|
||
|
>>> partition_graph(graph)
|
||
|
{('0', '1')}
|
||
|
"""
|
||
|
# Dict that maps contracted nodes to a list of all the nodes it "contains."
|
||
|
contracted_nodes = {node: {node} for node in graph}
|
||
|
|
||
|
graph_copy = {node: graph[node][:] for node in graph}
|
||
|
|
||
|
while len(graph_copy) > 2:
|
||
|
|
||
|
# Choose a random edge.
|
||
|
u = random.choice(list(graph_copy.keys()))
|
||
|
v = random.choice(graph_copy[u])
|
||
|
|
||
|
# Contract edge (u, v) to new node uv
|
||
|
uv = u + v
|
||
|
uv_neighbors = list(set(graph_copy[u] + graph_copy[v]))
|
||
|
uv_neighbors.remove(u)
|
||
|
uv_neighbors.remove(v)
|
||
|
graph_copy[uv] = uv_neighbors
|
||
|
for neighbor in uv_neighbors:
|
||
|
graph_copy[neighbor].append(uv)
|
||
|
|
||
|
contracted_nodes[uv] = {contracted_node for contracted_node in
|
||
|
contracted_nodes[u].union(contracted_nodes[v])}
|
||
|
|
||
|
# Remove nodes u and v.
|
||
|
del graph_copy[u]
|
||
|
del graph_copy[v]
|
||
|
for neighbor in uv_neighbors:
|
||
|
if u in graph_copy[neighbor]:
|
||
|
graph_copy[neighbor].remove(u)
|
||
|
if v in graph_copy[neighbor]:
|
||
|
graph_copy[neighbor].remove(v)
|
||
|
|
||
|
# Find cutset.
|
||
|
groups = [contracted_nodes[node] for node in graph_copy]
|
||
|
return {(node, neighbor) for node in groups[0]
|
||
|
for neighbor in graph[node] if neighbor in groups[1]}
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
print(partition_graph(TEST_GRAPH))
|