use rand::Rng;
pub struct UnionFind {
parent: Vec<usize>,
rank: Vec<usize>,
}
impl UnionFind {
pub fn new(n: usize) -> Self {
Self {
parent: (0..n).collect(),
rank: vec![0; n],
}
}
pub fn find(&mut self, x: usize) -> usize {
if self.parent[x] != x {
self.parent[x] = self.find(self.parent[x]);
}
self.parent[x]
}
pub fn union(&mut self, x: usize, y: usize) -> bool {
let x_root = self.find(x);
let y_root = self.find(y);
if x_root == y_root {
return false;
}
match self.rank[x_root].cmp(&self.rank[y_root]) {
std::cmp::Ordering::Less => self.parent[x_root] = y_root,
std::cmp::Ordering::Greater => self.parent[y_root] = x_root,
std::cmp::Ordering::Equal => {
self.parent[y_root] = x_root;
self.rank[x_root] += 1;
}
}
true
}
}
pub fn karger_min_cut(num_vertices: usize, edges: &[(usize, usize)], trials: usize) -> usize {
let mut best_cut = usize::MAX;
let mut rng = rand::thread_rng();
for _ in 0..trials {
let mut uf = UnionFind::new(num_vertices);
let mut remaining = num_vertices;
let mut current_edges = edges.to_vec();
while remaining > 2 {
let idx = rng.gen_range(0..current_edges.len());
let (u, v) = current_edges[idx];
let set_u = uf.find(u);
let set_v = uf.find(v);
if set_u == set_v {
current_edges.remove(idx);
continue;
}
uf.union(set_u, set_v);
remaining -= 1;
current_edges.retain(|&(a, b)| uf.find(a) != uf.find(b));
}
let cut = edges
.iter()
.filter(|&&(u, v)| uf.find(u) != uf.find(v))
.count();
if cut < best_cut {
best_cut = cut;
}
}
best_cut
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_karger_min_cut_triangle() {
let vertices = 3;
let edges = vec![(0, 1), (1, 2), (2, 0)];
let cut = karger_min_cut(vertices, &edges, 100);
assert_eq!(cut, 2);
}
#[test]
fn test_karger_min_cut_square() {
let vertices = 4;
let edges = vec![(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)];
let cut = karger_min_cut(vertices, &edges, 200);
assert!(cut <= 3);
}
}