Compare commits

..

No commits in common. "5fd80e56d877c2806f031aa1276eae846df67265" and "a0714336899dbbbe1025eb513efc14aa6b0f6225" have entirely different histories.

4 changed files with 57 additions and 1159 deletions

View File

@ -24,14 +24,6 @@ func main() {
solve("input") solve("input")
} }
// Brilliant insight from a redditor:
// suppose we only want to match the parity of the required joltages.
// we can use our solution for part 1 to find the minimum number of button presses
// to make that happen. now, consider what happens if we press a button twice:
// it won't change the parity at all, only add 2 to some joltages. ok.
// take the 2nd bit of each joltage. what's the minimum number of double-presses
// required to match that pattern? and so on.
func solve(filename string) { func solve(filename string) {
input, err := os.Open(filename) input, err := os.Open(filename)
check(err) check(err)
@ -43,19 +35,14 @@ func solve(filename string) {
line := scanner.Text() line := scanner.Text()
parts := strings.Fields(line) parts := strings.Fields(line)
parts = parts[1:] parts = parts[1:]
var buts []uint32 var jolts []Jolts
var target Jolts
for _, p := range parts { for _, p := range parts {
j, err := parseJolt(p) j, err := parseJolt(p)
check(err) check(err)
if p[0] == '{' { jolts = append(jolts, j)
target = j
} else {
buts = append(buts, j.uint32())
}
} }
fmt.Printf("%v %b %v\n", target, buts, parts) fmt.Printf("%v %v\n", jolts, parts)
n := solveJolts(target, buts) n := best(jolts[len(jolts)-1], jolts[:len(jolts)-1])
fmt.Printf("%s = %v\n", line, n) fmt.Printf("%s = %v\n", line, n)
total += n total += n
//fmt.Println(n) //fmt.Println(n)
@ -102,89 +89,62 @@ func parseJoltByValue(s string) (Jolts, error) {
return j, nil return j, nil
} }
func (j *Jolts) uint32() (u uint32) { // An CostHeap is a min-heap of ints.
for i, v := range j { type CostHeap struct {
if v != 0 { heap []Jolts
u |= uint32(1) << i cost map[Jolts]int
}
}
return u
} }
func (j Jolts) Sub(m uint32, value int) Jolts { func (h *CostHeap) Len() int { return len(h.heap) }
for i := range j { func (h *CostHeap) Less(i, j int) bool { return h.cost[h.heap[i]] < h.cost[h.heap[j]] }
if m>>i&1 != 0 { func (h *CostHeap) Swap(i, j int) { h.heap[i], h.heap[j] = h.heap[j], h.heap[i] }
j[i] -= int16(value)
} func (h *CostHeap) Push(x any) {
} // Push and Pop use pointer receivers because they modify the slice's length,
return j // not just its contents.
h.heap = append(h.heap, x.(Jolts))
} }
func (j Jolts) Valid() bool { func (h *CostHeap) Pop() any {
for i := range j { old := h.heap
if j[i] < 0 { n := len(old)
return false x := old[n-1]
} h.heap = old[0 : n-1]
} return x
return true
} }
// TODO: this isn't quite enough. we need to know not only how many button presses // it doesn't matter what order we push the buttons in --
// a mask can be solved in, but also *the specific buttons* -- because even though // only how many times we push each one.
// each button is pressed at most once, two buttons can be connected to the same
// output so the joltages can be between like 0-10. this needs to be subtracted
// from the target, and two different button patterns can have different effects
// on the target.
//
// the good news is that we can cache and reuse the cost map computed in best(),
// since it depends only on the button wiring. all that's left after that is a
// graph search through the state space, using masks to prune each level.
func solveJolts(target Jolts, pool []uint32) int { func best(target Jolts, pool []Jolts) int {
var bits uint32 var cost = make(map[Jolts]int)
for _, x := range target { var states []Jolts
bits |= uint32(x) states = append(states, Jolts{})
} cost[Jolts{}] = 0
var answer int for pi, p := range pool {
for bit := uint(0); bits>>bit > 0; bit++ {
var mask uint32
for i := range target {
mask |= uint32(target[i]) >> bit << i
}
if mask == 0 {
continue
}
n := best(mask, pool)
answer += n << bit
//for i, p := range pool {
// if mask>>i&1 != 0 {
// target.Sub(p, 1)
// }
//}
}
return answer
}
func best(target uint32, pool []uint32) int {
var cost = make(map[uint32]int)
var states = []uint32{0}
cost[0] = 0
for _, p := range pool {
// enumerate all the states that can be reached by toggling button p // enumerate all the states that can be reached by toggling button p
for _, s := range states { queue := states
t := s ^ p fmt.Print(pi, len(states), len(queue))
c := cost[t] + 1 for _, s := range queue {
here:
if cost_t, ok := cost[t]; ok { for t, c := s, cost[s]; ; {
if c < cost_t { for i, v := range p {
cost[t] = c t[i] += v
if t[i] > target[i] {
// blown target, cut path
break here
}
}
c += 1
if cost_t, ok := cost[t]; ok {
if c < cost_t {
cost[t] = c
}
} else {
cost[t] = c
states = append(states, t)
} }
} else {
cost[t] = c
states = append(states, t)
} }
} }
} }

View File

@ -15,27 +15,9 @@ def solve(input):
M = sympy.Matrix(matrix + [target]).T M = sympy.Matrix(matrix + [target]).T
#print(repr(M)) #print(repr(M))
S, pivots = M.rref() S, pivots = M.rref()
if len(pivots) == BTNS: if len(pivots) < BTNS:
# solved
presses = S.col(-1)
print("*", target, presses.T, sum(presses))
part2 += sum(presses)
else:
# not solved # not solved
# the system of equations is underdetermined, either because print(repr(S))
# we started with too few equations or because some of them were
# not linearly independent.
# the upshot is that we have one or more free variables
# (in practice 1-3 free variables) so if we just iterate
# through all legal values for those variables we should
# be able to find a solution?
# unfortunately i am still running into some unsolveable cases
# and i'm not sure why.
#if BTNS - len(pivots) >= 3:
# print(repr(S))
#continue
coords = [] coords = []
limits = [] limits = []
extra_rows = [] extra_rows = []
@ -64,6 +46,10 @@ def solve(input):
part2 += min(totals) part2 += min(totals)
else: else:
print("uhoh", target) print("uhoh", target)
else:
presses = S.col(-1)
print("*", target, presses.T, sum(presses))
part2 += sum(presses)
print(part2) print(part2)
#print(less, greater) #print(less, greater)

View File

@ -1,18 +0,0 @@
sizes = [7, 7, 7, 6, 7, 5]
t = 0
for line in open("input"):
if 'x' in line:
line = line.strip()
s = line.replace("x"," ").replace(":","").split()
n = map(int, s)
w, h, *n = n
need = sum(sizes[i]*x for i,x in enumerate(n))
if need > w*h:
print(line, "impossible")
else:
print(line, "<>", w*h - need)
t += 1
print(t)

File diff suppressed because it is too large Load Diff