day25: wow, part 1

This commit is contained in:
efim 2023-12-25 09:37:10 +00:00
parent a9caa4c8f1
commit b879b6541a
6 changed files with 1617 additions and 63 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,15 @@
flowchart LR flowchart TD
bvb --- cmg cmg --- qnr
hfx --- ntq cmg --- lhk
frs --- lsr
jqt --- nvd jqt --- nvd
lhk --- lsr
bvb --- ntq
bvb --- rhn bvb --- rhn
bvb --- xhk lsr --- pzl
frs --- rsh lhk --- lsr
pzl --- rsh lsr --- rsh
jqt --- ntq hfx --- ntq
lhk --- nvd
qnr --- rzs qnr --- rzs
cmg --- rzs bvb --- hfx
lhk --- nvd
frs --- qnr
jqt --- ntq
rhn --- xhk

View File

@ -1,34 +1,34 @@
flowchart TD flowchart TD
ntq --- xhk
bvb --- hfx
lsr --- pzl lsr --- pzl
nvd --- qnr
lhk --- lsr
nvd --- pzl nvd --- pzl
rhn --- xhk pzl --- rsh
jqt --- rhn frs --- lhk
cmg --- lhk
bvb --- cmg bvb --- cmg
jqt --- xhk jqt --- xhk
frs --- lsr
lsr --- rsh
hfx --- pzl
hfx --- xhk
frs --- lhk
pzl --- rsh
jqt --- ntq
hfx --- ntq
lhk --- nvd
lsr --- rzs
bvb --- xhk
frs --- rsh
qnr --- rzs
bvb --- rhn bvb --- rhn
bvb --- hfx jqt --- rhn
hfx --- xhk
frs --- lsr
lhk --- lsr
jqt --- nvd jqt --- nvd
cmg --- rzs cmg --- rzs
cmg --- nvd hfx --- pzl
bvb --- ntq bvb --- xhk
ntq --- xhk rhn --- xhk
frs --- qnr frs --- rsh
rsh --- rzs
hfx --- rhn
cmg --- qnr cmg --- qnr
nvd --- qnr
qnr --- rzs
bvb --- ntq
frs --- qnr
cmg --- nvd
hfx --- rhn
jqt --- ntq
hfx --- ntq
lsr --- rsh
cmg --- lhk
rsh --- rzs
lhk --- nvd
lsr --- rzs

View File

