812-graph-coloring — Graph Coloring
Tutorial Video
Text description (accessibility)
This video demonstrates the "812-graph-coloring — Graph Coloring" functional Rust example. Difficulty level: Intermediate. Key concepts covered: Functional Programming. Graph coloring assigns colors to vertices so no two adjacent vertices share a color, using at most k colors. Key difference from OCaml: 1. **Backtracking structure**: Identical in both languages — assign, recurse, undo. The algorithm is language
Tutorial
The Problem
Graph coloring assigns colors to vertices so no two adjacent vertices share a color, using at most k colors. The four-color theorem (every planar map is 4-colorable) is famous; the general k-coloring decision problem is NP-complete for k ≥ 3. Applications: register allocation in compilers (variables with conflicting lifetimes need different registers), exam scheduling (conflicting exams need different time slots), frequency assignment in cellular networks.
🎯 Learning Outcomes
is_safe(v, c) — no adjacent vertex has color cCode Example
#![allow(clippy::all)]
//! # Graph Coloring
pub fn graph_coloring(n: usize, edges: &[(usize, usize)], m: usize) -> Option<Vec<usize>> {
let mut adj = vec![vec![]; n];
for &(u, v) in edges {
adj[u].push(v);
adj[v].push(u);
}
let mut colors = vec![0; n];
fn is_safe(v: usize, c: usize, adj: &[Vec<usize>], colors: &[usize]) -> bool {
adj[v].iter().all(|&u| colors[u] != c)
}
fn solve(v: usize, n: usize, m: usize, adj: &[Vec<usize>], colors: &mut [usize]) -> bool {
if v == n {
return true;
}
for c in 1..=m {
if is_safe(v, c, adj, colors) {
colors[v] = c;
if solve(v + 1, n, m, adj, colors) {
return true;
}
colors[v] = 0;
}
}
false
}
if solve(0, n, m, &adj, &mut colors) {
Some(colors)
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_color() {
let c = graph_coloring(4, &[(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)], 3);
assert!(c.is_some());
}
}Key Differences
rustc uses a similar approach in its codegen.OCaml Approach
OCaml implements with colors: int array initialized to 0 (uncolored). The is_safe function uses List.for_all (fun u -> colors.(u) <> c) adj.(v). The recursive backtracking is a natural OCaml pattern. The exception Found can terminate early. OCaml's Graph_coloring in academic libraries implements both exact and approximation algorithms.
Full Source
#![allow(clippy::all)]
//! # Graph Coloring
pub fn graph_coloring(n: usize, edges: &[(usize, usize)], m: usize) -> Option<Vec<usize>> {
let mut adj = vec![vec![]; n];
for &(u, v) in edges {
adj[u].push(v);
adj[v].push(u);
}
let mut colors = vec![0; n];
fn is_safe(v: usize, c: usize, adj: &[Vec<usize>], colors: &[usize]) -> bool {
adj[v].iter().all(|&u| colors[u] != c)
}
fn solve(v: usize, n: usize, m: usize, adj: &[Vec<usize>], colors: &mut [usize]) -> bool {
if v == n {
return true;
}
for c in 1..=m {
if is_safe(v, c, adj, colors) {
colors[v] = c;
if solve(v + 1, n, m, adj, colors) {
return true;
}
colors[v] = 0;
}
}
false
}
if solve(0, n, m, &adj, &mut colors) {
Some(colors)
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_color() {
let c = graph_coloring(4, &[(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)], 3);
assert!(c.is_some());
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_color() {
let c = graph_coloring(4, &[(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)], 3);
assert!(c.is_some());
}
}
Deep Comparison
OCaml vs Rust: Graph Coloring
Overview
See the example.rs and example.ml files for detailed implementations.
Key Differences
| Aspect | OCaml | Rust |
|---|---|---|
| Type system | Hindley-Milner | Ownership + traits |
| Memory | GC | Zero-cost abstractions |
| Mutability | Explicit ref | mut keyword |
| Error handling | Option/Result | Result<T, E> |
See README.md for detailed comparison.
Exercises
Δ+1 colors where Δ is the maximum degree.