wonderful numbers that are too big for longs...
Hasnβt been the case for me. You can try using autopromoting operators
I'm approaching day6's question today, and I had inspiration to visualize my solution (both to learn it for debugging and as an additional challenge.) Does anyone have recommendations for what to use for stuff like that?
if you want text gui π
The first is a low level terminal interface which gives you the most basic control of the terminal text area. You can move around the cursor and enable special modifiers for characters put to the screen. You will find these classes in package com.googlecode.lanterna.terminal.Yeah I know that one, it's really neat
I picked up quil for now, although lanterna probably fits my needs a bit more
Didn't know Lanterna. Imma try it out. Thanks for the link !
well now Iβm curious
Progress: Should have probably forced everything to be squares. but I might change that later I fed it the initial state, now to implement moving the guard and then feeding that into the visualization
π
It seems like for the real input redrawing everything every turn is really slow - but I don't have (or want to create and test) the abstraction layer needed to only redraw the changed squares; So I changed it to only draw the player and not visualize the :visited data, and now it's fast
It's still useful drawing everything in the example case
(Actually I could just store an array of which squares I already visited before last turn, diff it with this turn's list, and only draw these; This might be simple enough)
for stuff like this I like text visualizations I know itβs boring but its quick
What do you use for it? I use clojure-lanterna in my console game, but I'd love to hear about more stuff
no libraries just printing text to the screen π Iβm guessing you want animations
Makes sense I guess I want animations, I haven't fully decided; I'm just trying cool stuff for now
when I do that I just print the individiual steps one after the other
I saw someone with a rust animation (in bevy_tiled_ecs) and it inspired me to do it myself, since gui/creative is usually my weak suit, and I don't have any idea how to visualize a reduce for example, so it will be a nice challenge
it certainly sounds fun π
Well doesn't this sound nice
Now only the visualization left π
Day 7 - Solutions
@benjamin.schwerdtner Very nice solution, thanks for sharing. Soo much nicer than my noob-core-logic solution π
I always struggle with numbers and math, so my initial solution was pretty naive β but at least I liked it! Anyway, inspired by @erdos's solution (the math trick), I decided to rewrite mine as an exercise. Now it runs in 4.9 ms and 6.1 ms for parts 1 and 2, respectively. https://github.com/zelark/AoC/blob/master/src/zelark/aoc_2024/day_07_fast.clj
https://github.com/jungwookim/aoc-exercise/blob/master/src/aoc/2024/day7.clj I'm back again and today is a bit of trivial for me compared to past few days.
Another day of Advent of Bruteforce π https://codeberg.org/nathell/aoc2024/src/branch/main/src/aoc2024/day7.clj
combinatorics/selections was ~12x slower
@dj942 https://preview.redd.it/two-ways-of-doing-advent-of-code-v0-lbpdff4fz75e1.jpeg?auto=webp&s=99c58108cbb491622b7cbbb1d2b1c453ba5f1a13
I mean, is there any other way for today? Isn't today's problem some kind of NP-complete problem that is unsolvable unless you literally try everything?
rearranging ops inside juxt seems to give another 5-10% speed up: grow numbers faster and remove sooner
A simple solution using recursion, run in about 300ms on my machine: https://github.com/ChrisBlom/advent-of-code/blob/master/src/adventofcode/2024/day07.clj#L60-L82 I found a small optimization (200ms) by pruning branches were the intermediate result is greater than the target
Is anyone able to ELI5 the math trick? Why is it giving such a fast solution?
Removing the string operations, I could get it down to 4ms+7ms with clj, 4ms+14ms with babashka: https://github.com/erdos/advent-of-code/blob/master/2024/day07_numeric.clj
I think it's because going backwards, from target to the last value, means that most recursion is conditional and thereby avoided entirely
First pass unoptimized:
(ns aoc.2024.d07
(:require
[aoc.file-util :as f]))
(def input (f/read-int-vectors "2024/d07.txt"))
(defn || [a b] (parse-long (str a b)))
(defn valid? [ops [result & operands]]
(some #{result}
(reduce (fn [poss n] (mapcat #(map (fn [op] (op % n)) ops) poss))
(list (first operands))
(rest operands))))
(defn solve [ops input]
(reduce + (map first (filter (partial valid? ops) input))))
(defn part-1 [input] (solve [+ *] input))
(defn part-2 [input] (solve [+ * ||] input))
@dingelsz3
> Is anyone able to ELI5 the math trick? Why is it giving such a fast solution?
Exactly what @mail191 said, but elaborating a bit:
Since all operations are reversible, we can move backwards. For example, if we have an equation like 7 OPβ 11 = 77 (where we want to find operator OPβ), then it is the same as solving 77 OPββ»ΒΉ 11 = 7 for OPββ»ΒΉ . Similarly, 7 OPβ 11 OPβ 1 = 78 becomes 78 OPββ»ΒΉ 1 OPββ»ΒΉ 11 = 7.
And here comes the optimisation: we only have easily invertible operations: for (*) we have (/) , for (+) we have (-) , and for (concat) we can implement (remove-prefix) . But we want to operate only on positive integers. So if the result of the inverse operation would not be such (eg. a fraction or a negative number), then we can stop the search on that branch.
Done! Not that bad
Parallelization + math trick (thanks @erdos!) + juxt instead of combinatorics, total run time ends up being 12ms with input parsing for each part included. Not bad, very cool to see every thing people come up with.
My part 2 right now is SLOOOOW. It takes many minutes.
(ns stuartstein777.2024.day7
(:require
[clojure.string :as str]
[clojure.math.combinatorics :as combo]))
(defn parse-line [line]
(let [[total values] (str/split line #":")]
{:total (parse-long total)
:values (-> (str/trim values)
(str/split #" ")
(->> (mapv parse-long)))}))
(defn parse []
(->> "puzzle-inputs/2024/day7"
(slurp)
(str/trim)
(str/split-lines)
(mapv parse-line)))
(defn comb [m n]
(parse-long (str m n)))
(defn solve
"Takes an equestion of the form [1 \+ 2 \* 3 \+ 4] and returns 13"
[eq]
(:total (reduce (fn [{:keys [total op] :as acc} i]
(if (or (= i \*) (= i \+) (= i \c))
(assoc acc :op i)
(condp = op
\* (assoc acc :total (* total i))
\+ (assoc acc :total (+ total i))
\c (assoc acc :total (comb total i))
nil (assoc acc :total i))))
{:total 0.0 :op nil}
eq)))
(defn merge-lists [main-list interleaved-items]
(let [pairs (interleave main-list interleaved-items)
remaining (drop (count interleaved-items) main-list)]
(concat pairs remaining)))
(defn equation-can-be-solved [operators equation]
(let [perm-count (dec (count (:values equation)))
operator-permutations (combo/selections operators perm-count)
equations (map (partial merge-lists (:values equation)) operator-permutations)]
(reduce (fn [_ eq]
(if (= (solve eq) (:total equation))
(reduced (:total equation))
0)) 0 equations)))
(defn part-one []
(let [input (parse)]
(->> (map (partial equation-can-be-solved [\+ \*]) input)
(reduce +))))
(defn part-two []
(let [input (parse)]
(->> (map (partial equation-can-be-solved [\+ \* \c]) input)
(reduce +))))
(part-one) ; 6083020304036
(part-two) ; 59002246504791I think your generate-permutations is equivalent to combo/selections
π Thanks, you are right, it is. I never noticed that function π
part 2 takes 15 seconds π’
every year I re-learn reversing the input is usually much faster, thinking about how he might create the problems/inputs it makes sense.
hah, very cool math trick to get the optimized version today. thanks for the explainer, @erdos
Hereβs todays: https://github.com/bhauman/adv2024/blob/main/src/adv2024/day07/sol.clj
(defn ev [[res init & args] ops]
(= res
(reduce (fn [acc [op n]] (op acc n)) init (map vector ops args))))
(defn solvable? [possible-ops calib]
(->> (combo/selections possible-ops (- (count calib) 2))
(filter #(ev calib %))
first))
;; part 1
#_(->> (filter (partial solvable? [+ *]) input)
(map first)
(reduce +))#yamlscript Part 2:
OK. I'm pretty drunk. But I do NOT understand this reversing math trick:confounded:
More drinks!
less than 2s. not bad.
(def input #(slurp (format "resources/2024/%02d" %)))
(defn func1 [s] (->> (re-seq #"\d+" s) (mapv parse-long)))
(time(doall(let [data (->> (input 7) (re-seq #".+") (map func1))
f (fn [a b] (parse-long (str a b)))]
(for [ops [[+ *] [+ * f]]]
(->> (for [[sum & nums] data
:when (some #{sum} (reduce (fn [accu n]
(for [a accu
o ops]
(o a n)))
[(first nums)] (rest nums)))]
sum)
(reduce +))))))
erdos's great solution of course. only 8ms. whopping increase in terms of %.
(time(let [data (->> (input 7) (re-seq #".+") (map func1))
round-up-to-nearest-power-of-10 #(let [x (long (Math/pow 10 (Math/ceil (Math/log10 %))))]
(if (= x %) (* x 10) x))
f (fn f [g [n & n*]]
(if (seq n*)
(or (if (= 0 (rem g n)) (f (/ g n) n*))
(if (>= g n) (f (- g n) n*))
(let [x (round-up-to-nearest-power-of-10 n)]
(if (= n (mod g x)) (f (quot g x) n*))))
(= g n)))]
(->> (for [[goal & nums] data
:when (f goal (reverse nums))]
goal)
(reduce +))))
Nothing transcendent today, just a pure bruteforce on the possible operations. Thanks clojure.math.combinatorics .
This is incredibly slow but π€·ββοΈ
EDIT: added some parallelization. Runs in about 5s on my 10 yo laptop. Good enough for me.
https://gitlab.com/maximoburrito/advent2024/-/blob/main/src/day07/main.clj I wrote part1 evaluation backwards and had to reverse the input. But that didn't work for part 2 so I had to rewrite it for that. It's slow, but it gets the job done
Quick and dirty recursion, takes about 1.8s in bb and 400ms in clj
Oh man, this is way faster than what I did x)
Ermm maybe I miscounted, its about 50s -_- I think I can get it down significantly though Edit: Down to 2.6s, no memoization
I don't really need all these sets...
with memoize a small trick i could bring it down to 11ms and 30ms
https://github.com/erdos/advent-of-code/blob/master/2024/day07.clj
(could be faster with fewer string operations)
That reversal of operations is diabolically clever, damn.
Saw the extra operator coming from a mile away so it only took a minute for the second solution π On another note, I've been solving the problems this year on pen and paper before running any code. Anyone doing the same?
https://github.com/rjray/advent-2024-clojure/blob/master/src/advent_of_code/day07.clj I imagine my solution looks a lot like most of them. Ran fast enough, for now. Tomorrow I'll clean it up so that there is less duplication of code. I might also run the lines in parallel for speed.
https://github.com/zelark/AoC/blob/master/src/zelark/aoc_2024/day_07.clj
Couldn't resist shortening the code, removing the duplicate parts: https://github.com/rjray/advent-2024-clojure/blob/master/src/advent_of_code/day07bis.clj Runs about the same speed as before, since none of the changes are optimizations. I'll look at some other solutions tomorrow and see if I can squeeze out some performance.
I like my solution because I finally got to use my depth-first search function in my utilities namespace. Give it a starting search space, a predicate to check if you've found the answer, and a function to create new values for the search space, and let it do its thing. Ended up with a pretty small solution that executes in just over a second for part 2.
β’ Blog: https://github.com/abyala/advent-2024-clojure/blob/main/docs/day07.md
β’ Source: https://github.com/abyala/advent-2024-clojure/blob/main/src/advent_2024_clojure/day07.clj
https://github.com/ghaskins/adventofcode/blob/main/src/aoc/2024/day7.clj
(time (part1))
"Elapsed time: 60.083042 msecs"
=> 20281182715321
(time (part2))
"Elapsed time: 3547.072666 msecs"
=> 159490400628354Enjoying the thread on the optimizations. I get the pruning approach. Still trying to grok the reverse inputs concept
Was real fun reading this thread after my own efforts. I was kicking myself! (But, then, I knew what I was doing. I chose flame graphs over back to the drawing board...) π https://github.com/CarnunMP/Advent-of-Code/blob/master/src/y2024/d7.clj
My first approach yesterday was so slow (fully combinatorics approach). So after some thinking about early cut search branches I come with much faster one. Still 1sec on part-2. However I'm happy with really low number of LOC: https://github.com/genmeblog/advent-of-code/blob/master/src/advent_of_code_2024/day07.clj
https://github.com/benjamin-asdf/advent-of-code/blob/master/src/Y2024/day7_core_logic.clj probably just the same as using recursion.
@erdos https://github.com/ghaskins/adventofcode/commit/e95e7391a55ab35d00140922b4329ee122aabd0f My mind was blown by your solution there. I had never thought about using recursion in this way to conditionally cache partial solutions. Brilliant.