Optimizations and minor cleanups
Thanks to Go's profiling tools, I discovered that the memoization, while it was cutting down runtime significantly, was itself slow because it was using arrays. Swapping those arrays out for maps made a _massive_ difference (4s/14s part1/part2 to 1ms/2ms with no other changes). Lesson learned. Again. The IntHeap rename was long overdue since I took the code originally from Go's sample docs for priority queues.
This commit is contained in:
82
days/18.go
82
days/18.go
@ -106,7 +106,7 @@ func (d Day18) findAdjacentCells(inPos day18Vec, keys, doors map[day18Vec]int, g
|
||||
}
|
||||
|
||||
queue := deque.NewDeque[u.Pair[int, day18Vec]]()
|
||||
visited := []day18Vec{inPos}
|
||||
visited := make(map[day18Vec]bool)
|
||||
for _, adjacent := range getAdjacent(inPos) {
|
||||
queue.PushBack(u.Pair[int, day18Vec]{First: 1, Second: adjacent})
|
||||
}
|
||||
@ -114,8 +114,8 @@ func (d Day18) findAdjacentCells(inPos day18Vec, keys, doors map[day18Vec]int, g
|
||||
for !queue.IsEmpty() {
|
||||
next := queue.PopFront()
|
||||
|
||||
if !u.ArrayContains(visited, next.Second) {
|
||||
visited = append(visited, next.Second)
|
||||
if _, exists := visited[next.Second]; !exists {
|
||||
visited[next.Second] = true
|
||||
|
||||
key, adjacentIsKey := keys[next.Second]
|
||||
door, adjacentIsDoor := doors[next.Second]
|
||||
@ -141,7 +141,7 @@ func (d Day18) findAdjacentCells(inPos day18Vec, keys, doors map[day18Vec]int, g
|
||||
}
|
||||
|
||||
for _, neighbor := range getAdjacent(next.Second) {
|
||||
if !u.ArrayContains(visited, neighbor) {
|
||||
if _, exists := visited[neighbor]; !exists {
|
||||
queue.PushBack(u.Pair[int, day18Vec]{First: next.First + 1, Second: neighbor})
|
||||
}
|
||||
}
|
||||
@ -155,19 +155,17 @@ type day18PriorityQueue struct {
|
||||
distance int
|
||||
neighbor rune
|
||||
}
|
||||
type IntHeap []day18PriorityQueue
|
||||
type PriorityQueueHeap []day18PriorityQueue
|
||||
|
||||
func (h IntHeap) Len() int { return len(h) }
|
||||
func (h IntHeap) Less(i, j int) bool { return h[i].distance < h[j].distance }
|
||||
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||
func (h PriorityQueueHeap) Len() int { return len(h) }
|
||||
func (h PriorityQueueHeap) Less(i, j int) bool { return h[i].distance < h[j].distance }
|
||||
func (h PriorityQueueHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||
|
||||
func (h *IntHeap) Push(x any) {
|
||||
// Push and Pop use pointer receivers because they modify the slice's length,
|
||||
// not just its contents.
|
||||
func (h *PriorityQueueHeap) Push(x any) {
|
||||
*h = append(*h, x.(day18PriorityQueue))
|
||||
}
|
||||
|
||||
func (h *IntHeap) Pop() any {
|
||||
func (h *PriorityQueueHeap) Pop() any {
|
||||
old := *h
|
||||
n := len(old)
|
||||
x := old[n-1]
|
||||
@ -176,25 +174,25 @@ func (h *IntHeap) Pop() any {
|
||||
}
|
||||
|
||||
type reachableKeysMemo struct {
|
||||
pos rune
|
||||
keysFound int
|
||||
reachableKeys []u.Pair[rune, int]
|
||||
pos rune
|
||||
keysFound int
|
||||
}
|
||||
|
||||
var knownReachableKeys = make([]reachableKeysMemo, 0)
|
||||
var knownReachableKeys = make(map[reachableKeysMemo][]u.Pair[rune, int])
|
||||
|
||||
func (d Day18) reachableKeys(inPos rune, keysFound int, graph day18Graph) []u.Pair[rune, int] {
|
||||
for _, m := range knownReachableKeys {
|
||||
if m.pos == inPos &&
|
||||
m.keysFound == keysFound {
|
||||
return m.reachableKeys
|
||||
}
|
||||
memo := reachableKeysMemo{
|
||||
pos: inPos,
|
||||
keysFound: keysFound,
|
||||
}
|
||||
if v, exists := knownReachableKeys[memo]; exists {
|
||||
return v
|
||||
}
|
||||
|
||||
ret := make([]u.Pair[rune, int], 0)
|
||||
distance := make(map[rune]int)
|
||||
|
||||
ih := make(IntHeap, 0)
|
||||
ih := make(PriorityQueueHeap, 0)
|
||||
|
||||
for _, p := range graph[inPos] {
|
||||
ih = append(ih, day18PriorityQueue{
|
||||
@ -231,31 +229,26 @@ func (d Day18) reachableKeys(inPos rune, keysFound int, graph day18Graph) []u.Pa
|
||||
}
|
||||
}
|
||||
|
||||
memo := reachableKeysMemo{
|
||||
pos: inPos,
|
||||
keysFound: keysFound,
|
||||
reachableKeys: ret,
|
||||
}
|
||||
knownReachableKeys = append(knownReachableKeys, memo)
|
||||
return memo.reachableKeys
|
||||
knownReachableKeys[memo] = ret
|
||||
return ret
|
||||
}
|
||||
|
||||
type minStepsMemo struct {
|
||||
pos string
|
||||
keysToFind int
|
||||
keysFound int
|
||||
dist int
|
||||
}
|
||||
|
||||
var knownMinimumSteps = make([]minStepsMemo, 0)
|
||||
var knownMinimumSteps = make(map[minStepsMemo]int, 0)
|
||||
|
||||
func (d Day18) minimumSteps(inPos string, keysToFind int, keysFound int, graph day18Graph) int {
|
||||
for _, m := range knownMinimumSteps {
|
||||
if m.pos == inPos &&
|
||||
m.keysToFind == keysToFind &&
|
||||
m.keysFound == keysFound {
|
||||
return m.dist
|
||||
}
|
||||
memo := minStepsMemo{
|
||||
pos: inPos,
|
||||
keysToFind: keysToFind,
|
||||
keysFound: keysFound,
|
||||
}
|
||||
if v, exists := knownMinimumSteps[memo]; exists {
|
||||
return v
|
||||
}
|
||||
|
||||
if keysToFind == 0 {
|
||||
@ -285,14 +278,8 @@ func (d Day18) minimumSteps(inPos string, keysToFind int, keysFound int, graph d
|
||||
}
|
||||
}
|
||||
|
||||
memo := minStepsMemo{
|
||||
pos: inPos,
|
||||
keysToFind: keysToFind,
|
||||
keysFound: keysFound,
|
||||
dist: int(best),
|
||||
}
|
||||
knownMinimumSteps = append(knownMinimumSteps, memo)
|
||||
return memo.dist
|
||||
knownMinimumSteps[memo] = int(best)
|
||||
return int(best)
|
||||
}
|
||||
|
||||
func (d Day18) buildGraph(pos []day18Vec, keys map[day18Vec]int, doors map[day18Vec]int, grid [][]day18Cell) day18Graph {
|
||||
@ -347,8 +334,9 @@ func (d *Day18) Part2() string {
|
||||
entrances := d.part2PatchMap(grid, d.entrance)
|
||||
// d.Draw(grid, d.keys, d.doors, entrances...)
|
||||
|
||||
knownMinimumSteps = make([]minStepsMemo, 0)
|
||||
knownReachableKeys = make([]reachableKeysMemo, 0)
|
||||
// clear memoized maps that (might have) came from part1
|
||||
knownMinimumSteps = make(map[minStepsMemo]int)
|
||||
knownReachableKeys = make(map[reachableKeysMemo][]u.Pair[rune, int])
|
||||
|
||||
graph := d.buildGraph(entrances, d.keys, d.doors, grid)
|
||||
minSteps := d.minimumSteps("1234", len(d.keys), 0, graph)
|
||||
|
Reference in New Issue
Block a user