From c6c9f4707bc7a2bb3d8a8c90eea5dba7ac12e55f Mon Sep 17 00:00:00 2001 From: Arin Khare <51566200+lol-cubes@users.noreply.github.com> Date: Sat, 1 Aug 2020 00:00:34 -0400 Subject: [PATCH] Karger's Algorithm (#2237) * Add implementation of Karger's Algorithm * Remove print statement from karger's algorithm function * Fix style issues in graphs/karger.py * Change for loops to set comprehensions where appropriate in graphs/karger.py --- graphs/karger.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 graphs/karger.py diff --git a/graphs/karger.py b/graphs/karger.py new file mode 100644 index 000000000..d5a27c285 --- /dev/null +++ b/graphs/karger.py @@ -0,0 +1,83 @@ +""" +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))