Gonna take a closer look to it, but probably tomorrow
Can someone help me with the following (may contain a spoiler)
(frequencies (map #(* -1 (apply - %)) (partition 2 1 [0 1 4 5 6 7 10 11 12 15 16 19 22])))
;; => {1 7, 3 5}
(defn jolt-distribution [jolts]
(frequencies (map #(* -1 (apply - %)) (partition 2 1 jolts))))
(jolt-distribution [0 1 4 5 6 7 10 11 12 15 15 19 22])
;; => {1 6, 3 4, 0 1, 4 1} ???
I am pretty tired, so maybe I just donโt see it - but why do I get a different result .. the function is exactly what the expression above it does
Maybe I should restart the REPL
I donโt get it ๐
two 15s
whaaat
look at line 4
Sigh ๐
12 15 15 19
(Y)
Hope I can solve part 1 before I pass out ๐
TY
today was tough
Uhoh now I am afdraid of what to get for part 2 ๐
I need to get better at thinking in recursion
are you onto part 2?
How long take your programs for day 11?
Good morning !
If you find it difficult today, just remember: โCโest la vie !โ (thatโs life)
Part 2 of day 11 on the real input runs in just under a second for me. Probably could be a lot faster but good enough for me ๐
Pleasantly surprised that my initial attempt at part 2 actually finished in under a second, I was expecting a similar scenario as yesterday... "back to the drawing board"
Yup, I decided to do it in haskell yesterday and I really don't want to add mutation to it.
Tried the "precompute 8 layers" approach, haven't finished it yet, but calculating the layers takes about 1ms per layer or 8-10ms for all ten. Still feels like it should be possible to do that faster.
Can we see how you did?
I'll push it, trying to wrap it up. Also surprising find, looking up in 2-dimensional array is twice as slow as lookup in a two-dimensional vector
(defn getxy ^long [grid x y]
(.nth ^clojure.lang.PersistentVector (.nth ^clojure.lang.PersistentVector grid x) y))
(defn agetxy [^"[[J" arr ^long x ^long y]
(java.lang.reflect.Array/get (java.lang.reflect.Array/get arr x) y))
(let [dim 1000
arr (array-grid dim dim)]
(time
(doseq [x (range dim)
y (range dim)]
(agetxy arr x y))))
;; This takes ~150ms
(let [dim 1000
arr (mapv vec (array-grid dim dim))]
(time
(doseq [x (range dim)
y (range dim)]
(getxy arr x y))))
;; This only take ~75ms
seems there not really any way to generate direct array lookup bytecode
@vincent.cantin https://github.com/lambdaisland/aoc_2020/blob/main/src/lambdaisland/aoc_2020/puzzle11_alt.clj
Here it is, a lot of effort, and in the end still slower than my first solution. Then I went back and just ported the optimized nested vector lookup, and it cut the time for my original solution in half. So now I'm at 500ms for part 2, or about 1.5s with the "8 layers" approach
I think that the lack of eccentricity of the input data implies that the simple implementation is working fast enough.
The optimization is only saving some time when the lookup of the neighbors is costly, and it does not seem to be the case here.
I give up on trying to improve my solution any further, it makes the code look like ugly C, itโs unreadable and not interesting.
Thank you for spending the time to look into it.
A last cheap optimization to be considered: having the grid on a 1 dimensional vector. PRO: faster look up CONS: need to manually write code to make sure that the col index is correct + it looks ugly as hell. โฆ tried and rolled back :face_vomiting:
@plexus I fixed your array access function, it should look like that instead:
(defn agetxy ^long [^"[[J" arr x y]
(aget ^"[J" (aget arr x) y))
The benchmarking has a problem too, the result of the operation needs to be used, otherwise the JVM will just drop the body using dead code elimination.
Probably, the DCE is triggered when the read expression is fully understood statically and thatโs why the reflective calls where not removed - but they were slow anyway.
@plexus This is the correct way to bench, and even with the addition and transducing process overhead, it is still faster than your previous fastest.
(set! *unchecked-math* :warn-on-boxed)
(defn agetxy ^long [^"[[J" arr x y]
(aget ^"[J" (aget arr x) y))
(let [dim 1000
arr (make-array Long/TYPE dim dim)
_ (doseq [x (range dim)
y (range dim)]
#_ (aset ^"[[J" arr x y 1) ;; super slow - don't use this!
(aset ^"[J" (aget ^"[[J" arr x) y 1))
result (time
(transduce (map (fn [x]
(transduce (map (fn [y]
(agetxy arr x y)))
+
(range dim))))
+
(range dim)))]
(prn result))
Day 11 answers thread - Post your answers here
my solution runs in 3s and 6s, I am very curious to learn from the thread to find better data structures or approach https://github.com/nbardiuk/adventofcode/blob/master/2020/src/day11.clj
cache first-seen xys, and remove dots (floor) from the map. @nbardiuk
My runs in 0.6s and 1.2s with index of seen chairs. https://github.com/genmeblog/advent-of-code/blob/master/src/advent_of_code_2020/day11.clj
My messy original solution (a cleaned up and faster version is coming later) For the part 2, it scans the whole data in 8 directions in O(n) before updating the data in O(n) https://github.com/green-coder/advent-of-code-2020/blob/1df1043c19017850257d403000d9b6f5568fddee/src/aoc/day_11.clj
Here is my solution. The key is โall decisions are based on theย number of occupied seatsโฆโ https://github.com/zelark/AoC-2020/blob/master/src/zelark/aoc_2020/day_11.clj
My solution to Day 11. https://github.com/benfle/advent-of-code-2020/blob/main/day11.clj
TFW you think you'll speed it up by changing arrays in-place, and it's 100 times slower than updating vectors
Did you transient
/ persistent!
a lot?
@vincent.cantin I tried, but it didnโt help much
I benched my solution with Criterium: part 1 โ 882ms, and part 2 โ 772ms.
Can you bench my part 1 and tell me how fast it takes on your computer? (I think my computer is way slower than everybody else) https://github.com/green-coder/advent-of-code-2020/blob/01fc1785a26872c165302c463e26caceb93e82ff/src/aoc/day_11.clj
Sure!
In this version, I tried to leverage the transducers.
Part 1 โย 512.213845 ms Part 2 โ 2.664164 sec
Thank you ! It seems that your computer is exactly twice faster than mine.
As concise as I could get it, but it does take ~20s to run both parts https://github.com/Solaxun/aoc2020_clj/blob/main/src/aoc2020_clj/day11.clj
(time (solve adjacent-count 4)) "Elapsed time: 1488.883654 msecs"
(time (solve seen-count 5)) "Elapsed time: 2997.948211 msecs"
I was pretty happy with the functional composition in this one! (And the completely unnecessary selections
from math.combinatorics)
https://github.com/Dexterminator/advent-of-code-2020/blob/main/src/day11/core.clj
@pez https://clojurians.slack.com/archives/C0GLTDB2T/p1607681391129000?thread_ts=1607671401.120300&cid=C0GLTDB2T
after incorporating examples and suggestions I've managed to reduce runtime from 2s,6s to 0.6s,0.9s. What helped is to precompute near seats for each seat and split map of seat states into 3 sets (occupied, floor, seats)
@misha:I remove floor cells from the map.
So if I memoize my neighbour-of function, I get 3 secs for step 2. But can only do it once then, because on the second round it never finishes.
(def neighbours-of-
(fn
[{:keys [floor size]} seat]
(let [[rows cols] size]
(->> [[-1 -1] [0 -1] [1 -1] [-1 0] [1 0] [-1 1] [0 1] [1 1]]
(keep (fn look [direction]
(loop [delta direction]
(let [[new-r new-y] (map + seat delta)]
(when-not (or (neg? new-r)
(neg? new-y)
(> new-r rows)
(> new-y cols))
(if (floor [new-r new-y])
(recur (map + delta direction))
[new-r new-y]))))))
(into #{})))))
(def neighbours-of
(memoize neighbours-of-))
(defn occupied-neighbours-of
[ferry seat]
(->> (neighbours-of (select-keys ferry [:floor :size]) seat)
(filter #((:occupied-seats ferry) %))
(into #{})))
Iโm down to 2 secs now, with cached neighbours/first-seens. Canโt see where I have gains to make. I might have choosen the wrong data structure for the grid.
(defn parse-seats [rows chs]
(->> rows
(map-indexed (fn [y row]
(map-indexed (fn [x this-ch]
(when (chs this-ch)
[x y]))
row)))
(apply concat)
(remove nil?)
(into #{})))
(defn parse [input]
(->> input
(re-seq #"[.L#]+")
((fn parse-ferry
[rows]
(let [floor (parse-seats rows #{\.})]
{:floor floor
:occupied-seats (parse-seats rows #{\#})
:all-seats (clojure.set/difference (parse-seats rows #{\# \. \L}) floor)
:neighbours-of {}
:size [(count (first rows)) (count rows)]
:generations 0})))))
(defn neighbours-of-1
[seat]
(->> [[-1 -1] [0 -1] [1 -1] [-1 0] [1 0] [-1 1] [0 1] [1 1]]
(map (fn [direction]
(map + seat direction)))
(into #{})))
(defn occupied-neighbours-of-1
[ferry seat]
(->> (neighbours-of-1 seat)
(filter (fn [c]
((:occupied-seats ferry) c)))
(into #{})))
(defn neighbours-of
[{:keys [floor size]} seat]
(let [[rows cols] size]
(->> [[-1 -1] [0 -1] [1 -1] [-1 0] [1 0] [-1 1] [0 1] [1 1]]
(keep (fn look [direction]
(loop [delta direction]
(let [[new-r new-y] (map + seat delta)]
(when-not (or (neg? new-r)
(neg? new-y)
(> new-r rows)
(> new-y cols))
(if (floor [new-r new-y])
(recur (map + delta direction))
[new-r new-y]))))))
(into #{}))))
(defn occupied-neighbours-of
[ferry seat]
(->> (get-in ferry [:neighbours-of seat])
(clojure.set/intersection (:occupied-seats ferry))))
(defn render [ferry] ; TODO: This is broken
(let [[rows cols] (:size ferry)
pixels (for [row (range rows)
col (range cols)]
(cond
((:floor ferry) [row col]) "."
((:occupied-seats ferry) [row col]) "#"
:else "L"))]
(->> pixels
(partition 10)
(map #(apply str %)))))
(defn next-ferry
[ferry]
(-> ferry
(update :occupied-seats
(fn [_]
(->> (:all-seats ferry)
(filter (fn [seat]
(let [num-occupied-neighbours (count (occupied-neighbours-of ferry seat))]
(cond
(and (not ((:occupied-seats ferry) seat))
(zero? num-occupied-neighbours))
true
(and ((:occupied-seats ferry) seat)
(>= num-occupied-neighbours 5))
false
:else
((:occupied-seats ferry) seat)))))
(into #{}))))
(update :generations inc)))
(defn cache-neighbours [ferry]
(update ferry
:neighbours-of
#(into
%
(->> (:all-seats ferry)
(map (fn [seat]
{seat (neighbours-of ferry seat)}))
(into #{})))))
(comment
(def input (util/fetch-input 11))
(time (->> input
(parse)
(cache-neighbours)
((fn stabilize [ferry]
(loop [old-ferry ferry]
(let [new-ferry (next-ferry old-ferry)]
(if (= (:occupied-seats new-ferry)
(:occupied-seats old-ferry))
old-ferry
(recur new-ferry))))))
(:occupied-seats)
(count))))
Would anyone be able to give me some clues as to why my Day 11 Part 1 solution is so slow? I'm caching neighbours, but I'm still getting >2 seconds. I'm doing a fair amount of set operations, maybe that's it? https://github.com/RedPenguin101/aoc2020/blob/main/day11-2.clj
Part 2 final stage (probably filpped): https://raw.githubusercontent.com/genmeblog/advent-of-code/master/images/advent_of_code_2020/day11_far_222.jpg
definitely not my best work, but i never cared for game of life when I had to do it in college either https://github.com/listba/advent-of-code-2020/blob/master/clojure/src/aoc_2020/days/11.clj
Not gonna win at code golf but at least it works ๐ https://github.com/lambdaisland/aoc_2020/blob/main/src/lambdaisland/aoc_2020/puzzle11.clj
Impressive
I did not believe that the simple approach would be fast enough, so I spent a lot of time optimizing the algorithm and writing it down.
Nice pictures
I read on the subreddit that itโs indeed a tribonacci sequence, but not sure what that means for the solution
One more optimized approach I can think of is doing a single pass over the layout for each of the 8 directions, so you end up with 8 data structures with precomputed values for what the next chair in that direction is. I hope that makes sense ๐
that's what I did.
And, how fast is it?
a couple of seconds ... but my computer is a mac book air from 2011
hmmm that still feels like it should be faster
Note to self: learn the mutating operators ... assoc! and friends.
Second part (just executing how it is stated) takes 2 minutes but well, https://github.com/transducer/adventofcode/blob/master/src/adventofcode/2020/day11.clj. Not sure how to improve the speed.
I used an atom as acc inside a double for loop
yeah transients can help... not sure how much
Doing the optimised algorithm only with immutable datastructure in limited time is very hard.
I only thought about atom because I usually don't do mutable things. It's time to change that.
Other note for myself: add a reverse-range
function to my tool ns
or just use a step of -1
?
(range (dec n) -1 -1) .. with the adrenaline of the rush, a reverse-range function is wiser.
I might have a stab at doing the precomputing later, I feel like it should be possible to make it very fast without funky stuff like transitiens
The key is not to have to write 8 functions.
yeah, makes sense. I might write two, one for diagonal one for straight
the transient stuff is a good idea for opening your next stream
meh...
I've never come across a real life need for transients
but people in AoC may need it soon.
.. and that would be a good demonstration of the versatility of Clojure.
Part 1, kinda crufty.
;; get neighbor coords
(defn addpairs [[x1 y1] [x2 y2]] [(+ x1 x2) (+ y1 y2)])
(def surround [[1 0] [1 1] [0 1] [-1 1] [-1 0] [-1 -1] [0 -1] [1 -1]])
(defn neighbors [posn] (map addpairs (repeat posn) surround))
;; get / set seat value
(defn getxy [brd [x y]] (get (get brd y) x))
(defn setxy [brd [x y] v]
(let [row (get brd y)
newrow (apply str (assoc (vec row) x v))]
(assoc brd y newrow)))
(defn countneighbors? [brd seat]
(count (filter #{\#} (map (partial getxy brd) (neighbors seat)))))
(defn update1seat [brd seat]
(let [seatstatus (getxy brd seat)
adjseats (countneighbors? brd seat)]
(case seatstatus
\L (if (= 0 adjseats) \# \L)
\# (if (>= adjseats 4) \L \#)
\. \.)))
(defn step1 [brd]
(let [dimx (count (first brd))
dimy (count brd)
allseats (for [y (range dimy) x (range dimx)] [x y])
updatedseats (map (partial update1seat brd) allseats)]
(vec (map (partial apply str) (partition dimx updatedseats)))))
(->> input
(iterate step1)
(partition 2 1)
(drop-while #(apply not= %))
ffirst
(apply str)
(filter #{\#})
count)
;; => 2412
๐ฑ I replaced get-in
by 2 consecutive get
and I cut my runtime by half !!!
My bed book this weekend will be http://clojure-goes-fast.com/blog/
as well as https://github.com/bsless/clj-fast
From slower to faster:
(swap! acc update row assoc col c)
(swap! acc assoc-in [row col] c)
(swap! acc (fn [grid]
(update grid row (fn [row]
(assoc row col c))))))))
My solution for Day 11, step 2 takes 6 seconds to runโฆ But at least I have the stars.
I've been getting some good mileage out of iterate
in some of the problems. Haven't gotten the opportunity to use it that much before, it's so nice when the problen fits! I started writing the body for a loop/recur and then realised that I could just feed it to iterate and it turned out much nicer
Ah nice yeah I forgot about iterate
I feel like I should use a proper matrix library instead of the usual vector of vectors
What's the best library for this kind of stuff atm?
@andrea.crotti The ones I've seen (like core.matrix
) are a bit more math focused I'd say. One of the main matrix processing tools in core is https://clojuredocs.org/clojure.walk/prewalk
I don't really think either is very useful for today's problem though since you need to keep track of indices and stuff
If you find a way to use one of the walk functions in a nice way in the problem I'd love to hear it! That was the ugliest part of my solution
@vincent.cantin may the lisp gods forgive this abomination inspired by your comment:
(defmacro update-in-faster [m [k & ks] f & args]
(if ks
`((fn [m#]
(assoc m# ~k (update-in-faster (m# ~k) ~ks ~f ~@args)))
~m)
`(update ~m ~k ~f ~@args)))