This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-12-12
Channels
- # adventofcode (112)
- # architecture (1)
- # beginners (55)
- # boot (26)
- # cider (19)
- # cljs-dev (19)
- # cljsjs (1)
- # cljsrn (7)
- # clojure (140)
- # clojure-android (3)
- # clojure-austin (3)
- # clojure-china (3)
- # clojure-gamedev (1)
- # clojure-greece (43)
- # clojure-spec (75)
- # clojure-sweden (5)
- # clojure-uk (21)
- # clojurescript (66)
- # core-async (2)
- # core-logic (1)
- # cursive (63)
- # datascript (5)
- # datomic (4)
- # devcards (2)
- # duct (13)
- # editors (5)
- # emacs (9)
- # figwheel (4)
- # fulcro (42)
- # graphql (25)
- # immutant (7)
- # jobs (29)
- # leiningen (13)
- # lumo (7)
- # numerical-computing (3)
- # off-topic (22)
- # om (2)
- # onyx (25)
- # pedestal (3)
- # re-frame (14)
- # reagent (20)
- # remote-jobs (1)
- # ring-swagger (3)
- # rum (12)
- # shadow-cljs (9)
- # uncomplicate (1)
- # unrepl (6)
https://github.com/minikomi/advent-of-code/blob/master/src/advent/2017/day12.clj#L27-L34
I'm not too strong at graph work. I took some time to look for something that could do it for me š
Like peek
and pop
in @minikomiās solution: wouldn't use it because they're not something I'm readily aware of.
Yeah, that was a double-edged sword, I think. You could end up walking in a circle if you didn't keep track of what you'd already seen.
If you want to dig deeper but don't want full spoilers, I'd recommended reading up on Connected Components in graph theory
Btw, @fellshard, did you perhaps mean data-prioritymap?
I wonder if @fellshard was referring to this one https://en.wikipedia.org/wiki/Disjoint-set_data_structure
I didnāt implement my own graph for this; ubergraph for the rescue. https://github.com/orestis/adventofcode/blob/master/clojure/aoc/src/aoc/2017_day12.clj
I have to implement a hairy layout algorithm for work today; donāt have the time to actually implement a graph algorithm!
150-200ms to the answer for part 2, which is then a filter away to give the answer to part 1.
To my shame, I admit I tried various functions of ubergraph.alg
until I found something that seemed to a useful result.
advent.2017.day12> (c/quick-bench (solve2 input-raw))
Evaluation count : 78 in 6 samples of 13 calls.
Execution time mean : 8.241557 ms
https://github.com/sciyoshi/advent-of-rust-2017/blob/master/src/day12/mod.rs wow, rust has funky tools to use
No more time to spend on that tonight. Never mind, got it worked out. Pseudograph
is sufficient.
Always highlight the entrance points to your library in big, bold letters. Same issue I had when I was working with Google Dataflow / Apache Beam for a client.
@fellshard link to your solution?
Day 12 with Datascript: https://github.com/thegeez/clj-advent-of-code-2017/blob/master/src/advent/day12.clj
fast enough, part 1 224ms, part 2 1.4seconds. both including parsing input, but not slurping input
https://github.com/bhauman/advent-of-clojure-2016/blob/master/src/advent_of_clojure_2017/day12.clj
and why did you need disj here? https://github.com/bhauman/advent-of-clojure-2016/blob/master/src/advent_of_clojure_2017/day12.clj#L16
it really doesn't need to be there, as it doesn't have a big affect, it only doubles the leaf step
(tree-seq
(constantly true)
{0 #{2 3}
2 #{4 5}
3 #{6 7}}
0)
how does it see this as a tree? I donāt understand it yet.The second function converts a value to children 0 comes in, you get children 2 and 3 2 and 3 come in, they give 4 and 5; and 6 and 7 respectively.
so normally you pass in a tree, but now only the number 0 is used as the ārootā of the tree.
then children are looked up in this ātreeā by (children 0)
which is then the tree which is walked. This is brilliant.
yeah. we are relying on the fact here that branch?
can be side-effecting (to do the atom bookkeeping), but itās probably a safe assumption
My day 12: https://github.com/mfikes/advent-of-code/blob/master/src/advent_2017/day_12.cljc
@borkdude the quick-bench for part-2 was wrong. copy-paste error late at night. I get 25-29ms for part-2.
@grzm Nice solution! Only suggestion I'd make is to use a volatile instead of atom in prog-group
.
I used a volatile
for a second but then thought that there is no reason that tree-seq
couldn't be run in parallel and so preferred atom
Just to learn/reinforce another area of the library. To be able to choose the right tool for the job I at least need to know what tools are out there. Right now I've got big holes in my knowledge of the core library.
Are these times right? Are people really solving this in less than 3 minutes? http://adventofcode.com/2017/leaderboard/day/12
@grzm Nah, volatile is only better than atom for perf reasons. So it may become an idiom to use volatiles for local function state and atoms for defs. (Essentially a thing where you don't think about it.)
For those curious, the algorithm I'm using comes in at 10.6 ms for part 1 and 11.4 ms for part 2 if you exclude the I/O and parsing (and apply the algorithm to parsed data)
(It is easy to convert that algorithm from a functional one to an imperative one that bashes on a mutable array and I think that works much more quickly.)
https://github.com/armstnp/advent-of-code-2017/blob/master/src/advent_of_code_2017/day-12.clj https://github.com/armstnp/advent-of-code-2017/blob/master/src/advent_of_code_2017/day-12-union-find.clj https://github.com/armstnp/advent-of-code-2017/blob/master/src/advent_of_code_2017/day-12-jgrapht.clj
@fellshard Is jgrapht any faster than the others?
I'd expect union-find to be fastest, if only by dint of skipping the graph entirely. Union-find has inverse-Ackermann amortized performance.
If you do a mutable union-find, part 1 comes in at 539 µs for me and part 2 at 1.12 ms.
Here is the code that I benchmarked to produce those numbers: https://gist.github.com/mfikes/fc2e94db229b53d68f243cd7b8c253b5
Yeah, that's one thing I noted - the union-find
library I picked up has no capacity to leverage the bind-to-root optimization you'd normally use. So I suppose that makes its perf more like log(n).
Union-find seems like a good example of an imperative algorithm from the 60s-70s which is difficult to efficiently express in a functional way.
(I haven't found a way to write it in "normal" Clojure that runs as fast as bashing on arrays.)
You could always pass the tree back with every operation. Perhaps hiding it behind an atom would allow you to make modifications that are 'effectively immutable'?
Trust the contract to hold properties, I guess, with a dash of testing for certainty
@fellshard I am passing the tree back. Perhaps it is the case that I have a functional version that has the same computational complexity and Iām just seeing a constant overhead associated with working with persistent data structures. Iām going to scrutinize it a bit more. Here it is for reference https://github.com/mfikes/advent-of-code/blob/master/src/advent_2017/day_12.cljc#L19-L37
Well, the good news is that it is possible to get part from 10.6 ms down to 1.35 ms by using transients, which compares well to the imperative version that runs in 539 µs. https://gist.github.com/mfikes/c4cf04d10bbfc8c2862fe15a58642501
I call it union-find, but it has other names https://en.wikipedia.org/wiki/Disjoint-set_data_structure