@ -68,8 +68,9 @@ func (g *Graph) readGraphLine(l string) {
return return
} }
// NOTE this is so sad. nodeA.Neighbors.Remove(nodeB.Name) hangs for a reason i don't understand
func (g *Graph) RemoveEdge(a, b string) { func (g *Graph) RemoveEdge(a, b string) {
log.Printf("entering remove edge for %s and %s", a, b) // log.Printf("entering remove edge for %s and %s", a, b)
nodeA, existsA := g.Nodes[a] nodeA, existsA := g.Nodes[a]
// log.Println("got first node", nodeA, existsA) // log.Println("got first node", nodeA, existsA)
nodeB, existsB := g.Nodes[b] nodeB, existsB := g.Nodes[b]
@ -79,52 +80,52 @@ func (g *Graph) RemoveEdge(a, b string) {
} }
// log.Println("before removals") // log.Println("before removals")
newANeighbors := mapset.NewSet[string]() // log.Println("before remove first", nodeA)
for oldNeighbor := range nodeA.Neighbors.Iter() { // nodeA.Neighbors = newANeighbors
if oldNeighbor != nodeB.Name { nodeA.Neighbors = nodeA.Neighbors.Difference(mapset.NewSet[string](nodeB.Name))
newANeighbors.Add(oldNeighbor) // nodeA.Neighbors.Remove(nodeB.Name)
} // log.Println("removed first", nodeA)
}
log.Println("before remove first", nodeA)
nodeA.Neighbors = newANeighbors
// nodeA.Neighbors.Remove(nodeB.Name)
log.Println("removed first", nodeA)
newBNeighbors := mapset.NewSet[string]() // log.Println("before remove second", nodeB)
for oldNeighbor := range nodeB.Neighbors.Iter() { // nodeB.Neighbors = newBNeighbors
if oldNeighbor != nodeA.Name { nodeB.Neighbors = nodeB.Neighbors.Difference(mapset.NewSet[string](nodeA.Name))
newBNeighbors.Add(oldNeighbor)
}
}
log.Println("before remove second", nodeB)
nodeB.Neighbors = newBNeighbors
// nodeB.Neighbors.Remove(nodeA.Name) // nodeB.Neighbors.Remove(nodeA.Name)
log.Println("removed second", nodeB) // log.Println("removed second", nodeB)
}
func (g *Graph) AddEdge(a, b string) {
nodeA, existsA := g.Nodes[a]
nodeB, existsB := g.Nodes[b]
if !existsA || !existsB {
panic("requesting not found node")
}
nodeA.Neighbors.Add(nodeB.Name)
nodeB.Neighbors.Add(nodeA.Name)
} }
func (g *Graph) findCycle() (from, to string, exists bool) { func (g *Graph) findCycle() (from, to string, exists bool) {
log.Printf(">>>> starting new find cycle") // log.Printf(">>>> starting new find cycle")
var firstNode *Node var firstNode *Node
for _, n := range g.Nodes { for _, n := range g.Nodes {
firstNode = n firstNode = n
break break
} }
log.Printf("initial search from %s and neighbors %+v", firstNode.Name, firstNode.Neighbors) // log.Printf("initial search from %s and neighbors %+v", firstNode.Name, firstNode.Neighbors)
for neighborName := range firstNode.Neighbors.Iter() { for neighborName := range firstNode.Neighbors.Iter() {
initialVisited := mapset.NewSet[string](firstNode.Name) initialVisited := mapset.NewSet[string](firstNode.Name)
log.Printf("initial dfs from %s to %s with initial visited %+v", firstNode.Name, neighborName, initialVisited) // log.Printf("initial dfs from %s to %s with initial visited %+v", firstNode.Name, neighborName, initialVisited)
from, to, exists = g.dfcCycle(firstNode.Name, neighborName, initialVisited) from, to, exists = g.dfcCycle(firstNode.Name, neighborName, initialVisited)
if exists { if exists {
break break
} }
} }
log.Printf("<<<< cycle %t, from %s to %s", exists, from, to) // log.Printf("<<<< cycle %t, from %s to %s", exists, from, to)
return return
} }
func (g *Graph) dfcCycle(fromName, atName string, visited mapset.Set[string]) (cycleFrom, cycleTo string, cycleExists bool) { func (g *Graph) dfcCycle(fromName, atName string, visited mapset.Set[string]) (cycleFrom, cycleTo string, cycleExists bool) {
log.Printf("> step from %+v to %+v. visited : %+v", fromName, atName, visited) // log.Printf("> step from %+v to %+v. visited : %+v", fromName, atName, visited)
if visited.Cardinality() == len(g.Nodes) { if visited.Cardinality() == len(g.Nodes) {
log.Println("exit by visited all") log.Println("exit by visited all")
return return
@ -215,6 +216,9 @@ func (g *Graph)SaveAsMermaid(filename string) {
type Edge struct { type Edge struct {
smaller, bigger string smaller, bigger string
} }
func (e Edge)String() string {
return fmt.Sprintf("%s/%s", e.smaller, e.bigger)
}
func CreateEdge(a, b string) Edge { func CreateEdge(a, b string) Edge {
var smaller, bigger string var smaller, bigger string
@ -235,7 +239,7 @@ func (g *Graph) RemoveAllCycles() (removedEdges mapset.Set[Edge]) {
for hasCycle { for hasCycle {
from, to, hasCycle = g.findCycle() from, to, hasCycle = g.findCycle()
if hasCycle { if hasCycle {
log.Printf("\n!!!! found cycle %s to %s\n", from, to) // log.Printf("\n!!!! found cycle %s to %s\n", from, to)
edgeToRemove := CreateEdge(from, to) edgeToRemove := CreateEdge(from, to)
removedEdges.Add(edgeToRemove) removedEdges.Add(edgeToRemove)
g.RemoveEdge(from, to) g.RemoveEdge(from, to)
@ -243,3 +247,55 @@ func (g *Graph) RemoveAllCycles() (removedEdges mapset.Set[Edge]) {
} }
return return
} }
func (g *Graph) TryToSplit() (componentSizeMult int) {
// first remove all cycles
removedEdges := g.RemoveAllCycles()
g.SaveAsMermaid("after-removing-cycles.mmd")
// log.Printf("all removed edges %+v, two of them are necessary to split initial graph into 2 ", removedEdges)
triedEdges := mapset.NewSet[Edge]()
for _, node := range g.Nodes {
for neighborName := range node.Neighbors.Iter() {
edge := CreateEdge(neighborName, node.Name)
if triedEdges.Contains(edge) {
continue
}
triedEdges.Add(edge)
// first remove the edge
g.RemoveEdge(edge.bigger, edge.smaller)
// then ask for components of the nodes of removed edge
compA := g.ComponentFrom(edge.bigger)
compB := g.ComponentFrom(edge.smaller)
// iterate over the initially removed edges. only two of them should be 'connecting'
// i.e were necessary to remove
necessaryEdgesCount := 0
for initiallyRemovedEdge := range removedEdges.Iter() {
endA, endB := initiallyRemovedEdge.bigger, initiallyRemovedEdge.smaller
isNonNecessary := (compA.Contains(endA) && compA.Contains(endB)) || (compB.Contains(endA) && compB.Contains(endB))
if !isNonNecessary {
// log.Printf("with edge %+v test removed, the %+v also seems necessary", edge, initiallyRemovedEdge)
necessaryEdgesCount += 1
}
}
// log.Printf("with edge %+v test removed neessary count is %d", edge, necessaryEdgesCount)
// if we found 2 necessary, then our currently tried edge is the third necesary to remove
// and out two components are the searched
if necessaryEdgesCount == 2 {
return compA.Cardinality() * compB.Cardinality()
}
// in the end add edge back if not fitting
g.AddEdge(edge.bigger, edge.smaller)
}
}
// now huh. if we didn't find `necessaryEdgesCount == 2`
// that means 0, 1 or 3
return
}

View File

@ -2,6 +2,8 @@ package day25
import ( import (
"testing" "testing"
mapset "github.com/deckarep/golang-set/v2"
) )
func TestReadFileExample(t *testing.T) { func TestReadFileExample(t *testing.T) {
@ -54,7 +56,41 @@ func TestRemoveAllCycles(t *testing.T) {
g.SaveAsMermaid("example-before-removing.mmd") g.SaveAsMermaid("example-before-removing.mmd")
t.Logf("initial graph is %+v", g) t.Logf("initial graph is %+v", g)
edges := g.RemoveAllCycles() edges := g.RemoveAllCycles()
expectedNecessary := mapset.NewSet[Edge](
CreateEdge("hfx", "pzl"),
CreateEdge("bvb", "cmg"),
CreateEdge("nvd", "jqt"),
)
intersection := expectedNecessary.Intersect(edges)
t.Logf("i expect that exactly two will be in intersection %+v", intersection)
if intersection.Cardinality() != 2 {
panic("huh?")
// ok, this is not what i expected.
// this is unstable. but i could run it several times? and hopefully luck out?
}
t.Logf("removed edges %+v", edges) t.Logf("removed edges %+v", edges)
t.Logf("after removal graph is %+v", g) t.Logf("after removal graph is %+v", g)
g.SaveAsMermaid("example-after-removing.mmd") g.SaveAsMermaid("example-after-removing.mmd")
} }
func TestSplittingExample(t *testing.T) {
filename := "example"
g := ReadGraphFile(filename)
result := g.TryToSplit()
t.Logf("hopefully same as example answer: %d", result)
}
func TestSplittingInput(t *testing.T) {
// kind of brute force
result := 0
filename := "input"
for result == 0 {
g := ReadGraphFile(filename)
result = g.TryToSplit()
t.Logf("hopefully as answer: %d", result)
}
}

View File

@ -456,3 +456,25 @@ PASS
ok sunshine.industries/aoc2023/day25 0.003s ok sunshine.industries/aoc2023/day25 0.003s
#+end_src #+end_src
** kind of bruteforce
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 518391
graph_test.go:93: hopefully as answer: 0
graph_test.go:93: hopefully as answer: 518391
graph_test.go:93: hopefully as answer: 0