use a heap for marginally faster execution

This commit is contained in:
2024-12-16 09:53:09 -08:00
parent 9bb5d1f769
commit b016717754
3 changed files with 98 additions and 40 deletions

View File

@@ -58,7 +58,7 @@ struct Maze : CustomStringConvertible {
let s: [[String]] = walls.enumerated().map { i, r in let s: [[String]] = walls.enumerated().map { i, r in
[String(format: "%3d ", i)] + r.map { $0 ? "██" : " " } [String(format: "%3d ", i)] + r.map { $0 ? "██" : " " }
} }
print(" 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4") print(" " + (0..<w).map { " \($0 % 10)" }.joined())
return s.map { $0.joined() }.joined(separator: "\n") return s.map { $0.joined() }.joined(separator: "\n")
} }
init(fromFile f: String) throws { init(fromFile f: String) throws {
@@ -111,10 +111,10 @@ struct Maze : CustomStringConvertible {
let edges = graph() let edges = graph()
// edges.forEach { k, v in print(" - \(k): \(v)") } // edges.forEach { k, v in print(" - \(k): \(v)") }
var q: [CellCost] = [(cell: start, cost: 0)] var q = Heap<CellCost>(comparator: { $0.cost < $1.cost })
q.insert((cell: start, cost: 0))
var visited: [Cell:Int] = [:] var visited: [Cell:Int] = [:]
while !q.isEmpty { while let e = q.pop() {
let e = q.removeLast()
if let node = edges[e.cell] { if let node = edges[e.cell] {
for (cell, cost) in node { for (cell, cost) in node {
if let prevCost = visited[cell] { if let prevCost = visited[cell] {
@@ -123,7 +123,7 @@ struct Maze : CustomStringConvertible {
} }
} }
visited[cell] = e.cost + cost visited[cell] = e.cost + cost
q.append((cell: cell, cost: e.cost + cost)) q.insert((cell: cell, cost: e.cost + cost))
} }
} }
} }
@@ -131,6 +131,12 @@ struct Maze : CustomStringConvertible {
} }
} }
@main
struct AoC {
static func main() throws {
let maze = try Maze(fromFile: CommandLine.arguments[1]) let maze = try Maze(fromFile: CommandLine.arguments[1])
// print(maze) print(maze)
print(maze.search()) print("minCost: \(maze.search())")
}
}

View File

