11
votes
Day 13: Shuttle Search
Today's problem description: https://adventofcode.com/2020/day/13
Join the Tildes private leaderboard! You can do that on this page, by entering join code 730956-de85ce0c
.
Please post your solutions in your own top-level comment. Here's a template you can copy-paste into your comment to format it nicely, with the code collapsed by default inside an expandable section with syntax highlighting (you can replace python
with any of the "short names" listed in this page of supported languages):
<details>
<summary>Part 1</summary>
```python
Your code here.
```
</details>
I'm just going to post part A for now, I'm still wrapping my head around the math for part B. This might take a bit.
Edit: After ramming my head against it for a while, I finally figured it out. I ALMOST had to turn off the eurobeat to help myself concentrate, but thankfully that wasn't necessary.
Day 13 Part A – Python
This one was pretty easy for me. I just just had each bus loop until I could see the first value AFTER the earliest arrival time, then kept the 'best' answer from there.
Day 13 Part B – Python
Figuring out how to do it in the first place was relatively simple. Figuring out how to get the computation done before the end of the month was trickier. I spend a while chasing a fancy mathematical solution, and that was definitely helpful, but it would have taken several hours to understand and implement. If I'm being honest, it's still a bit hard to understand why exactly it works, but here goes:
When I have bus A and B, and they have no offset, I KNOW that they sync up every A * B steps. If they have an offset, then that offset will be exactly the same every A * B steps.
I start with a group of synced busses. It starts empty (or with a ghost bus with an interval of 1).
For each bus, I move the synced bus group until the new bus is at the appropriate offset.
When I find that offset, I add that bus to the synced bus group, and increase the 'speed' of all the synced busses by that busses speed.
So after bus 1 it synced busses just has bus A in it
After bus 2 it is moving at speed A * B
After bus 3 it's moving at speed A * B * C
Etc...
Once all the busses are synced, I have the answer
Tips and Commentary
This one might be really difficult, depending on your approach
For the love of god DO NOT BRUTE FORCE part B. My answer was on the order of 700 trillion
There are fancy fancy mathy ways to solve it. I couldn't wrap my head around the equations after midnight though, but reading up on the concepts did help me eventually develop my solution
If you don't go for a purely math solution, figuring out how to speed up your search is extremely important.
Try breaking the problem up into smaller, more manageable portions. For example, can you solve the problem for just 2 busses at a time? And can you think of a way to treat 2 busses as 1 bus?
All my busses were prime numbers. I have reason to believe that everyone else's will be as well. Knowing they were primes was sort of helpful, but looking at the prime factorization was mostly a dead end for me.
Python
Repo Link
Part 1
The first part was relatively straightforward: take the ceiling of the arrival time (first number) divided by the bus_id of each bus. From there, you just take the minimum of each of these ceilings to get the closest departure time.
Part 2
The second part took me a lot longer to understand conceptually, but turned out to be not a lot of code. The key here is to realize (as mentioned by others) that once a set of busses are synchronized, they will remain synchronized for the product of their ids. So the idea is to simply synchronize one bus at a time, keeping in mind the offset from the initial bus.
For instance, given
17,x,13,19
, we would first try to synchronize17
and13
where13
has an offset of2
(its position in the input).Start from
17
, we initially count upwards by17
until we reach102
. Here we can see that102 + 2
is a multiple of13
, therefore we have found the synchronization point for these two numbers. From now on, these two busses will be synchronized every additional17*13
timesteps. That is, at102 + 17*13 = 323
, bus13
will be2
timesteps away from bus17
Therefore, our next increment should be
17*13 = 221
and we will use that find the synchronization point with the final bus,19
.I had to work this out on paper a few times to confirm it works, but once I saw the pattern, the code was pretty straightforward. The multiplying of the increment (which works because the synchronization cycles) is what makes the search efficient (runs in milliseconds).
Ruby
Like many others, part 2 took me a lot longer than part 1.
Part 1
Part 2
This puzzle reminded me straight away of 2019's [day 16](https://adventofcode.com/2019/day/16) and [day 22](https://adventofcode.com/2019/day/22), which also both involved enormous numbers.I started by looking at the GCD/LCM of the bus ids, and quickly wrote a dumb brute force search (which was obviously too slow), played around with using various combinations of LCM and modulus to make the search faster (this is where I realized that all the bus ids were prime) and got stuck for a good while. Thinking about primes and modulus, plus some half-remembered college material, lead me (after walking away for a bit and coming back with fresh eyes) to Residue Number Systems and the Chinese Remainder Theorem, which provided the correct solution I'd been fumbling towards.
The key observation, as mentioned in the other posts, is that the times/solutions for each subset of buses ([bus1], [bus1, bus2], [bus1, bus2, bus3], etc) will repeat at intervals of their least common multiples (which, given that they're all prime, is just the product, though my solution leaves in the LCM call from an earlier version). This means that you can start at the first bus, and then search forward by steps of that bus's id until you find a match for the next bus, change your step size to the LCM of the solved bus ids, proceed until you find a match for the next, and so on until all the buses are solved.
I'm not sure about part 2... but here's part 1 in Sheets!
Part 1
I was late starting tonight but made ~7000, which is within 2k of my best and well under my average.
ok, Part 2 is done -- but Google Sheets cannot do the math. The result Sheets spits out is 1,118,684,865,113,060 where the proper answer is 1,118,684,865,113,056. However, even posting this proper answer into Sheets, it'll change it to 1,118,684,865,113,050 after formatting it. When you check the difference between the formatted correct answer and the answer it spits out, it says there's a difference of 7, when in fact there should be a difference of 4.
This one was a doozy. Part 1 was not bad but part 2 I tried several things that did not work. I looked at it as linear algebra, as a linear optimization problem, as something I could do recursively with a simpler method, and eventually got close based on hints from @PapaNachos, and then just had to look at a solution to get over the last hump. My final solution is pretty speedy (mean time of 380.573 μs over 10,000 samples) but wow I would not have gotten there on my own.
Part 1
Part 2 diff
I had originally sorted the bus IDs to get the biggest ones first, in the hope that would get a larger step size early on and lead to faster runtime. In the end it was actually a bit slower so I left them as is.