173 lines
5.3 KiB
Go
173 lines
5.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
)
|
|
|
|
type graph struct {
|
|
nn int // number of nodes
|
|
st int // node numbering starts from
|
|
nbr [][]int // neighbor list for each node
|
|
}
|
|
|
|
type nodeval struct {
|
|
n int // number of node
|
|
v int // valence of node i.e. number of neighbors
|
|
}
|
|
|
|
func contains(s []int, n int) bool {
|
|
for _, e := range s {
|
|
if e == n {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func newGraph(nn, st int) graph {
|
|
nbr := make([][]int, nn)
|
|
return graph{nn, st, nbr}
|
|
}
|
|
|
|
// Note that this creates a single 'virtual' edge for an isolated node.
|
|
func (g graph) addEdge(n1, n2 int) {
|
|
n1, n2 = n1-g.st, n2-g.st // adjust to starting node number
|
|
g.nbr[n1] = append(g.nbr[n1], n2)
|
|
if n1 != n2 {
|
|
g.nbr[n2] = append(g.nbr[n2], n1)
|
|
}
|
|
}
|
|
|
|
// Uses 'greedy' algorithm.
|
|
func (g graph) greedyColoring() []int {
|
|
// create a slice with a color for each node, starting with color 0
|
|
cols := make([]int, g.nn) // all zero by default including the first node
|
|
for i := 1; i < g.nn; i++ {
|
|
cols[i] = -1 // mark all nodes after the first as having no color assigned (-1)
|
|
}
|
|
// create a bool slice to keep track of which colors are available
|
|
available := make([]bool, g.nn) // all false by default
|
|
// assign colors to all nodes after the first
|
|
for i := 1; i < g.nn; i++ {
|
|
// iterate through neighbors and mark their colors as available
|
|
for _, j := range g.nbr[i] {
|
|
if cols[j] != -1 {
|
|
available[cols[j]] = true
|
|
}
|
|
}
|
|
// find the first available color
|
|
c := 0
|
|
for ; c < g.nn; c++ {
|
|
if !available[c] {
|
|
break
|
|
}
|
|
}
|
|
cols[i] = c // assign it to the current node
|
|
// reset the neighbors' colors to unavailable
|
|
// before the next iteration
|
|
for _, j := range g.nbr[i] {
|
|
if cols[j] != -1 {
|
|
available[cols[j]] = false
|
|
}
|
|
}
|
|
}
|
|
return cols
|
|
}
|
|
|
|
// Uses Welsh-Powell algorithm.
|
|
func (g graph) wpColoring() []int {
|
|
// create nodeval for each node
|
|
nvs := make([]nodeval, g.nn)
|
|
for i := 0; i < g.nn; i++ {
|
|
v := len(g.nbr[i])
|
|
if v == 1 && g.nbr[i][0] == i { // isolated node
|
|
v = 0
|
|
}
|
|
nvs[i] = nodeval{i, v}
|
|
}
|
|
// sort the nodevals in descending order by valence
|
|
sort.Slice(nvs, func(i, j int) bool {
|
|
return nvs[i].v > nvs[j].v
|
|
})
|
|
// create colors slice with entries for each node
|
|
cols := make([]int, g.nn)
|
|
for i := range cols {
|
|
cols[i] = -1 // set all nodes to no color (-1) initially
|
|
}
|
|
currCol := 0 // start with color 0
|
|
for f := 0; f < g.nn-1; f++ {
|
|
h := nvs[f].n
|
|
if cols[h] != -1 { // already assigned a color
|
|
continue
|
|
}
|
|
cols[h] = currCol
|
|
// assign same color to all subsequent uncolored nodes which are
|
|
// not connected to a previous colored one
|
|
outer:
|
|
for i := f + 1; i < g.nn; i++ {
|
|
j := nvs[i].n
|
|
if cols[j] != -1 { // already colored
|
|
continue
|
|
}
|
|
for k := f; k < i; k++ {
|
|
l := nvs[k].n
|
|
if cols[l] == -1 { // not yet colored
|
|
continue
|
|
}
|
|
if contains(g.nbr[j], l) {
|
|
continue outer // node j is connected to an earlier colored node
|
|
}
|
|
}
|
|
cols[j] = currCol
|
|
}
|
|
currCol++
|
|
}
|
|
return cols
|
|
}
|
|
|
|
func main() {
|
|
fns := [](func(graph) []int){graph.greedyColoring, graph.wpColoring}
|
|
titles := []string{"'Greedy'", "Welsh-Powell"}
|
|
nns := []int{4, 8, 8, 8}
|
|
starts := []int{0, 1, 1, 1}
|
|
edges1 := [][2]int{{0, 1}, {1, 2}, {2, 0}, {3, 3}}
|
|
edges2 := [][2]int{{1, 6}, {1, 7}, {1, 8}, {2, 5}, {2, 7}, {2, 8},
|
|
{3, 5}, {3, 6}, {3, 8}, {4, 5}, {4, 6}, {4, 7}}
|
|
edges3 := [][2]int{{1, 4}, {1, 6}, {1, 8}, {3, 2}, {3, 6}, {3, 8},
|
|
{5, 2}, {5, 4}, {5, 8}, {7, 2}, {7, 4}, {7, 6}}
|
|
edges4 := [][2]int{{1, 6}, {7, 1}, {8, 1}, {5, 2}, {2, 7}, {2, 8},
|
|
{3, 5}, {6, 3}, {3, 8}, {4, 5}, {4, 6}, {4, 7}}
|
|
for j, fn := range fns {
|
|
fmt.Println("Using the", titles[j], "algorithm:\n")
|
|
for i, edges := range [][][2]int{edges1, edges2, edges3, edges4} {
|
|
fmt.Println(" Example", i+1)
|
|
g := newGraph(nns[i], starts[i])
|
|
for _, e := range edges {
|
|
g.addEdge(e[0], e[1])
|
|
}
|
|
cols := fn(g)
|
|
ecount := 0 // counts edges
|
|
for _, e := range edges {
|
|
if e[0] != e[1] {
|
|
fmt.Printf(" Edge %d-%d -> Color %d, %d\n", e[0], e[1],
|
|
cols[e[0]-g.st], cols[e[1]-g.st])
|
|
ecount++
|
|
} else {
|
|
fmt.Printf(" Node %d -> Color %d\n", e[0], cols[e[0]-g.st])
|
|
}
|
|
}
|
|
maxCol := 0 // maximum color number used
|
|
for _, col := range cols {
|
|
if col > maxCol {
|
|
maxCol = col
|
|
}
|
|
}
|
|
fmt.Println(" Number of nodes :", nns[i])
|
|
fmt.Println(" Number of edges :", ecount)
|
|
fmt.Println(" Number of colors :", maxCol+1)
|
|
fmt.Println()
|
|
}
|
|
}
|
|
}
|