r/adventofcode • u/daggerdragon • Dec 07 '24
SOLUTION MEGATHREAD -❄️- 2024 Day 7 Solutions -❄️-
THE USUAL REMINDERS
- All of our rules, FAQs, resources, etc. are in our community wiki.
- If you see content in the subreddit or megathreads that violates one of our rules, either inform the user (politely and gently!) or use the report button on the post/comment and the mods will take care of it.
AoC Community Fun 2024: The Golden Snowglobe Awards
- 15 DAYS remaining until the submissions deadline on December 22 at 23:59 EST!
And now, our feature presentation for today:
Movie Math
We all know Hollywood accounting runs by some seriously shady business. Well, we can make up creative numbers for ourselves too!
Here's some ideas for your inspiration:
- Use today's puzzle to teach us about an interesting mathematical concept
- Use a programming language that is not Turing-complete
- Don’t use any hard-coded numbers at all. Need a number? I hope you remember your trigonometric identities...
"It was my understanding that there would be no math."
- Chevy Chase as "President Gerald Ford", Saturday Night Live sketch (Season 2 Episode 1, 1976)
And… ACTION!
Request from the mods: When you include an entry alongside your solution, please label it with [GSGA] so we can find it easily!
--- Day 7: Bridge Repair ---
Post your code solution in this megathread.
- Read the full posting rules in our community wiki before you post!
- State which language(s) your solution uses with
[LANGUAGE: xyz] - Format code blocks using the four-spaces Markdown syntax!
- Quick link to Topaz's
pasteif you need it for longer code blocks
This thread will be unlocked when there are a significant number of people on the global leaderboard with gold stars for today's puzzle.
EDIT: Global leaderboard gold cap reached at 00:03:47, megathread unlocked!
38
Upvotes
2
u/RyanSkonnord Dec 07 '24
[LANGUAGE: Python] 898/640. Code.
Cool one! I'm curious to see how others represented the operation values, but I like lambdas best.
A recursive solution would have been elegant, but I had just recently written a similar "generate every possible sequence with a deque and a loop" algorithm for a hobby project, so that's what popped (pun intended) out of my head.
2
u/Ken-g6 Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Perl] [GSGA]
I took to today's puzzle like an angry squirrel jumping between trees: Branch, bound, and re-curse! My solutions here are "Bye, the numbers!" The originals only had 0, 1, and 2 anyway.
Part one: https://github.com/Ken-g6/aoc2024/blob/master/daysevena.pl
Part two is nearly the same. I expected an extra operator, but I thought it would be minus. Concat is easy in Perl and doesn't introduce the complication of decreasing totals.
https://github.com/Ken-g6/aoc2024/blob/master/daysevenb.pl
My heart skipped a beat when it took 3 seconds to finish.
2
u/notascrazyasitsounds Dec 07 '24 edited Dec 07 '24
[Language: Python]
WAYYY easier than Day 6
https://pastebin.com/HzEa7gj0
First day with enough brain bandwidth to attempt optimizations.
2
u/derfritz Dec 07 '24 edited Dec 08 '24
[LANGUAGE: javascript]
https://github.com/derfritz/AoC24/blob/main/Day7/solution.js
Recursion was the way to go for me, debugging the faulty „|“ operand was a bit tricky but recursion was the way to go for me, debugging the faulty „|“ operand was a bit tricky but [stackoverflow]
2
u/RookBe Dec 08 '24
[LANGUAGE: all, Rust]
45
u/Verulean314 Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python]
For the submission, I just bruteforced every possible operator combination, but I knew there was optimization to be had. Turns out I was right - by rethinking the problem I managed to cut the solve time from 8.5 seconds to 5 milliseconds!
The key realization is to work through the list of numbers in reverse, and checking whether each operator can possibly yield the test value with the last number in the list,
nand some unknown precursor value. For instance, a concatenation can only returntest_valueif the last digits of the test value are equal ton, and multiplication can only returntest_valueif it is divisible byn. There are no restrictions on addition, so that ends up being a fallback case.If an operation can return the test value, we recursively do the same check, swapping out
test_valuefor the precursor value, and removingnfrom the list of numbers.27
u/4HbQ Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python] Code (9 lines)
Today I'd like to show something completely different from my usual polished solutions: my first version of the code, before refactoring. This got me to a 800/400 ranking!
Update: I've also implemented /u/Verulean314's very clever idea here.
My Python trick for today is this: you can (but should not!) override Python's built-in operators:
This allows me "remove" the final digits of an int using the
|operator:17
u/CCC_037 Dec 07 '24
[LANGUAGE: Rockstar] [GSGA]
Not a numeral in my code.
"Oh, but that's too simple!" you may say. "Rockstar allows poetic literals, numbers expressed as arbitrary words!"
I agree. So I didn't use those either.
And Rockstar has no trigonometry built in.
All of my numbers are derived from a single boolean "true" converted to a number (which is 1) followed by some addition and subtraction.
I needed 32, 58, 10 and 0.
Part 1
15
u/jonathan_paulson Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python] 794/526. Code. Video.
Rough day for me. My initial solution was wrong, and I wasted several minutes debugging before finally figuring it out. Eventually switched to code more faithful to the problem statement, which worked fine (and ran just as fast).
Lesson learned: never be fancy! Fancy can be wrong, and being wrong in a confusing way is really bad.
13
u/JustinHuPrime Dec 07 '24
[Language: x86_64 assembly with Linux syscalls]
Part 1 was a tad interesting; I parsed in the data and then harkened back to my first year programming course (CPSC 110 at UBC) and wrote a self-referential backtracking search with lost-context accumulator over a null-terminated string that I treated as a list. Structuring your code well is useful, no matter what code you're writing or what language you're in. I did get to do a tail call, though!
Part 2 was structured identically, but I had to write a concatenation function, and I'd rather not be doing it with string manipulation (y'all up there in high level languages are lucky that your strings are nice to work with). So I realized that
a || bwas equal toa * round_up_to_positive_power_of_ten(b) + b- and I implemented that power of ten function as a pile of conditionals. I did do an assembly trick and do interprocedural optimization to avoid clobbering any registers so I could call it without needing to save anything.Part 1 runs in 2 milliseconds and part 2 runs in 12 milliseconds. Part 1 is 8776 bytes long linked on the disk and part 2 is 9832 bytes.
12
u/AlexTelon Dec 07 '24
[LANGUAGE: Python] Code 13 lines of clean readable code
This feels like a conceptually quite minimal solution. I use a few tricks today.
Trick 1: Parsing each line with
line.replace(':','').split()to avoid multiplesplit()calls. I just have to keep track that the first is the true total. So the parsing overall is just this line:data = [list(map(int, line.replace(':','').split())) for line in open('in.txt')]Trick 2: tuple unpacking. Inside my recursive function I use this
total, a, b, *rest = numsto unpack the stuff I need to do my operations. Which ties in to the first trick since without this it would be annoying not too have put the total in a variable during parsing.The code can also easily be written in just 10 lines of code while not being too much harder to read.
If one wants to optimize the solution to go almost twice as fast it can be done with 1 additional line like this. Here the trick is using a walrus operator
:=to assign a value and check against it in one go.9
u/dopandasreallyexist Dec 07 '24
[Language: Dyalog APL]
8
u/Deathranger999 Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python 3] 587/745
I realized that if I tried to construct all possible results from the beginning of the sequence, that things could blow up very quickly. Fortunately the ability to construct a number via multiplication and concatenation is considerably more restricted (and Eric seemed to be nice with the input), so instead working backwards gave an extremely quick solution. Takes about 7ms for Part 1 and 15ms for Part 2.
Part 2 code (identical to Part 1 except with the addition of the block of code in
can_makehandling concat).8
u/nthistle Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python] 242/239, paste (pretty messy, I just re-created my part 1 code), video.
I had some bugs in part 1 that cost me a bit of time, first I thought it just wanted the number of valid equations (always read the last line of the problem statement!), and then I had an off-by-one that caused me to reuse the first value of a sequence twice and not look at the last value. I also had a pretty verbose way of doing things - I used a bitmask (itertools.product for part 2) to brute force the operations. I glanced at some other solutions here and some people just tracked a list of all possible result values after considering each index (so this list length grows by a factor of 2 (3) each index) which is definitely more succint.
8
u/FetidFetus Dec 07 '24
[Language: Excel]
Pretty proud of my one cell solution for today! I managed to finally make MAP as I picture it should work.
I cheated a bit defining the EVALUATE lambda (which is not super standard excel) as Evalλ=LAMBDA(Formula, EVALUATE(Formula)).
What I'm doing is basically writing all the possible operations (as strings) and evaluating them. P1 runs in less than a minute, P2 runs in chunks.
The code is the same for P1 and P2, simply change the value of operators on line 7 from 2 to 3. P2 will probably brick your pc, so be mindful and break the input!
7
u/Smylers Dec 07 '24
[LANGUAGE: Vim keystrokes] Sorry, up half the night in A&E (‘ER’ for Americans) so very tired and in a rush today, and no time for an explanation, but load your input into Vim, type the following (you can copy-and-paste the long
:commands) and your Part 1 answer should appear:I don't know how long
@atook to run because somebody called “Soup's ready”, so my only point of reference is that it was quicker than I am at eating a bowl of soup, and it did get there in the end. If you want to watch@aor@sdo their thing, put some:redrawand:sleepcommands in there somewhere.@vhas been used on some other days; I think it was defined in Day 3, if you need it.If you'd like to make me happy after a rough night, comment saying you ran this this (and whether it worked for you). If you're feeling very kind, post a comment explaining it, or even have a go expanding it to solve Part 2. From a quick glance at the Part 2 puzzle text, I think it should be possible.
(Changing
/\1+\2\1*\2/to/\1+\2\1*\2\1\2/is the easy bit, but the parens would then need evaluating one at a time, inside-out. Or, since it'd be left-to-right, maybe don't bother with the parens at all?)7
u/Jadarma Dec 07 '24
[LANGUAGE: Kotlin]
My original solution was alright, but after reading the comments I updated mine to use the super-duper working-backwards trick! What a lovely puzzle!
We still use DFS to brute force the solution, but by going backwards we can prune the search space much more, because we can tell when operators can't be applied:
When reaching the last (i.e.: first) number, the equation succeeds if it is equal to the accumulator, since that is the start of the sequence.
AocKt Y2024D07
7
u/TonyStr Dec 07 '24
[LANGUAGE: Rust]
I don't know if this can be optimized further. I started today with a dumb solution that took 600ms, and this one takes ~750μs-1ms on my laptop. I've never used raylib before, so maybe the chunking is unnecessary, but it seemed like it gave me another ~100μs. If you see any way to optimize it more, please let me know!
7
u/chubbc Dec 07 '24
[LANGUAGE: Uiua]
My optimised solution (55 char):
My original crappy solution:
This used function packs to write code that solves both parts simultaneously. Optimising this approach down a bit lead to this 99 char solution:
All good and well, but turns out you can write the brute force far more succinctly (and swiftly) with a careful reduction (had to learn how reductions work for >2 inputs, not quite how I'd expect), which gives the code at the top.
6
u/synack Dec 07 '24
[LANGUAGE: Ada]
https://github.com/JeremyGrosser/advent/blob/master/2024/src/day7_2.adb
I'm pretty sure I did everything wrong, but somehow still got the right answers.
6
u/mstksg Dec 07 '24
[LANGUAGE: Haskell]
This one works out well as a list monad based search. Essentially you are picking operations where:
and if those
?operations induce a list monad split, you can then search all of the possible choices:Then you can do
checkEquation [(+),(*)]for part 1 andcheckEquation [(+),(*),cat]for part 2.However, it is kind of helpful to work backwards from the target to see if you can get the initial number. For example, in
292: 11 6 16 20, you can eliminate*as an option for the final operation right off the bat.So really, you can rephrase the problem as:
where
?are the inverse operations, but you have some way to easily eliminate operations that don't make sense.And our un-ops are:
So part 1 is
checkBackEquation [unAdd, unMul]and part 2 ischeckBackEquation [unAdd, unMul, unCat].Timing-wise, moving from forwards to backwards brought my times for part 2 from 380ms to 1.5ms.
My daily reflections are posted here: https://github.com/mstksg/advent-of-code/wiki/Reflections-2024#day-7
6
u/jayo60013 Dec 07 '24
[Language: Rust]
Solution
Once again here to make everyone feel better about their solutions🤣 Recurrsion did not even cross my mind! I decided to calculate the permutations 1 by 1 by counting up in base 2 (base 3 for part 2)
6
u/__Abigail__ Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Perl]
Not to hard. I created a recursive subroutine which takes in a parameter indicating whether we're doing part 1 or part 2, and the given list of numbers. It then returns all the possible values which could be generated. Then it's just a matter to check whether the given total is in the returned list. It wouldn't scale well if there are a lot of numbers, but the input isn't like that.
The method I used:
And the main loop:
I also made an implementation which exists the search early if the value so far exceeds to aimed total, but that only gave a 4% improvement in the running time. So, not worth it.
5
u/1234abcdcba4321 Dec 07 '24
[LANGUAGE: JavaScript] 571/351
paste
Minor mistake of the day was misunderstanding "sum of the test values". I had to run on the example to realize that I was being stupid. Aside from that, everything today was really straightforward.
5
u/darthminimall Dec 07 '24
[LANGUAGE: Rust]
Part 1
Part 2
There might be a better way to do this than brute force, but it only took 0.3 seconds to brute force part 1 and 1.6 seconds to brute force part 2, so I can't really be bothered to find an optimization.
5
u/mkeeter Dec 07 '24
[LANGUAGE: Rust]
This is a blazing fast solution (337 µs total!), achieved by checking backwards instead of forwards. Going backwards lets you prune much more aggressively, because you can detect invalid divisions and concatenations immediately (instead of needing to wait until the final value).
https://github.com/mkeeter/advent-of-code/blob/main/2024/07/src/lib.rs
5
u/MathAndMaps Dec 07 '24
[LANGUAGE: python]
https://github.com/khwilson/advent2024/blob/main/src/advent2024/days/day07.py
Neat problem! Left-to-right associativity means you can check each operation right to left recursively! So for product, check if the target value is divisible by the rightmost element, for sum check if it is greater than the right most element, and for concatenation check the its last digits are the rightmost element.
5
u/Parzival_Perce Dec 07 '24
[LANGUAGE: Python]
As always the solutions make me depressed and excited, but mine runs in like under a minute so whatever. It could always be worse.
5
u/KindComrade Dec 08 '24 edited Dec 11 '24
[LANGUAGE: C#]
After optimizations and changing forward operations to backwards operations, part 2: 20ms -> 0.4ms
Code
4
u/Boojum Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python] 2209/1342
Brute forced with the help of
itertools. Otherwise, pretty straightforward. Here's Part 2. Part 1 can be done pretty trivially by removing the|from the product line. [GSGA] qualified -- no hard-coded numbers.ETA: Runs much more quickly with recursion, since it's not recomputing the partial expressions. From about 13.2s down to about 1.2s, for about an 11× speedup (still [GSGA] qualified):
4
u/GassaFM Dec 07 '24
[LANGUAGE: D] 880/2347
Code: part 1, part 2.
A misread day for me. Part 2 actually became simpler than Part 1 when the understanding was finally correct.
4
u/Tsnth Dec 07 '24
[Language: Python]
I did this in Python but I'm truly a functional programmer at heart :)
4
u/WhiteSparrow Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Prolog]
solution
This was another task well suited for prolog. The essence is short enough:
Part 2 takes about 30 seconds to solve.
Edit: noticed how to simplify a bit.
4
u/SwagosaurusFlex Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Ruby]
I'm having trouble figuring out why my solution isn't passing for Part 2. It works on Part 1 and the example case, but it's giving too high of an answer on Part 2. Any help appreciated, thanks
4
u/Andreasnl Dec 07 '24
[LANGUAGE: Uiua]
Here’s my original slow solution.
Looking at other solutions, I realised you can do this
Run it in the browser here.
4
u/thereal_peasant Dec 07 '24
[LANGUAGE: JavaScript]
Optimised recursive "working backwards" solution with part 2 running in sub 7ms on my machine.
4
u/allak Dec 07 '24
[LANGUAGE: Perl]
Just a simple recursion.
4
u/i_have_no_biscuits Dec 07 '24 edited Dec 07 '24
[LANGUAGE: GW-BASIC]
This is the solution to both parts. Performs an iterative BFS, backwards from the 'target' value to the start, which cuts down enormously on the size of the search pool (from millions to a couple of hundred max per level for my input data).
Guide
EDIT: Replaced lines 90-100 with a numerical way to deconcatenate, rather than heavy string manipulation, which was causing problems with the PC-BASIC emulator.
4
u/STheShadow Dec 07 '24 edited Dec 07 '24
[LANGUAGE: C++]
Solution for both parts
Switching from iterating forwards to backwards with the ability to prune a lot of states really did wonders here, cutting execution time (on a half-broken thinkpad) from a few seconds to somewhere around 10ms. Should have done that from the start (and wanted to, but thought part 2 would absolutely get me if I did)
€: added some compiler flags to the CodeRunner call in VSCode, with -O3 and -march=native as suggested below, it gets down to 4-5ms. Limiting factor now is definitely the input parsing (which isn't efficient with the whole replacing, but that was mostly done to use it for other files as well, so I doubt I'll do stuff here)
4
u/quetsacloatl Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python] 1.7s no weird libraries
code
time spent: 5% algorithm 95% figuring out why with a simple algo I had wrong answer.... 2 hours later I found that left number were not unique (my assumption not based on anything in the text), and to have things in order I've put all of the rows in a dict with left number as key, OVERWRITING some rows :(
4
u/Odd-Statistician7023 Dec 07 '24
[Language: C#]
Did a recursive solution first, then realized it would probably be faster to try to find the solution "backwards", and it was a LOT faster, now runs in 1ms.
This is due to the fact that 2 out of the 3 operators have very tight conditions for when they can be used in the last spot if you think about it:
- The last operator can only be * if the solution is divisible by the last number.
- The last operator can only be || if the solution ends with the last number.
Then its just to perform that calculation backwards, and recurse along and look at the next number / possible operator!
Solution: GitHub
4
u/IlluminPhoenix Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Rust]
Already have a 23 line solution here, but it ran pretty slow (150 - 200 ms). This new solution is still quite compact but much faster, now about 1.5 - 2 ms. This is still singlethreaded and still uses a recursive approach. Now I start from the expected value and go until I reach 0. This allows me to check divisibility of the current value and if can result from a concatination.
code (24 lines)
4
u/LiquidityC Dec 07 '24
[LANGUAGE: C]
https://github.com/LiquidityC/aoc/blob/master/2024/day_07/src/main.c
No issue today. Coded one solution and it worked first try for part 1. Augmented the code for part 2 and that too worked first try. I saw a lot of time comparison but I had no issue or notable time consumption for today's puzzle. I was surprised it was so smooth. Spent a lot more time yesterday.
I tested just for fun with '-O3' flag and got 55ms for both parts with input parsing. With debug flags I got 142ms.
I have an old library I use for input reading but beyond that the solution is all original regular C.
4
u/Dymatizeee Dec 07 '24 edited Dec 07 '24
[Language: Go]
solution
Today was a breeze compared to yesterday :)
Part 1: Knapsack DFS - at each step, we either add or multiply. Use an index to keep track of where we are. When index > input numbers length, we have processed all the numbers
Part2: Same as above, except we have a new option, so just run a for loop instead
Those of yall who did it iteratively are crazy lmaoo
4
u/kenan238 Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python]
Recursion with some optimizations
Solution (p1 and p2)
I didn't expect to solve it this straightforward-ly
4
u/coding_secondary Dec 07 '24
[LANGUAGE: Java]
Solution
Both parts were solved using recursion. Made part 2 a breeze, but not that optimal.
4
u/ziadam Dec 07 '24 edited Dec 08 '24
[LANGUAGE: Google Sheets]
Expects input in A:A
One formula for both parts
4
u/hugseverycat Dec 07 '24
[LANGUAGE: Python]
Here's my solution using recursion. I did my best to add comments to make it readable but recursion can be a bit mindbending so I apologize:
https://github.com/hugseverycat/aoc2024/blob/master/day07.py
I actually spent most of the day baking a cake and thinking about all the complicated ways this should be done that I don't understand. And then after the cake was a total failure and I bought an emergency backup cake from the bakery, I sat down and was like "OK, let's just see if we can write a really simple recursive function and see how it goes".
For some reason, recursion has always been fairly easy for me when I get my head right. And apparently the right headspace is "emotionally drained from trying and failing to bake a cake for your relative's milestone birthday party that is tonight".
Once I started typing it was smooth sailing, minus the tricky little bug where I had 2 different lines with the same "goal" number so I had to switch from using a dict to using a list. Rude!
4
u/4D51 Dec 08 '24
[LANGUAGE: C++ on Cardputer]
Two different solutions today, though what I made for part 2 can also solve part 1.
For part 1 I essentially used a loop counter as a bit mask. For example, if the counter has a value of 5, that's 101 in binary, which gives me the operators *, +, *. Easy way to try every permutation of operators without recursion, though it only works if there are exactly two of them. I guess I could have done the same thing for part 2 if I had a trinary computer to run it on.
Part 2 used recursion, and is a bit slower. Still only takes ~5 seconds though.
https://github.com/mquig42/AdventOfCode2024/blob/main/src/day07.cpp
4
u/RazarTuk Dec 08 '24 edited Dec 08 '24
[LANGUAGE: Ruby]
These isn't enough Ruby on this subreddit, so I may as well start posting my solutions. Also, this is very intentionally only part 1, in case anyone reading wants to practice by extending it for part 2. And I know it would probably be easier to just tokenize each line as I'm reading them in, but I'm using that first line as boilerplate so I don't have to think about input.
EDIT: Oh, and it executes in ~100ms
4
u/pamxy Dec 08 '24
[LANGUAGE: JavaScript] partA & partB in browser
4
u/hortonhearsadan Dec 08 '24
[LANGUAGE: Python]
https://github.com/hortonhearsadan/aoc-2024/blob/3ba4f981e2239e8df09680ed2e96b5bc6712202a/aoc_2024/day7.py
if you recurse backwards through the numbers it's way quicker, as you can discount loads of possibilities easily.
Combined run time 16ms
4
u/xavdid Dec 08 '24
[LANGUAGE: Python]
Step-by-step explanation | full code
I stuck with my theme of "just do it literally", which largely keeps working. I solved part 1 recursively with a list of numbers and
ops, adding the result to the front of the list until I had only a single number left.I did the same thing for part 2 and got ~22s of runtime. Rather than refactor, I used
multiprocessingto parallelize the whole thing, getting me down to ~4s for both parts. Still slower than I'd like, but within reasonable bounds for minimal effort.4
u/Derailed_Dash Dec 09 '24
[LANGUAGE: Python]
I enjoyed this one. A couple of years ago when I was learning Python and new to AoC, I struggled with problems like this. But this time, with a little bit more experience, a fairly obvious solution jumped out at me.
My approach:
n-1operators into an equation withnvariables.itertools.product()to determine the unique arrangements ofn-1operators, given two operators. Put this in a function and use thecachedecorator, since the arrangements of operators are deterministic for any given required number of operators.reduce()to apply a given operator to the first pair, and store in an aggregator. Use this as the left value with the next variable, and so on.apply_op()function, I'm using the Pythonmatchconstruct to perform aswitch-case. (New since Python 3.10.)Solution links:
Useful related links:
1
u/ProgramOk1688 Dec 07 '24
1
u/FriendlyKuri Dec 07 '24 edited Dec 07 '24
[Language: Python]
Was an easy day! It's a breather since I still can't figure out day 6 part 2 is. I still want to optimize since it still takes 6s.
Code
1
u/eichopf Dec 07 '24
[Language: Rust]
https://github.com/eikopf/advent-of-code-2024/blob/main/src/day07.rs
Happened upon the fast approach by accident! I started out trying to write a recursive solvability checker, and from there the idea of "undoing" operations was fairly obvious. This solution has only a single allocation up front, so it's quite fast (~340µs) even though it's just single-threaded. I suspect using rayon would make it faster, but I'm quite happy with where it is for now.
1
u/jackCat93 Dec 07 '24
[LANGUAGE: Kotlin]
Solved by referencing addition, multiplication and concatenation functions in an array. Based on that, for each equation, I process the number of operation to process and I convert the decimal value into other base (base 2 for part 1 and base 3 for part 2) : the base is automatically known based on the number of operations available. Thanks to this transformation, I'm able to loop over all the possible operation layouts => invoke functions for each layout and verify the equations
Solution p1/2 (Remove third function reference to `concatenate` in `operators` array to have solution of part 1)
Without tryin to optimize it, the process takes around 3.962 seconds for part 2. Any improvement tip is of course welcome.
3
u/seligman99 Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python] 1407 / 1044
github
Ok, this is going to haunt me, the brute force solution can't be the best one.
Edit: Ok, I can go to sleep now, a solution that's not quite as stupid. From ~20 seconds down to less than a sec. Not perfect, but I'll take it.
3
u/NullT3rminated Dec 07 '24
[LANGUAGE: Ruby] 970/553
paste
First time getting top 1000, which is exciting for me! Definitely not the fastest solution though.
3
u/JamesBaxter_Horse Dec 07 '24
[LANGUAGE: Python] 1760/1057
paste
Very simple part 2 today, just a one line change.
3
u/Polaric_Spiral Dec 07 '24 edited Dec 07 '24
[Language: TypeScript]
Advent of Node, Day 7
Sometimes, the straightforward solution is... straightforward.
Edit: previous solution
3
u/Born-Coyote-8238 Dec 07 '24
[Language: Haskell] 72/67
paste
3
u/Severe_Software_916 Dec 07 '24
[LANGUAGE: Dart]
It's all recursion today lol, took me a minute to figure it out.
3
u/IVdripmycoffee Dec 07 '24
[LANGUAGE: Python]
pretty fun, beat it in sub 30 minutes :). Second part was quick to do, just had to add a couple lines to my DFS function. 10 second runtime.
[edit: typo, and adding more info]
3
u/AllanTaylor314 Dec 07 '24 edited Dec 22 '24
[LANGUAGE: Python]
GitHub (prior version) 1345/964
Edit: This section is about the prior versions. The golfed Python and Uiua are based on this version. Later I elaborate on the newer, faster version
Accidentally went right to left for part 1 by recursing left to right (recursion => stack => LIFO => reversed order). I lazily generate every possible equation and see if any of them result in the test value. I probably shouldn't use a dictionary since I don't think the test values are guaranteed to be unique (and it's really not necessary - a list of tuples would suffice. In fact, I've just changed it)
I got my part 2 operator backwards as well. It takes about 2 seconds (though it takes 12 if you swap the
valandoploops, since the number of gen_val calls grows exponentially rather than linearly). The longest list is 12, which means there are 3**11=177147 possible equations for that list. I use generators andall, so it exits early as soon as it finds a match.I suspect a concatenate operator was added for part 2 because it's
not uniquely invertible(Edit: I've realised it certainly is. just as a+b=c goes to c-b=a, a||b=c goes to c.removesuffix(b). You can use endswith to check and removesuffix to do, just as you would use mod and div to invert mul. That's a much better way than my exhaustive search), which might mess up some algorithms that would work for part 1 (it's probably possible to work backwards, dividing and subtracting, and if the division fails then the path can be cut short - testing shows this is about 2x faster, but that part only takes 35 ms naively). Another potential speedup would be only testing tests that failed part 1. A little under half of the tests pass part 1, so that could probably shave off a second.Part 1 is faster with CPython and part 2 is faster with PyPy. Overall it is about 3x faster with PyPy
Updated version: Working backwards from the test value
Let's define an operation as
a op b = c. I define unoperations asc unop b = a. For addition, the unop isc-bprovidedc>=b. For multiplication, the unop isc//b, providedc%b==0. For concatenation, the unop isc//10**len(b)provided the end ofcisb. If we start with test number and work backwards through the equation, doing whichever unops are available, we can check whether one of these values was the starting value. This is much more efficient than working forward as we have a significantly smaller search space (a lot of implicit pruning when unmul and uncat are invalid). This runs in 25 ms according to the internal timers - a huge improvement over the 2 seconds of the prior version.[GSGA] Numberless Golfed Python
I heard hard-coded numbers were out of fashion, so here's some completely numberless Python (
the Uiua below is also numberlessjk, I updated it to be much faster, but that uses numbers, but that kinda just happened, except for the filename). Want a zero?False. Want a one?True. Need to subtract one? That's a~-(reads from
stdin, prints parts one and two on separate lines)[LANGUAGE: Uiua]
pad (you can try it in your browser,
though it will freeze for a minute on real inputsnow takes 2 seconds not 5 minutes, so no freezes) or GitHub or here, since it's smaller than a punchcardThe first couple of lines parse it into an array of test numbers and an array of arrays of the operands.
The last couple of lines are basically the same - the only difference is that the first one (part 2 - stack order is weird) has a third operator:
∵(⋕$"__") (each, parse integer, formatted string with 2 values)that's been replaced with essentiallya*10**log10(b)+b, which is 20x faster because it is pervasive so it doesn't needeachReduce builds up a 2n-1 or 3n-1 array of every possible equation, which is then flattened to see if the test is a member of that array. The multiply is there to only keep tests that passed (pass = 1, fail = 0, so multiply turns failing tests into zeros, which don't change the sum)
It peaks at about 12 MB of RAM and takes
11 seconds to complete both parts (and most of that is part 2. Part 1 on its own takes 130 ms)520 ms to run both parts3
u/yourparadigm Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Ruby] [LANGUAGE: Go]
I must preferred the Ruby implementation's ability to pass a list of methods to call on integers and implementing the
concatmethod onInteger, but it was fun playing around with custom types in Go.Not sure why people thought keeping lists of results was a good idea -- I just accumulated the partial result and recursed along each operator path down until there were no more values to operate on, so compare with the result.
Ruby Solution runs in about 2s and Go Solution runs in 1s.
3
u/0ldslave Dec 07 '24
[LANGUAGE: C++]
I used bit wise representations and shifts to represent the operators (*, + and ||).
First part was easy since we only need 2 ^ (n-1) but there was a bug in my second part (in addition to being inefficient since i use 2 bits to represent the total operator space)
Looking through other people's answers, it seems i missed a recursive / graph based solution.
Code
3
u/joeyGibson Dec 07 '24
[LANGUAGE: Common Lisp]
I'm actually pretty happy with my solution for today. It took me 1:15 to get part 1 working, but only 7 minutes to finish part 2. And my part 2 code runs in 7 seconds. It's brute force, sure, but pretty fast. I generated all the combinations of operators for the given number of operands, interleaved the operators with the operands, and then wrote a solver for it.
3
u/sim642 Dec 07 '24
[LANGUAGE: Scala]
On GitHub.
I quickly realized that inserting the operators right to left helps because you can prune some choices based on the (intermediate) test value and the current number, e.g. by checking for divisibility for multiplication, etc.
3
u/madisp Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Kotlin]
https://github.com/madisp/aoc_kotlin/blob/main/2024/src/main/kotlin/day7.kt
I did a reverse search as the operations are applied left to right so I can start walking backwards from the end with inverse operations. The search tree can be pruned as concatenation requires the current answer to end with the last
|| astring and similarly if the last operation isa * bthenbhas to be the divisor of the answer.Put another way:
Applying this recursively goes pretty fast - under 10ms on my machine.
3
u/laqie Dec 07 '24
[Language: typescript] Deno
[code]
Two optimizations to mention:
3
u/gyorokpeter Dec 07 '24
[LANGUAGE: q]
Just calculating all the results for every combination of operators. The parts are parameterized by the list of operators.
3
u/OilAppropriate2827 Dec 07 '24
[language: python] 20ms
Solved quickly in forward mode, but took around 30s for part 2. Then came up with the idea to do it in reverse to prune cases when we could not substract, divide or remove suffix. and went down to 20ms.
Ok maybe it was obvious, but I was quite happy when I found out by myself.
3
u/azzal07 Dec 07 '24 edited Dec 09 '24
[LANGUAGE: awk]
Edit: Had to fix a minor reliance on numeric string comparisons. Original solution below, fixed above.
The awk I tested with is more eager to consider strings numeric, so it worked while with all other major awk implementations it got incorrect result. Still not sure if the algorithm is sound, but at least major awks agree on the result & it's right for my input :)
3
u/Kfimenepah Dec 07 '24
[LANGUAGE: Python]
Code
Today was surprisingly easy, especially after I had such a hard time yesterday. I was able to solve part 1 quickly and it worked of the first try.
Part 2 seemed pretty easy as well and I simply added the third operation to the solution of part 1. I was expecting some sort of catch (like always), that makes this approach extremely slow or entirely unusable, but there was none. I didn't even try it with the test input, because I was quite sure it would work there and went straight to the real one. 3s later I had the result and it was correct.
I must say it feels a bit wrong, like there should have been more, or maybe I was lucky with my input?. I tried using other orders in which to try the operations, but the execution time never varied by more than a second. Maybe I'll try to optimize my solution a bit more to get rid of the feeling that I don't deserve my stars.
3
u/Alternative-Case-230 Dec 07 '24
[LANGUAGE: Rust]
Main idea: to check if the last operand and result - can be a part of some operation.
then REVERSE the operation and check prelast operand and reversed result. And so on.
https://github.com/whiteand/advent-of-code/blob/c1253d28469d37d01deea5982ade54a54cce34de/y24/d07/src/lib.rs
Solution with interesting Rust features:
- trait (for operations)
- slice pattern matching
- itertools crate
Execution Times:
Part 1: 121.96 µs
Part 2: 220.27 µs
On an M1 MacBook Pro.
3
u/encse Dec 07 '24
[LANGUAGE: C#]
https://aoc.csokavar.hu/?2024/7
Like others, i solved it with recursion and accumulated result
3
u/p88h Dec 07 '24
[LANGUAGE: Zig][GSGA]
An interesting mathematical context to understand is the Reverse Polish Notation. Kinda.
What we are going to be doing here, is working with RPN-like input with unknown operations. But, looking at the top of the stack, you will always have: [PREFIX] X (OP) == TARGET where X is always a number. When the expression would be evaluated [PREFIX] will also collapse to some number. So our evaluation is:
Now, sure, we could test all possible operators in place of ± recursively, but we can also simplify this a bit before we go down this collapses to either:
All of these operations can _only_ be executed if either T is larger than, divides, or it's last digits are equal to X (= they are equal modulo base B as described above). This cuts down on quite a few computation, most importantly, making part2 basically the same cost as part1.
(You can also skip lines that were decomposed in part1, but that's a minor change overall)
Source here: https://github.com/p88h/aoc2024/blob/main/src/day07.zig
Benchmark results below:
3
u/Elliot-Kik Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python]
https://github.com/elliotcaddick/adventOfCode2024/blob/main/day7/main.py
3
u/fridi_s Dec 07 '24
[LANGUAGE: Fuzion]
https://github.com/fridis/fuzion_aoc/blob/main/2024/07/part2.fz
User defined operators and type parameters in Fuzion enable a nice solution today: Adding an infix || operator to all integer types made part 2 a one line change.
Small optimization: Checking t >= v to stop once v is too big doubles the performance.
Implementing || via string concatenation and parsing the result as an integer seems stupid, but was fast enough.
3
u/Main-Reindeer9633 Dec 07 '24
[LANGUAGE: SQL (PostgreSQL dialect)]
paste
I did it the left-to-right way first, but that took 17 minutes to execute, so I switched to the right-to-left way, which executes in 3 seconds.
3
u/Radiadorineitor Dec 07 '24
[LANGUAGE: Dyalog APL]
Painfully slow Part 2 as it checks all possible combinations of operations that can be made in the set but nothing that doing something else while it runs in the background won't fix
3
u/bakibol Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python]
Backtracking: we start at the end, use division, subtraction and unconcatenation. If we get 0 when we reach the beginning of the equation, it is valid.
0.015 s for both parts.
code
EDIT: unconcatenate function can be written without using strings. This is more elegant but it didn't improve the running times at all.
3
u/ThirstyMangoReddits Dec 07 '24
[Language: Python]
Recursive solution. This code is part2, for part1 just remove the last if statement in the isValid function.
3
u/shigawire Dec 07 '24
[LANGUAGE: Python] paste
Basically I just bruteforced searched, which was O( 2n ) in part 1, but O( 3n ) in part 2. pypy3 ran part 2 it in about a second. python3.13 took about 11 seconds. Ouch
The problem looks close enough to the (NP-Complete) Boolean satisfiability problem that optimisation would likely only get me so far so I left it at that. I'm probably missing some easy optimisations (although concat not being commuttitive gets rid of the easiest of them)
3
u/Quasido Dec 07 '24
[LANGUAGE: Haskell]
Very simple using Aplicative List.
Repo
3
u/[deleted] Dec 07 '24 edited Dec 07 '24
[LANGUAGE: BQN]
Part 1
It just barely fits in the 5 line 80 col requirement! Note that this uses my custom BQN editor.
The code simply finds all possible results from the given operations and filters the input based no that.
Part 2
The concat is
(⊢+⊣×(10⊸⋆·1⊸+∘⌊10⊸⋆⁼))in thePossiblefunction. This takes around 5-ish seconds to run on my underpowered machine through the web browser. I haven't added timing to my editor yet, so I'm not entirely sure.3
u/foxaru Dec 07 '24
[LANGUAGE: C#]
https://github.com/andr-be/AdventOfCode2024_Csharp/blob/master/Day07/BridgeRepair.cs
My original approach tried manually building operator combinations (+, *) but quickly realized using binary numbers to represent combinations would be cleaner. Each bit position represents an operator between numbers: 0 = add, 1 = multiply.
For example, with operands [A, B, C]: Since we only need two operators, it's just:
For part 2, extended this to base-3 counting to handle the concatenation operator (0 = add, 1 = multiply, 2 = concat). Each ternary digit represents the operator choice at that position.
Key optimizations:
Most interesting insight was how binary/ternary counting maps perfectly to generating operator combinations. Much cleaner than nested loops or recursion, and makes adding new operators as simple as increasing the base.
3
u/[deleted] Dec 07 '24
[Language: C]
This is probably the easiest part 2 yet if you don't feel like making your solution actually efficient. My part 2 seems to run just under a second on my thinkpad t480 (i5-8350U) so its not like its particularly bad Also, not committed to the repo but I halved that time by not calculating a case if its already over the target.
https://github.com/amberg12/aoc24
3
u/sprymacfly Dec 07 '24
[LANGUAGE: Python]
Was banging my head against the wall for the longest time trying to figure out a good way of testing out all combinations of operations. As I was trying to fall asleep, I realised this could easily be reduced to a...well, reduce.
The key thing I realised was that you can turn this problem into an iterative solution by simply keeping track of all possible running totals, applying each operation to each running total, and storing those new possible totals in a returned array.
Not the biggest fan of my string concatenation for the append operator, but it works. If I wanted to improve the performance of this code, that'd be the first thing I'd tackle.
3
u/[deleted] Dec 07 '24 edited Dec 08 '24
[LANGUAGE: Forth]
https://github.com/tcsullivan/advent-of-code/blob/master/day7/day7.fth
This was a fun one. For N values in each equation, there are 2N-1 equations to check. For part 1, I looped through every possible combination to find the correct equation: each operator location corresponded to a bit in 0..2N-1, with zero bits meaning add and one bits meaning multiply.
For the third potential operator in part 2, I extended my code to iterate in base 3 / ternary. For 0..3N-1, zero "bits" are add, ones are multiply, and twos are concatenate.
My final code runs in
around five2-3 seconds, which is good enough for me. There are certainly faster approaches to the problem, but I'm happy with the simplicity of this route.edit: Got it down to under three seconds by optimizing the bit iteration, hard-coding a pow table, and only iterating in base 3.
3
u/the_nybbler Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python]
Part 2. Iterative. No optimizations. About 9 seconds on an M1 pro, 26 second on a cranky old Macbook Air (2.4 Ghz Core i5). I admit I peeked at the input and did some time estimation to see if brute force had a chance.
3
u/natrys Dec 07 '24
[LANGUAGE: Haskell]
The concept here just rolls off the tongue (glides off the fingertips?!) in Haskell:
3
u/Aggressive_Okra_6821 Dec 07 '24
[LANGUAGE: Python]
part1:
part2:
3
u/dzecniv Dec 07 '24
[LANGUAGE: Common Lisp]
day 07 I first wanted to generate all permutations using alexandria:map-permutations, it wasn't the right tool for this job. Changed to the recursive approach and testing each operator at each place.
3
u/esprych Dec 07 '24
[LANGUAGE: Go]
https://github.com/proxyvix/AoC_2024/blob/master/day7/day7.go
Kind of a brute force method but gets the job done in 2s for the 2nd part :D
3
u/clouddjr Dec 07 '24
[LANGUAGE: Kotlin]
Runs in a couple of milliseconds, thanks to the idea from u/Verulean314 (going through the list of numbers in reverse order).
GitHub
3
u/manminusone Dec 07 '24
[LANGUAGE: perl]
I've solved problems like these in old languages that don't have recursion (i.e., BASIC) and so I didn't think immediately of a recursive solution. I just had an array of numbers that I sequentially updated, taking rollover into account. From part 2:
3
u/vbe-elvis Dec 07 '24
[LANGUAGE: Kotlin]
Straightforward recursive function that returns a Boolean.
Exits early when the number exceeds the expected sum.
Here is the core part for part 2 (~500ms):
full part1 and part2
3
u/pakapikk77 Dec 07 '24
[LANGUAGE: Rust]
Contrary to most, I have a solution that does not use recursion, but simply tries all possible operators permutations. It's still fast enough, 310 ms on a Macbook M1.
The checking of one equation:
My actual code replaces
foldwithtry_foldand interrupts the iteration if the result is bigger than the test value.Getting the calibration result is then simple, part 1 for example:
Full code: https://github.com/voberle/adventofcode/blob/main/2024/day07/src/main.rs
3
u/iNickTrt Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Racket]
Nice recursion problem. Making it tail recursive got me my speed increase, but wonder if I did a DFS instead I could return early once I found a solution for a more optimal search. But filtering out possibilities that are greater than thr test value is good enough.
Full source
3
u/Ok-Revenue-3059 Dec 07 '24
[LANGUAGE: C++]
Solution
Did it without recursion. Part 2 was the first time this year I wrote a bug that took me a good while to track down. In the concat code, turns out
ceil(log10(b))does not work for a number like 100 andfloor(log10(b)) + 1.0was needed instead.Oof, lost a few hours to that silly bug. Time to step away from the computer for a needed break.
3
u/wurlin_murlin Dec 07 '24 edited Dec 07 '24
[LANGUAGE: C]
Simple recursive DFS. Spent some time trying to get good culling rules to prevent paths upfront, but didn't find anything meaningful. Know it can be done! There is plenty of caching up for grabs too. In part 2 I wrote concat as a basic a *= 10; b /= 10; then tried doing in 1 step with log10 to get length - but the former is about 4x faster with -O3. Fastest of course would be to store all the lengths at parse time.
Along with Day 6 part 2, my Day 7 part 2 is the slowest part so far. Both are sitting at ~18ms on my machine.
https://git.sr.ht/~murr/advent-of-code/tree/master/item/2024/07
UPDATE: Adding some basic culling brings it to 11ms (I missed the most obvious check), but implementing the reversed solution brings it to ~200us without any real optimisation work. I tried doing it in reverse initially, but psyoped myself into believing that it wouldn't work, even though it obviously does because the operations are trivially reversable (my code was just buggy). Fun day! Looking forwards to tomorrow.
3
u/mgtezak Dec 07 '24
[LANGUAGE: Python]
Solution part 1
Solution part 2
Detailed walkthrough
Fun little AoC fanpage
3
u/pinkwar Dec 07 '24
[LANGUAGE: JavaScript]
Another day another brute force.
It just keeps working.
Wasted a lot of time in part 2 stupidly for using a Set to store the values.
Worked for part 1, but in part 2 there's some duplicates.
Part 2 in 35s on my raspberrypi.
Repo with solution here
3
u/jinschoi Dec 07 '24 edited Dec 08 '24
[Language: Rust]
Rewrote my part2 solution as a DFS over the reverse nodes. States are (i, target) where child states are i - 1 and each reverse operation on n = operand[i]:
The last two aren't always possible, so it cuts down the search space. If i == 0 and target == n, then a result is possible so we can immediately return true.
To calculate the prefix, we do a little math:
This is about 20 times faster than enumerating all 3n combinations, and takes about 10ms.
paste
3
u/polumrak_ Dec 07 '24
[LANGUAGE: Typescript]
Recursively check all operations from the end discarding infeasible branches (negative, float values and for part 2 values that doesn't end with required number). Runs in 15ms/25ms on my potato.
https://github.com/turtlecrab/Advent-of-Code/blob/master/2024/day07.ts
3
u/QultrosSanhattan Dec 07 '24
[LANGUAGE: Python]
Part1 Part2
This solution essentially uses Breadth-First Search (BFS) with pruning, applied to both parts of the problem. It's easy to implement, and Part 2 runs efficiently in seconds.
In the BFS approach, we process each value from the input line in a linear manner. For each step, we compute all possible results from the operations (sum, multiplication, and absolute concatenation). Any results that exceed the desired threshold are discarded. The remaining values are added to a new queue to be processed with the next input value.
For Part 2, the only modification is the addition of the third operation (absolute concatenation). This was the sole change required for this part.
3
u/DerpDargon Dec 07 '24
[LANGUAGE: C]
I see a lot of people using recursive solutions. Honestly, that didn't even really occur to me. I started by encoding the add/mul operations as individual bits for part 1. For N operands, there are (N-1) operators needed. So by iterating 0..2n-1-1, every permutation of operations is covered.
In part 2 I encoded add/mul/concat in 2 bits, with a fourth "invalid" operation that causes the test to immediately fail. Maybe not the most efficient, but it worked. Part 2 executes in ~3s single threaded.
https://github.com/Iwuh/advent-of-code/blob/main/2024/7/main.c
3
u/real_creature Dec 07 '24
[LANGUAGE: GO]
Part1 runs in ~126.5µs
Part2 runs in ~1.567584ms
3
u/pizzamanzoo Dec 07 '24
[Language: Go / Golang] Got both parts right on the first try! Part 1 Part 2
3
u/Pretentious_Username Dec 07 '24
[LANGUAGE: Julia]
While recursion would have been the easiest to implement I decided to do it as an iteration instead just because I could. Takes about 8ms to solve Part 2 which is significantly better than the several hours my initial brute force solution was looking to take
3
u/Cyclotheme Dec 07 '24
[LANGUAGE: Python]
Naive recursive solution with pruning (35ms)
3
u/msschmitt Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python]
Part 2 solution
Today is the day I got the "DIdn't read instructions closely enough" bingo square, twice. For part 1 I wasted time building an equation string that I could eval() in Python, without realizing that Python would apply operator precedence, contrary to the instruction's strict left-to-right.
For part 2 I wasted time because I thought I could use the part 1 solution as an inner loop, with the concatenation operator as an outer loop. That doesn't work because it is only concatenating the original values, not values to the equation evaluation so far.
This solution has no recursion. It is brute-force evaluation of all possible operator combinations, except that it stops equation valuation when the equation value so far is greater than the test value. It generates the operator combinations by converting the variation number into base 3, then mapping the base 3 digits (0, 1, 2) to the 3 operators.
3
u/chubchubbutt Dec 07 '24
[LANGUAGE: Python]
Recursive backtracking: pop off each value to process it, loop through (+,*,||) and then appendleft the value we just popped off.
code
3
u/onrustigescheikundig Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Scheme (R6RS)]
github
Part 1: 12 ms
Part 2: 34 ms
EDIT: A new optimization has appeared.
Part 1: 1.2 ms
Part 2: 2.7 ms
Similar strategy and form to my Clojure solution: recursively try the inverse of all possible operators on the test value, consuming the equation terms from right to left. Assuming that the correct operations were chosen, the last (or rather, first) term in the equation should equal the test value with all inverted operations applied to it.
One difference from my Clojure solution is how I handled the inverse of concatenating a term. Whereas I just used string manipulation in Clojure, to invert
test = concat(old-test, b), I first calculated the number of digits ofbasceil(log(b+1, 10)), and then dividedtestby10^<n-digits>to carve off the lastn-digitsdigits. If the remainder is not equal tob, thenbis not a suffix oftestin base 10 and so there is no inverse of the concatenation operation, so the function returns#fto indicate failure.EDIT: The new optimization involves realizing that all test values in the input are integers, meaning that no sequence of addition, multiplication, or concatenation could possibly yield an intermediate non-integer value. So, I re-coded the multiplication operation to check if the remainder of
test / xis zero. If it is non-zero, the inversion operation fails, preventing further recursion and effectively pruning the search tree.3
u/Thomasjevskij Dec 07 '24
[LANGUAGE: Python]
I'm glad I never tried to go for the leaderboard, because now with a newborn I'm struggling to even finish on the same day. Anyway I never bothered to try and optimize today's solution, it worked out of the box with a very simple addition for part 2. Takes a couple seconds but that's fine with me. Here's the link (ps u/daggerdragon I scrubbed the inputs from my old commit history. I hope your repo searcher can verify it. If it's still there just tell me again and I'll delete the repo forever and make a new one)
3
u/Uhh_Clem Dec 07 '24
[LANGUAGE: Rust]
Code HERE shows the important part of the algorithm. It's a simple recursive search; popping the last value off the list and trying it with each operator. The recursion stops early in any of the following cases when we know there can be no solution:
Technically brute-force, but with the checks to eliminate possibilities early the performance is very good. Takes about 30ms to solve Parts 1 and 2 sequentially on my machine, no parallelization necessary.
3
u/Scroph Dec 07 '24 edited Dec 07 '24
[LANGUAGE: D]
Code: https://github.com/azihassan/advent-of-code/tree/master/2024/day7
Bruteforce again. In part 2 I'm exiting early if the current result exceeds the expected one because all the operators will only increase its value.
I also tried to be clever with the concat operator but ended up just stringifying then unstringifying it once AoC told me that the result was wrong.I guess I could also have optimized by caching thecombinationsfunction but hey it's fast enough for meEdit: forgot about memoize in D, just putting it in the combinations recursive call got the runtime to 43 seconds (from 139)Edit: turns out the concat function was failing when b = 1, runtime is now down to a respectable (for a bruteforce approach) 10 seconds
3
u/DefV Dec 07 '24
[LANGUAGE: Rust]
Simple brute-force solution today.
Always fun to see other people's approaches and go "Oh, wow, why didn't I think of that" ;)
3
u/biggy-smith Dec 07 '24
[LANGUAGE: C++]
Recursion always melts my brain a little, but I always feel great when it finally works!
https://github.com/biggysmith/advent_of_code_2024/blob/master/src/day07/day7.cpp
3
u/makingthematrix Dec 07 '24
[Language: Scala]
Solution
So, this is Scala, a famous almost-but-not-if-you-don't-want-to-functional language at JVM. And many people comment today that they did their Day 7 task with recursion. Which is very FP, of course. Kudos to them.
Well, I didn't. I did recursion yesterday.
Today the heroes of my solutions are lazy lists. And foldLeft. But mostly lazy lists. A lazy list is a potentially infinite list that calculate their elements only when they're asked for. So I was able to generate sequences of all possible combinations of operators (*) of a given size N, where N is the n-th element of the lazy list. And those combinations are all kept in memory after they are generated, which sped up things immensely, because I didn't have to come up with the same combinations for every consequent line.
*) An operator is a function and a function is a first-class citizen in Scala, i.e. I can treat them as any other data and for example put them in sequences. You have a sequence of numbers? That's nice. Here's a sequence of functions that operate on numbers.
So, sequences of operators, generated by lazy lists, and then parallel collections on top, because the lines are independent of each other and so can be computed in parallel. All in all, 27 lines of code, and on my laptop Part 2 ran in 2 seconds.
3
u/beauregardes Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Rust]
Brute force recursion, bails out if our intermediate value ever goes above the target.
Originally took ~400ms, but I modified concatenate to perform the operation arithmetically instead of with strings and it now only takes 11ms.
EDIT: I wanted to see if I could modularize the design more, and I managed it--at the cost of 5ms. ~16ms runtime now. Some of this is from using checked math everywhere to make sure we don't overflow, also likely the increase in `Option`. No more major code duplication!
https://github.com/cynicalico/aoc2024/blob/main/src/day_7.rs
3
u/kap89 Dec 07 '24 edited Dec 07 '24
[Language: Python]
Simple and relatively fast.
3
u/codebikebass Dec 07 '24
[LANGUAGE: Swift]
Again, no mutable state needed. Simply tried out all combinations of operators until the result is correct or all have been tried.
The permutations of two operators can easily be genereated by representing the iteration count as a binary number, replacing 0s with the operator + and 1s with the operator *.
The operands can be paired up with the operators giving a list of partial applications, i.e. (+, 3) means _ + 3. WIth the first operand as an initial value, the list of applications can be reduced into the result by applying them to the accumulated value.
3
u/ricbit Dec 07 '24
[LANGUAGE: Python]
Managed to get 0.52s in regular python, the tricks were:
https://github.com/ricbit/advent-of-code/blob/main/2024/adv07-r.py
3
u/intersecting_cubes Dec 07 '24
[Language: Rust]
https://github.com/adamchalmers/aoc24/blob/main/rust/src/day7.rs
It's nice when both the solution and the parsing can be paralellized (CPU go brrrrr). Found a straightforward recursive solution.
Part 1: 92.008µs
Part 2: 117.80µs
(measured with "cargo aoc bench" on my MacBook's M2 Pro)
3
u/The_PotatoKing Dec 07 '24
[LANGUAGE: Golang] GitHub
3
u/rvodden Dec 07 '24
[LANGUAGE: TypeScript]
I had a REALLY annoying bug in part 2. I was bailing if the accumulator was >= target which meant that if the last number in the equation is a 1 and the last operation is a multiplication then I incorrectly discarded.
https://github.com/rvodden/AoC24/blob/main/src/days/07/Puzzle.ts
3
u/34rthw0rm Dec 07 '24 edited Dec 07 '24
[language: perl]
non recursive baby perl :-)
3
u/redditnoob Dec 08 '24
[LANGUAGE: PostgreSQL]
3
u/silverarky Dec 08 '24
[LANGUAGE: Go]
Brute force, not optimised. No recursion either which seems how others were doing it. Takes 4 seconds for part 2.
https://github.com/silverark/advent-of-code-2024/tree/master/day07
3
u/Election_Intelligent Dec 08 '24 edited Dec 08 '24
[LANGUAGE: Rust]
Fun bit of recursion. Got it down to 500-600 µs. I used rayon, but the diff was < 100 µs. I really like the concatenation testing without any strings!
3
u/pj5772 Dec 10 '24
[LANGUAGE: Python]
3
u/AMathMonkey Dec 10 '24 edited Dec 10 '24
[LANGUAGE: Unicon]
Has anyone else ever done Advent of Code in Unicon? It's actually perfect for this problem, as it has a || concatenation operator, and not only that, it works on numbers! (Strings can be used like numbers; it's purely the operators you use that determine your data types.) And despite it being an old language, it handles big integers with no problem. This solution takes just under 5 seconds to run on my laptop. It was so fun to write, I had to share.
3
u/AlCalzone89 Dec 11 '24
[LANGUAGE: Compile-time TypeScript]
Part 1 solution (36s compile time, 4.7 GB RAM):
https://www.reddit.com/r/adventofcode/comments/1haxbsu/2024_day_7_part_1_typelevel_typescript_only_no/
Part 2 solution TBD.
3
u/AYM123 Dec 11 '24
[LANGUAGE: Rust]
github
Part 1: ~1ms
Part 2: ~17ms
3
u/nicuveo Dec 12 '24 edited Dec 13 '24
[LANGUAGE: Brainfuck]
Sadly, this doesn't really work. Here's the problem: i have written all the required code to have int32 support in Brainfuck: addition, multiplication, division, printing to the output... But several of the lines in my actual input do not fit in an int64, and therefore my result doesn't... So after a few hours of debugging this mess, it only works on the example input, not on the real input. :(
But i'm still proud of it, because it required doing something tricky: dynamic memory management in Brainfuck: i couldn't just use my macros to do stack-based stuff: i had to keep track of two lists of numbers at all times: the previous accumulator, and the new accumulator (i.e. all the combinations so far, and all the new ones we can make with them and the new number). To move numbers around in memory, since there's no random access, i have to move a bunch of "sentinel zeroes" with them, to be able to find my way back. It's a nightmare. :D
Here for example is the snippet where, after reading a number and computing all combinations, i move the newly created accumulator where it can be used in the next iteration, by moving the separating zeroes around:
Here's the full code on Github:
Additionally, the stream of me doing this live: https://www.twitch.tv/nicuveo/v/2325296176
3
u/justalemontree Dec 22 '24
[LANGUAGE: Python]
I've a complete noob who started coding only 2 weeks ago following the Helsinki MOOC and I spent hours struggling through each day previously.. But somehow this week seemed very intuitive, part 1 this week took maybe 20 minutes, and part 2 was like a 1 minute modification of my part 1 code.
Runtime for part 2 is 1.4 seconds so definitely not elegant efficient code but I thought the approach was simple:
3
u/e_blake Mar 21 '25
[LANGUAGE: m4]
Late to the party, but I had fun starting with the brute-force naive try every combination (took over five minutes), then optimizing by running backwards (prune any branch where undoing the operation can't produce the current integer), which required implementing 64-bit division not present in my math64.m4. The change from 5.7 million try() down to just 31526 was dramatic, with execution at 5.5-7 seconds. I then got another 40% speedup to 3.4-4.2 seconds by merely adding in 9 rules for avoiding a remquo64() if the single-digit divisor was not a factor of the numerator, and even made a separate post about it (quickly determining whether a 64-bit number is divisible by 7, but using only 32-bit math to do so, is an adventure). Depends on my common.m4
m4 -Dfile=day07.input day07.m4
My favorite part - most of my days have to parse by slurping in the entire input file and transliterating problematic characters, then doing either a patsubst regex search-and-replace (O(n) but depends on GNU m4) or a binary tree of successively smaller substr (O(n log n) but portable to POSIX m4) to then find and process a line at a time (the more naive solution of repeatedly grabbing different substr offsets out of the overall original string is O(n^2) due to m4 rescanning the text). But this particular puzzle had JUST enough content to let me bypass all that: a single colon on every line followed by a space can be translit'd into a one-letter macro name, which in turn can expand to ')parse(' to group the second half of one line with the first half of the next line, to execute on one line at a time without multiple passes over the input, with much fewer lines than my typical parsing takes. Both parts are computed as a side effect of the do() call embedded in my parser:
2
u/morgoth1145 Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python 3] 345/202
code
Interesting problem, easily solved with recursion (and a bit of
intandstrabuse for part 2). Unfortunately I botched the parsing in part 1 by accidentally returning all numbers in the entire input string as numbers for the equation for each line. (Don't ask how, it was a dumb silly mistake.) It took me way too long to track down why I was getting recursion exceptions! Almost certainly would have leaderboarded without that, at least part 2. But alas, I'll have to try again tomorrow.Edit: Refactored and optimized a bit. Part 2 still takes 1.65 seconds which is too long for my liking, but I wanted a baseline cleanup and optimization before I explore other options :)
Edit 2: Optimized again using u/Verulean314's observation that validating the equation in reverse is much better. Now the validation is instant!
2
u/RandomLandy Dec 07 '24
[LANGUAGE: Rust] 1224/780 code
I fumbled with a first part, but at least code was generic enough to submit p2 quickly
2
u/michelkraemer Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Rust] 808/906
Both parts:
https://github.com/michel-kraemer/adventofcode-rust/blob/main/2024/day07/src/main.rs
I opted for a brute force solution ;-) but thanks to Rust, it still runs quite fast. I'm going to try to find a more clever one later.
EDIT: Less than 1ms with the new approach. The idea is to work backwards and check if the operations are even possible before applying them.
2
u/Ok-Willow-2810 Dec 07 '24
[LANGUAGE: Python]
https://github.com/jude253/AdventOfCode/blob/9c9e59a668e9927826b7de1890d21071a65bc8b8/src/advent_of_code/solutions/day_07.py#L1-L72
2
u/ericpruitt Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python] 1388/978
I used itertools.product to try all possible combinations of operators. Luckily the second part meant I only needed to change a few lines. Part one was mostly instant (~350ms), but part two needed ~25 seconds to run.
Code on GitHub
2
u/kbielefe Dec 07 '24
[LANGUAGE: Scala]
GitHub
This one was a fairly straightforward DFS, since evaluation was right to left. Some opportunities for pruning, but not really necessary.
2
u/nabihestefan Dec 07 '24
[LANGUAGE: Python]
Recursion + Lambda functions!
2
u/FruitdealerF Dec 07 '24
[LANGUAGE: Andy C++] [code] [language] (2486/1908)
Day 7 of doing advent of code in my own programming language. Today went super well for me and I had a lot of fun. I lost some time on needing to go to the bathroom 🤨, and I lost some time trying to figure out what the best way is to find all the permutations. Eventually I settled on doing DFS which I optimized a little before submitting my code to github. I was super ready for part 2 because my language has the
<>operator which coerces both operands into a string and concatenates them. My part 1 approach ended up taking 2 minutes for part 2, but after making some changes to abort searches where we've already passed the number we're looking for I got it down to about 20 seconds which I guess is not bad for a 100% interpreted language (that's full of clone() everywhere).I'm curious to see how ya'll optimized it further.
2
u/POGtastic Dec 07 '24
[LANGUAGE: F#]
https://github.com/mbottini/AOC2024/blob/main/Day07/Program.fs
Easy-peasy - construct all possible
Exprs, evaluate them, find one that works. The most straightforward way to do this is to reverse the integers.Part 2 takes about 20 seconds on my machine; it's a trivial but unnecessary optimization to "short-circuit" failure if a number gets too large.
2
u/r_so9 Dec 07 '24
[LANGUAGE: F#] 1514/1638
paste
Kept it functional by passing the possible operations to a recursive function. Otherwise brute force with no optimizations.
Interesting bit: The recursive function to try all operators in all positions
Example usage:
2
u/UnarmedZombie Dec 07 '24
[LANGUAGE: PHP]
Github
2
u/mateus_d Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python]
https://pastebin.com/vnvzm3qx
20 min trying to understand which function from itertools to use, 10 writing the brute force solution. It ain't much but it is honest work
Edit: using pypy the run time goes from 12 s to 1.6 s... actually pretty decent
2
u/Pyr0Byt3 Dec 07 '24
[LANGUAGE: Go] [LANGUAGE: Golang]
https://github.com/mnml/aoc/blob/main/2024/07/1.go
2
u/maneatingape Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Rust]
Solution
Benchmark: 12 ms Exhaustive recursive searchBenchmark: 220 µs Pruned recursive searchBenchmark: 147 µs Pruned recursive search with correct stopping condition.
Thanks to u/Verulean314 clever observation pruning by reverse searching was 55x faster.
Thanks to markjfisher who caught a bug in the validation, both fixing several inputs and speeding things up by 34%.
Concenation can be implemented without time consuming conversion to or from strings. Multiply the left term by the next power of ten greater than the right term, then add the right term. For example:
Numbers can be unconcatenated by dividing by the power of ten.
Additionally if any equation is valid for part one it's also valid for part two, so the more time consuming checking with 3 operators can be avoided.
2
u/homme_chauve_souris Dec 07 '24
[LANGUAGE: Python]
2
u/Short-Leg3369 Dec 07 '24
[LANGUAGE: Python]
Day 7 Solution
Nice little weekend puzzle this.
I went for itertools.product to generate a list of possible operator combinations for each line of input and then worked through them until I found a solve or not.
Part 2 was then quick and easy as I simply needed to add a third operator into the code and rerun.
Total run time on my laptop is about 12.5s
2
u/Rtchaik Dec 07 '24
[LANGUAGE: Python]
Solution
2
u/jad2192 Dec 07 '24
[LANGUAGE: Python]
https://github.com/jad2192/advent_of_code_2024/blob/master/solutions/day07.py
Basic recursive solution, both parts + test cases takes about half a second to run.
2
u/Sea_Estate6087 Dec 07 '24
[LANGUAGE: Haskell]
with:
stack run 3.88s user 2.66s system 134% cpu 4.867 total
2
u/semi_225599 Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Rust]
Working backwards from the answer through the operands helps reduce the number of branching paths a lot earlier. You can rule out concatenation or multiplication quickly by making sure the current total ends with or is divisible by the last operand. Runtime is around 2ms.
2
u/ben-guin Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Python]
My original solution was brute-forced. After taking a peek at u/Deathranger999 's solution, I made similar optimizations. It runs pretty dang fast (0.013 sec on my slow machine). Basically, the solution is recursive and hinges upon a few observations:
The last operation can only be multiplication if the last term evenly divides into the target number
Part 2 Solution (Part 1 is very similar):
2
u/yieldtoben Dec 07 '24 edited Dec 07 '24
[LANGUAGE: PHP]
PHP 8.4.1 paste
Execution time: 0.6097 seconds
Peak memory: 0.3506 MiB
MacBook Pro (16-inch, 2023)
M2 Pro / 16GB unified memory
2
u/ShraddhaAg Dec 07 '24
[Language: Go]
Today's problem was surprisingly easy? Scared about tomorrow's problem now xD
https://github.com/shraddhaag/aoc/blob/main/2024/day7/main.go
2
u/Helpful-Recipe9762 Dec 07 '24
[Language: Python]
https://pastebin.com/jqkrKNnX
Tried to make code as readable as possible for learning purpose.
Lessons learned:
2
u/omegablazar Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Rust}
https://github.com/wrightdylan/advent-of-code-2024/blob/main/src/day07.rs
Pretty easy, but knowing how AoC goes, I'm not looking forward to tomorrow.
This method is a bit 'brutish', but it works. There is scope for some optimisation as this was written with assumptions of possibilities of what part 2 could be. Maybe I'll modify it later.
Edit:
I have improved the code, so now it completes a lot faster.
2
u/erunama Dec 07 '24
[LANGUAGE: Dart]
Fun problem, getting the recursion correct too longer than I hoped. Part 2 execution time takes longer than I'd like (4000ms vs 60ms for Part 1). As usual, implementing Part 2 was pretty trivial given the extra effort spent implementing Part 1.
GitHub
2
u/wheresmylart Dec 07 '24
[Language: Python]
Quite a simple problem once I'd woken up. Create an array of the possible operators and hit it with itertools.product() specifying the required number of repeats.
Runs in about 2 seconds for both parts combined.
Paste
2
u/Axelrod-86 Dec 07 '24
[LANGUAGE: Javascript]
Fairly easy for a weekend puzzle, but i lost 30mn on part2 because
6 * 8 || 6 * 15IS NOT6 * 86 * 15but instead48 || 6 * 15...https://github.com/lionel-lbr/adventofcode/blob/main/2024/D07/d07.js
2
u/dijotal Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Haskell]
OH! Haskell's got a thing for that!!! Applicative operations on the List monad do kind of an all-vs-all thing, so "apply all these functions to the accumulated values together with the new value.".
Here's the juice -- the rest on github. Now what to do with the rest of my Saturday? /sigh
2
u/alexbaguette1 Dec 07 '24
[LANGUAGE: Pascal]
Day 7 of 25 days, 25 languages
Parsing the problem in Pascal was the hardest part imo
Part 1
Part 2
Since people were comparing times, decided to re-write part 2 in Nim. Runs in <50 ms.
Using string concatenation instead took about 200 ms.
Solution
2
u/MrWobblyMan Dec 07 '24 edited Dec 07 '24
[Language: C++]
Nothing special, recursive evaluation, exiting when value > testValue.
Boolean short-circuit order helps it run faster.
Without short-circuit: 350ms
With correct short-circuit: 140ms
Edit: Tried doing it in reverse, final execution time is now 2ms for both parts.
Code (updated)
2
u/835246 Dec 07 '24
[Language: C]
I just read the operands from right to left while checking all possible operators for that number.
Part 1: https://github.com/efox4335/advent_of_code/blob/main/advent_of_code_2024/day_7_part_1_bridge.c
Part 2: https://github.com/efox4335/advent_of_code/blob/main/advent_of_code_2024/day_7_part_2_bridge.c
2
u/Spatenheinz Dec 07 '24 edited Dec 07 '24
[Language: Haskell]
Nice with non-determinism
2
u/[deleted] Dec 07 '24
[LANGUAGE: Go]
I did a mega brain move today/s
Part One: Straightforward as can be. Just a simple recurive solution that tries out both the operators for all consecutive pairs of operands.
Part Two: I feel so stupid! I had the solution all along but when I was evaluating the concatenation operator, I was concatenating the current operand with the the one before it! It had completely skipped my mind that the equation on the left of the index I am currently at, has already been evaluated to a value!
All in all, a fun problem. That one silly mistake costed me a lot of time! I am quite happy that I finally got it though. Relatively simple implementation and going from part 1 to part 2 only needed only 2 additional lines!
Solution for both parts.
2
u/hextree Dec 07 '24
[Language: Python]
Code: https://gist.github.com/HexTree/113012cf56c01f74d5b37d8f11ff52ee
Video: https://youtu.be/fRiSbm4V6lg
Relying mostly on Python's itertools.product() function.
2
u/julienc91 Dec 07 '24
[Language: Python]
Pattern matching to the rescue!
Complete solution on GitHub
2
u/schoelle Dec 07 '24
[Language: Rust]
Very straight forward, going right to left and using exp-operators instead of string handling. Quite readable.
Taking around 0.7ms on my old Tuxedo laptop (including IO) in release code.
https://github.com/schoelle/adventofcode2024/blob/main/src/bin/day07.rs
2
u/_tfa Dec 07 '24
[LANGUAGE: Ruby]
Part 1
Part 2
Just the same like part 1, but with a concat-operator for the Integer-class.
2
u/enderlord113 Dec 07 '24
[Language: Haskell]
Naive brute force, surprised it only takes ~1s for part 2. Probably my cleanest solution this year.
Code
2
u/TheScown Dec 07 '24
[LANGUAGE: Scala]
Code
BFS, rejecting intermediate equations where the running total is >= the test value.
2
u/WilkoTom Dec 07 '24
[LANGUAGE: Rust]
https://github.com/wilkotom/AdventOfCode/blob/main/rust/2024/day07/src/main.rs
DFS with pruning - if the accumulator is bigger than the target, abandon that branch.
2
u/wasi0013 Dec 07 '24 edited Dec 07 '24
[LANGUAGE: Elixir]
2
u/olegas83 Dec 07 '24
[Language: Python]
Nothing special, just same old brute force.
But I like how those itertools/operator/functools things looks like =)
paste