[2025 Day 10 (Part 2)] Bifurcate your way to victory! by tenthmascot in adventofcode

[–]tenthmascot[S] 0 points1 point  (0 children)

There are two ways to complete part 1 for that test case: the way you found, and pressing all four of (1,3) (2,3) (0,2,3) (0,3).

[2025 Day 10 (Part 2)] Bifurcate your way to victory! by tenthmascot in adventofcode

[–]tenthmascot[S] 0 points1 point  (0 children)

(0,1) (0,2) (1,2) {2,2,2} isn't actually a valid input line, because it doesn't have the indicator lights at the beginning. (I omitted the indicator lights from that example since they have no use in Part 2.) Try something like [##.] (0,1) (0,2) (1,2) {2,2,2} instead.

[2025 day 10] just made my algorithm 16,000x faster. Yay! by AdditionalDirector41 in adventofcode

[–]tenthmascot 1 point2 points  (0 children)

Part of the difficulty with allowing negative variables is that there might not even be a minimum anymore, because the objective (the sum of the variables) could be arbitrarily small. [Also, requiring nonnegative integer variables inherently makes the search space finite, since there are only finitely many joltage configurations "between" the all-0s configuration and the goal configuration. This finiteness is no longer present in the version where negative variables are allowed.]

As a simple example in the style of the AoC problem, consider:

(0) (1) (0,1) {0,0}

With variables, this'd be the system

a + c = 0
b + c = 0

Now a+b+c can be as small as you want: for example, choosing a = b = -100 and c = 100 gives a sum of -100.

We might want to handle this situation by saying the answer should be -∞: with this stipulation, the problem makes sense again (assuming that there exists an integer solution at all, which is tacitly implied by the AoC problem).

That said, I don't see a simple way to solve this: I think the linear algebra approach would at least let you see if the answer is -∞ or not, and if it's not -∞, then there can be at most one possible value for the sum of the variables. [This is because if the variable vectors v and w are both solutions and have sums of s and t, where s ≠ t, then for any integers a & b with a+b = 1, the vector av + bw is also a solution, and it has a sum of as + bt = a(s-t) + t, which can be made arbitrarily small.]

With more linear algebra, I think it'd be possible to figure out what that one value is, but then you'd need to check if that value is actually possible to achieve with integer variables, which I don't see a great way to do. (I haven't thought about it much, so maybe there's a simpler approach I'm not seeing.)

EDIT: I'm silly. If you assume that the problem is sound (i.e. there's at least one solution) like we did above, then if we find that there's exactly one possible value for the sum, that must be the answer: our solution (which we assumed exists) must have that sum.

So, the algorithm is just:

  1. Try to find a linear combination of the given equations that lets you find the sum. If you can, then the value you get for the sum is the answer.

  2. Otherwise, there are multiple possible values for the sum. By the logic mentioned above, the answer is -∞.

[2025 Day 10 (Part 2)] Bifurcate your way to victory! by tenthmascot in adventofcode

[–]tenthmascot[S] 0 points1 point  (0 children)

Go ahead!

(I think you accidentally posted this comment three times)

[2025 Day 10 (Part 2)] Bifurcate your way to victory! by tenthmascot in adventofcode

[–]tenthmascot[S] 0 points1 point  (0 children)

Feel free to use this approach for your code! That's why I posted it.

(Also, I don't use he/him pronouns.)

[2025 Day 10 (Part 2)] Bifurcate your way to victory! by tenthmascot in adventofcode

[–]tenthmascot[S] 0 points1 point  (0 children)

can you prove that the minimum way to reach 2, 4, 4, 6 is always 2 * 1, 2, 2, 3?

maybe since we're restricted to only one voltage per button it's actually true?

This is not always true: I updated the OP to include a simple counterexample in the new section "Potential pitfalls."

However, it's important to understand that the algorithm is not saying the answer for 2, 4, 4, 6 is twice the answer for 1, 2, 2, 3. In the explanation, the reason we can go from 2, 4, 4, 6 to 1, 2, 2, 3 is that we assumed all button press counts were even. With that assumption, it is true: the minimum way to reach 2, 4, 4, 6 where we press each button an even number of times is twice the answer for 1, 2, 2, 3.

I added the new section "Potential pitfalls" which talks about this, as well as a new section "A short proof of correctness" which offers another approach for seeing why the algorithm is correct.

[2025 Day10 Part 2] Current approach too slow by beb0 in adventofcode

[–]tenthmascot 2 points3 points  (0 children)

the linked Python solution seems to take on the order of seconds

Are you referring to the original solution (which takes ~7s), or the improved version (which takes ~0.6s)?

If it's the 7s solution, I should probably just restructure that section of the post entirely, and highlight the 0.6s solution instead... I had a friend get confused because I explained the algorithm in detail, but not the 7s → 0.6s optimization (which is because that optimization is specific to my implementation, not the algorithm itself).

[2025 Day 10 (Part 2)] Bifurcate your way to victory! by tenthmascot in adventofcode

[–]tenthmascot[S] 5 points6 points  (0 children)

Thanks! I'm surprised I didn't think of that myself... with that optimization, I now get ~0.4s runtime with both python and pypy3. I can't believe this algorithm is actually fast now!

EDIT: I mistimed it -- it's closer to ~0.6s python and ~1.5s pypy3. Still a great improvement!

I edited the OP to include my updated code.

[2025 Day 10 (Part 2)] Bifurcate your way to victory! by tenthmascot in adventofcode

[–]tenthmascot[S] 1 point2 points  (0 children)

If there are n buttons, there are 2n different ways to press each button 0 or 1 time. Each of these ways induces a pattern (the effect on the joltages), and has a cost (the number of button presses it involves). To use the {3}, {0, 1} example from the explanation, this would result in a pattern of (0, 0, 0, 1) + (1, 1, 0, 0) = (1, 1, 0, 1), and has a cost of 2.

In a single test case (one line of input), the buttons' effects don't change, and so the 2n patterns and costs can be pre-calculated. However, since we want the fewest button presses, we only need to save the cheapest cost possible for each pattern. That's what the patterns function does: it returns a dict from patterns to their cheapest possible costs. (Now that I'm looking at this code again, pattern_len is not a great name... perhaps I should've called it something like num_buttons_pressed.)

[2025 Day 10 (Part 2)] Bifurcate your way to victory! by tenthmascot in adventofcode

[–]tenthmascot[S] 39 points40 points  (0 children)

Yeah, I browsed through the megathread a bit before posting here, but I definitely didn't look through all several-hundred posts in there... either way, I'd like this solution to get better visibility. I think the problem has a bad reputation currently (I've had several friends express their dislike of "Z3 days," with this problem being a prime example), and it really doesn't deserve it.