38
Reducing in Kotlin
Kotlin's data classes and extension functions make it easy to express "the same problem from another angle."
- Define a Graph as a set of vertices and a set of unordered edge pairs. Kotlin's Set<Set<Int>> is a fine, immutable representation for small problems.
data class Graph(val n: Int, val edges: Set<Set<Int>>) { fun adjacent(u: Int, v: Int) = setOf(u, v) in edges } - The complement graph swaps "edge" and "non-edge." A one-line extension function turns the original problem into a graph-coloring problem.
fun Graph.complement(): Graph { val es = mutableSetOf<Set<Int>>() for (u in 0 until n) for (v in u + 1 until n) if (!adjacent(u, v)) es += setOf(u, v) return Graph(n, es) } - Greedy coloring (Welsh–Powell) on the complement gives an upper bound on the clique cover number. Sort vertices by degree descending; assign the smallest color that doesn't clash with already-colored neighbors.
fun Graph.greedyColor(): IntArray { val color = IntArray(n) { -1 } val order = (0 until n).sortedByDescending { v -> edges.count { v in it } } for (v in order) { val used = (0 until n).filter { adjacent(v, it) && color[it] >= 0 } .map { color[it] }.toSet() var c = 0 while (c in used) c++ color[v] = c } return color } - Read off the clique cover by grouping vertices by color in the complement. Same color = independent set in complement = clique in original. The reduction is the algorithm.
fun Graph.cliqueCover(): List<List<Int>> { val color = complement().greedyColor() return color.withIndex() .groupBy({ it.value }, { it.index }) .values.toList() }