@@ -1,6 +1,7 @@
import Foundation import Foundation
typealias CellCost = (cell: Cell, cost: Int) typealias CellCost = (cell: Cell, cost: Int)
typealias PathCostFromStart = [Cell:(Int, [Cell])]
enum Dir : String, CaseIterable, CustomStringConvertible { enum Dir : String, CaseIterable, CustomStringConvertible {
case n = "" case n = ""
@@ -35,34 +36,25 @@ struct Cell : Hashable, CustomStringConvertible {
} }
} }
struct QueueEntry : Hashable, CustomStringConvertible { struct QueueEntry : Hashable {
let cell: Cell let cell: Cell
let origin: Cell let origin: Cell
let costSoFar: Int let costSoFar: Int
let path: [Cell] let path: [Cell]
var description: String { return "\(cell) from \(origin) @\(costSoFar)" }
} }
struct CellPair : Hashable, CustomStringConvertible { struct CellPair : Hashable {
let a: Cell let a: Cell
let b: Cell let b: Cell
var description: String { return "\(a),\(b)" }
} }
struct Maze : CustomStringConvertible { struct Maze {
let walls: [[Bool]] let walls: [[Bool]]
let (w, h): (Int, Int) let (w, h): (Int, Int)
var start: Cell { return Cell(i: h-2, j: 1, dir: Dir.e) } var start: Cell { return Cell(i: h-2, j: 1, dir: Dir.e) }
var ends: Set<Cell> { var ends: Set<Cell> {
return Set(Dir.allCases.map { Cell(i: 1, j: w-2, dir: $0) }) return Set(Dir.allCases.map { Cell(i: 1, j: w-2, dir: $0) })
} }
var description: String {
let s: [[String]] = walls.enumerated().map { i, r in
[String(format: "%3d ", i)] + r.map { $0 ? "██" : " " }
}
print(" 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4")
return s.map { $0.joined() }.joined(separator: "\n")
}
init(fromFile f: String) throws { init(fromFile f: String) throws {
let content = try String(contentsOfFile: f, encoding: .ascii) let content = try String(contentsOfFile: f, encoding: .ascii)
let lines = content.split(separator: "\n") let lines = content.split(separator: "\n")
@@ -114,11 +106,11 @@ struct Maze : CustomStringConvertible {
} }
func search(graph edges: [Cell:[CellCost]], start: Cell) -> [Cell:(Int, [Cell])] { func search(graph edges: [Cell:[CellCost]], start: Cell) -> PathCostFromStart {
var q: [CellCost] = [(cell: start, cost: 0)] var q = Heap<CellCost>(comparator: { l, r in l.cost < r.cost })
var visited: [Cell:(Int, [Cell])] = [:] q.insert((cell: start, cost: 0))
while !q.isEmpty { var visited: PathCostFromStart = [:]
let e = q.removeLast() while let e = q.pop() {
if let node = edges[e.cell] { if let node = edges[e.cell] {
for (cell, cost) in node { for (cell, cost) in node {
let newCost = e.cost + cost let newCost = e.cost + cost
@@ -132,14 +124,14 @@ func search(graph edges: [Cell:[CellCost]], start: Cell) -> [Cell:(Int, [Cell])]
} }
} }
visited[cell] = (newCost, [e.cell]) visited[cell] = (newCost, [e.cell])
q.append((cell: cell, cost: newCost)) q.insert((cell: cell, cost: newCost))
} }
} }
} }
return visited return visited
} }
func trace(_ visited: [Cell:(Int, [Cell])], from: Cell, to: Cell) -> [[Cell]] { func trace(_ visited: PathCostFromStart, from: Cell, to: Cell) -> [[Cell]] {
if from == to { if from == to {
return [[from]] return [[from]]
} }
@@ -148,6 +140,9 @@ func trace(_ visited: [Cell:(Int, [Cell])], from: Cell, to: Cell) -> [[Cell]] {
} }
} }
@main
struct AoC {
static func main() throws {
let maze = try Maze(fromFile: CommandLine.arguments[1]) let maze = try Maze(fromFile: CommandLine.arguments[1])
let (graph, pathPairs) = maze.graph() let (graph, pathPairs) = maze.graph()
let visited = search(graph: graph, start: maze.start) let visited = search(graph: graph, start: maze.start)
@@ -161,3 +156,6 @@ let seats = maze.ends
.map { $0.map { cell in Cell(i: cell.i, j: cell.j, dir: Dir.n) } } .map { $0.map { cell in Cell(i: cell.i, j: cell.j, dir: Dir.n) } }
.flatMap { $0 } .flatMap { $0 }
print("seats: \(Set(seats).count)") print("seats: \(Set(seats).count)")
}
}

54
day16/heap.swift Normal file
View File

@@ -0,0 +1,54 @@
struct Heap<T> {
var comparator: ((_ l: T,_ r: T) -> Bool)
var heap: [T] = []
var isEmpty: Bool { return heap.isEmpty }
private mutating func bubbleUp(idx: Int) {
let parent = (idx - 1) / 2
if idx <= 0 {
return
}
if comparator(heap[idx], heap[parent]) {
heap.swapAt(parent, idx)
bubbleUp(idx: parent)
}
}
private mutating func heapify(_ idx: Int) {
let left = idx * 2 + 1
let right = idx * 2 + 2
var comp = idx
if heap.count > left && comparator(heap[left], heap[comp]) {
comp = left
}
if heap.count > right && comparator(heap[right], heap[comp]) {
comp = right
}
if comp != idx {
heap.swapAt(comp, idx)
heapify(comp)
}
}
mutating func insert(_ item: T) {
heap.append(item)
bubbleUp(idx: heap.count-1)
}
mutating func pop() -> T? {
let item: T? = heap.first
if heap.count == 0 {
return nil
}
if heap.count > 1 {
heap[0] = heap[heap.count-1]
heap.removeLast()
heapify(0)
} else if heap.count == 1{
heap.removeLast()
}
return item
}
}