adventofcode

Happy Advent 2020! Please put answers in the pinned threads or create one if it does not exist yet. | https://github.com/adventofcode-clojurians/adventofcode-clojurians | Join the private leaderboard with code 217019-4a55b8eb
quoll 2018-12-05T01:17:27.530800Z

I disagree

quoll 2018-12-05T01:17:58.531Z

yes, it’s terse. It’s not much nicer than a top level defn (or def)

1✔️
baritonehands 2018-12-05T04:06:43.531400Z

I did this for day 3:

(map (comp #(Integer/parseInt %)
            (memfn trim)))

baritonehands 2018-12-05T04:08:32.532200Z

I did this for the padded numbers:

(defn to-int [s]
  (Integer/parseInt
    (if (= (.charAt s 0) "0")
      (.substring s 1)
      s)))

baritonehands 2018-12-05T04:08:46.532500Z

surprised that my char to string comparison worked

baritonehands 2018-12-05T04:10:33.532600Z

Is (reduce + ... any faster?

baritonehands 2018-12-05T04:11:40.532800Z

Looks like yes

baritonehands 2018-12-05T04:11:42.533Z

user=> (time (apply + (repeat 10000000 1)))
"Elapsed time: 526.222753 msecs"
10000000
user=> (time (apply + (repeat 10000000 1)))
"Elapsed time: 656.773543 msecs"
10000000
user=> (time (reduce + (repeat 10000000 1)))
"Elapsed time: 178.887459 msecs"
10000000
user=> (time (reduce + (repeat 10000000 1)))
"Elapsed time: 47.995412 msecs"
10000000
user=> (time (reduce + (repeat 10000000 1)))
"Elapsed time: 39.555085 msecs"
10000000

athos 2018-12-05T04:20:37.533800Z

(= \0 "0") ;=> false So, your function is equivalent to (Integer/parseInt s)

baritonehands 2018-12-05T04:21:12.534Z

good to know

baritonehands 2018-12-05T04:21:46.534300Z

(Integer/parseInt "09")
=> 9

baritonehands 2018-12-05T04:21:52.534500Z

accidentally on purpose

1😉
2018-12-05T06:32:36.535600Z

Anyone getting subsecond timings on part 1?

3Jane 2018-12-05T11:58:57.555100Z

fwiw (since my result is incorrect) locally my solution works in ~100 msec

3Jane 2018-12-05T12:00:30.556600Z

Two string builders acting as stacks. “Right” initialised to original string, “left” empty. Action consists of examining heads of stacks and then: - if reducible, remove both - if non-reducible, move char from “right” to “left” Repeat until “right” is empty.

3Jane 2018-12-05T12:15:53.561800Z

…so yeah, I forgot to trim the input 🙂 now it works.

3Jane 2018-12-05T12:19:18.562500Z

(it’d probably be even more performant if I was operating at the last element of the “left” sb)

2018-12-05T12:32:55.562700Z

I finally got mine working.

2018-12-05T12:33:03.562900Z

One string builder - 40ms.

3Jane 2018-12-05T12:42:28.565900Z

deleting from the middle is efficient?

2018-12-05T12:47:33.567300Z

StringBuilder is mutable, so it mostly doesn’t matter.

3Jane 2018-12-05T12:51:31.569Z

…right, well, it matters how the memory is actually allocated

3Jane 2018-12-05T12:52:39.569900Z

but it sounds like it works just fine 😄 thanks!

2018-12-05T14:17:47.586800Z

right, if they’re having to shift mem around

2018-12-05T14:18:21.587Z

So it’s back by System.arrayCopy — so towards the start of the string it’s having to do a fair number of (native) copy operations

2018-12-05T14:39:45.590200Z

So… @lady3janepl you should look at @me1740’s solution! More in line with what you were saying.

2018-12-05T06:33:13.536300Z

I’m basically bashing on a StringBuilder in a loop/recur and coming up w/ ~4 sec.

2018-12-05T06:33:40.536600Z

seriously no idea how I could speed that up

2018-12-05T06:33:52.536900Z

but I hear of others in the ms range

Mario C. 2018-12-05T06:36:36.537400Z

Not having state is making these puzzles really difficult for me. 😅

2018-12-05T06:37:06.537800Z

You should join the twitch stream! That’s part of why I’m doing it!

2018-12-05T06:37:31.538400Z

alas, I’ve devolved into a PLOP animal to get sweet speed

Mario C. 2018-12-05T06:39:09.539200Z

Are the videos saved?

2018-12-05T06:39:45.539800Z

yep!

1👍
2018-12-05T06:39:55.540100Z

https://www.twitch.tv/timpote/videos

ClashTheBunny 2018-12-05T06:39:55.540200Z

I couldn't get speed this time at all: https://gitlab.com/randall.mason/advent-of-code/blob/master/src/advent_of_code_2018/day05.clj Ended up with 845 seconds for all tests on my Pixel Slate.

2018-12-05T06:40:38.540900Z

that’s some cond 🙂

ClashTheBunny 2018-12-05T06:41:00.541300Z

lol, yes!

2018-12-05T06:41:03.541500Z

ah atom bashing as well I see

ClashTheBunny 2018-12-05T06:41:27.542600Z

Yeah, that shouldn't be needed, but I get a smaller answer without it.

2018-12-05T06:41:31.542800Z

yeah I didn’t know what else to do for this one besides bash some memory around

ClashTheBunny 2018-12-05T06:41:40.543Z

I'll refine it on the train tomorrow.

2018-12-05T07:56:02.545100Z

wow i went through a lot of pain there, i had a newline at the end and I was off by 1 for #5 🤦 that said, I switched from a functional approach to using an ArrayList to make removal easier, and was able to run the algorithm in < 1s on my mac https://github.com/rymndhng/advent-of-clojure/blob/master/src/advent_2018/05.clj

1👍1☝️
pesterhazy 2018-12-05T08:08:23.545400Z

Elapsed time: 32991.632126 msecs -- https://github.com/pesterhazy/advent2018/blob/master/src/advent/puzzle05.clj#L43

pesterhazy 2018-12-05T08:08:40.545800Z

Not the fastest 🙂

pesterhazy 2018-12-05T08:09:45.546500Z

Make it work, make it right, make it fast, right? Except I won't make it right or fast because it's just a kata...

2018-12-05T08:10:26.547300Z

totally haha, it's pretty cool to see the different approaches folks went for

pesterhazy 2018-12-05T08:10:32.547500Z

Honestly the main source of ugliness in my solution is the lack of cond-let in clojure.core

pesterhazy 2018-12-05T08:11:38.548100Z

I went for a pure function, which is never going to be super fast for this type of problem

2018-12-05T08:13:40.549600Z

i agree with you for the pure functional part -- I ended up going with an arraylist because removing elements by index via subvec + concat is too cumbersome

pesterhazy 2018-12-05T08:17:31.550300Z

I ended up setting them to nil and skipping over nils when considering neighbors

helios 2018-12-05T08:43:05.551200Z

it's not super fast but i'm happy i got to use partition-all 😛

genmeblog 2018-12-05T10:11:14.551800Z

My day5: "Elapsed time: 7839.507737 msecs" https://github.com/genmeblog/advent-of-code-2018/blob/master/src/advent_of_code_2018/day05.clj

3Jane 2018-12-05T11:54:54.553500Z

guys, have you maybe got a regexp that I could check against my results?

3Jane 2018-12-05T11:55:31.554Z

something’s wrong in that I’ve got too many characters, but I can’t tell what. superficial examples seem fine.

3Jane 2018-12-05T11:58:57.555100Z

fwiw (since my result is incorrect) locally my solution works in ~100 msec

3Jane 2018-12-05T12:00:30.556600Z

Two string builders acting as stacks. “Right” initialised to original string, “left” empty. Action consists of examining heads of stacks and then: - if reducible, remove both - if non-reducible, move char from “right” to “left” Repeat until “right” is empty.

rmprescott 2018-12-05T12:03:50.560200Z

I really enjoyed reading @mfikes solution for day4. A few things that stuck out: - the concision of naming a map in the domain space: guard-id-&gt;minutes - learned a new trick: destructuring in a let block when extracting the key and value of interest - how much this approach reads like a proof

erwinrooijakkers 2018-12-05T12:10:40.561300Z

Also check out the reduce, reduced, reductions for part 2 of day 1 of @bhauman https://github.com/bhauman/advent-of-clojure/blob/master/src/advent-2018/day01.clj#L18

3Jane 2018-12-05T12:15:05.561700Z

trim :woman-facepalming:

1☝️1😱
3Jane 2018-12-05T12:15:53.561800Z

…so yeah, I forgot to trim the input 🙂 now it works.

3Jane 2018-12-05T12:19:18.562500Z

(it’d probably be even more performant if I was operating at the last element of the “left” sb)

2018-12-05T12:32:55.562700Z

I finally got mine working.

2018-12-05T12:33:03.562900Z

One string builder - 40ms.

2018-12-05T12:36:44.564500Z

So I did eventually find a way to get part 1 down to ~40ms.

athos 2018-12-05T12:36:50.564800Z

The combination of sorted-map and subseq works very nicely 😃

2018-12-05T12:36:52.564900Z

took a few minutes tho

borkdude 2018-12-05T12:42:15.565500Z

Clojure:

Testing aoc.y2018.d05.borkdude
part-2 took 5546.88 msecs
part-1 took 160.69 msecs
CLJS:
Testing aoc.y2018.d05.borkdude
part-1 took 107.00 msecs
part-2 took 1896.00 msecs

borkdude 2018-12-05T12:42:28.565800Z

funny enough, Node is faster than the JVM here…

3Jane 2018-12-05T12:42:28.565900Z

deleting from the middle is efficient?

genmeblog 2018-12-05T12:45:48.566700Z

@athos really fast solution... nice

borkdude 2018-12-05T12:46:57.567Z

@athos where can we see the time for your solution?

2018-12-05T12:47:33.567300Z

StringBuilder is mutable, so it mostly doesn’t matter.

2018-12-05T12:47:56.567600Z

til: subseq

athos 2018-12-05T12:49:59.568100Z

On my MBP:

user=&gt; (time (day05/solve1 (slurp (io/resource "input05.txt"))))
"Elapsed time: 414.086682 msecs"
9238
user=&gt; (time (day05/solve2 (slurp (io/resource "input05.txt"))))
"Elapsed time: 1936.757214 msecs"
4052

borkdude 2018-12-05T12:50:37.568300Z

cool.

borkdude 2018-12-05T12:50:51.568500Z

oh you’re using pmap. no can do in cljs

3Jane 2018-12-05T12:51:31.569Z

…right, well, it matters how the memory is actually allocated

borkdude 2018-12-05T12:52:01.569600Z

with pmap I get on clj:

Testing aoc.y2018.d05.borkdude
part-2 took 2900.71 msecs
part-1 took 160.96 msecs

athos 2018-12-05T12:52:23.569800Z

👍

3Jane 2018-12-05T12:52:39.569900Z

but it sounds like it works just fine 😄 thanks!

borkdude 2018-12-05T12:58:31.570600Z

Hmm, I see one of my tests get killed now regularly on CircleCI:

Testing aoc.y2018.d04.dfuenzalida
part-2 took 2706.55 msecs
Killed
Maybe they use some sort of time out?

borkdude 2018-12-05T13:02:08.570800Z

could be a memory issue.

ihabunek 2018-12-05T13:06:29.571200Z

Advent of code 2018, day05
Part 1 "Elapsed time: 107.586455 msecs"
Part 2 "Elapsed time: 2453.459493 msecs"

ihabunek 2018-12-05T13:07:03.571900Z

now to look what everyone else did, cause I'm certain this can be faster

ihabunek 2018-12-05T13:07:16.572300Z

i'm half tempted to solve it using a transient vector but not sure i want to

borkdude 2018-12-05T13:09:05.572900Z

@ihabunek cool, our solutions are almost identical

ihabunek 2018-12-05T13:09:12.573200Z

yeah, just wanted to comment 🙂

ihabunek 2018-12-05T13:09:28.573800Z

pop and peek would make mine less verbose

ihabunek 2018-12-05T13:10:05.574500Z

i first did mine in elixir, and used regex search&replace, but it took 6 minutes for part 2 ^^;

benoit 2018-12-05T13:13:44.575400Z

My solution for Day5. https://github.com/benfle/advent-of-code-2018/blob/master/day5.clj

ihabunek 2018-12-05T13:14:11.576300Z

weird... i just tried to optimize by converting the input to list of ints instead of chars which I had my logic was that i wouldn't have to convert to int when checking if two chars react but it takes 4 times as long ...

benoit 2018-12-05T13:14:29.576600Z

17ms for part 1 and 350 ms for part 2.

ihabunek 2018-12-05T13:14:34.576800Z

nice!

benoit 2018-12-05T13:17:05.578600Z

I think the key to a fast functional solution is to build the reduced polymer in reverse to make the backtracking faster.

ihabunek 2018-12-05T13:18:07.578900Z

ah, right

ihabunek 2018-12-05T13:18:14.579200Z

will try it out

pesterhazy 2018-12-05T13:33:43.579400Z

what the devil is subseq?

pesterhazy 2018-12-05T13:40:18.579800Z

Clojure's API surface is positively huge

athos 2018-12-05T13:52:48.580200Z

clojure.core/subseq
([sc test key] [sc start-test start-key end-test end-key])
  sc must be a sorted collection, test(s) one of &lt;, &lt;=, &gt; or
  &gt;=. Returns a seq of those entries with keys ek for
  which (test (.. sc comparator (compare ek key)) 0) is true

athos 2018-12-05T13:52:52.580400Z

I used it to find the greatest key that is less than a certain key in a sorted map.

genmeblog 2018-12-05T13:59:05.583500Z

I took @me1740 idea and got 220ms for both using pmap

2018-12-05T14:02:37.585700Z

Hmm. 17ms/350ms is quite fast. My naive solution was way too slow, (I left it running over night, but it probably ended up finishing after an hour or so). I optimized while it was running and got it down to 500ms/20s. But, I guess I still missed something to get those kinds of speeds

genmeblog 2018-12-05T14:04:21.586100Z

without pmap I have around 450ms

taylor 2018-12-05T14:16:37.586600Z

https://www.youtube.com/watch?v=9OfLNCWM_yA day 4 visualized

2
2018-12-05T14:17:47.586800Z

right, if they’re having to shift mem around

2018-12-05T14:18:21.587Z

So it’s back by System.arrayCopy — so towards the start of the string it’s having to do a fair number of (native) copy operations

2018-12-05T14:21:40.589200Z

@me1740 wtf is this black magic???

benoit 2018-12-05T14:34:58.590100Z

divide and conquer + being more careful about data structure when the first approach was too slow

2018-12-05T14:39:45.590200Z

So… @lady3janepl you should look at @me1740’s solution! More in line with what you were saying.

2018-12-05T14:40:36.590800Z

I feel like I just got completely schooled.

2018-12-05T14:41:15.591500Z

Me, a simpleton: “Linked lists aren’t fast.” @me1740: “Hold my beer.”

2018-12-05T14:42:54.592200Z

So you can get similar performance w/o having to reverse if you use a vector as your “reduced queue.”

benoit 2018-12-05T14:44:05.592900Z

Yes but I would have had to make sure all my internal operations return a vec 🙂

benoit 2018-12-05T14:44:31.593200Z

But it's probably better all vec, yes.

2018-12-05T14:44:46.593400Z

nah, pretty much the same

2018-12-05T14:45:15.593800Z

perhaps even faster as a linked list because there’s never a re-allocation

2018-12-05T14:45:20.594Z

(guessing)

2018-12-05T14:46:53.594600Z

You can pretty much change that reduced queue at will tho. It’s a local and only uses conj.

2018-12-05T14:48:19.595600Z

criterium says that of [] PersistentQueue/EMPTY and '()'() is between 30-40% faster

2018-12-05T14:48:30.595800Z

fun stuff

2018-12-05T14:49:14.596500Z

Thanks @me1740! A valuable lesson was taught today.

benoit 2018-12-05T14:49:16.596600Z

But conj doesn't behave the same for all those types.

2018-12-05T14:49:37.597Z

right, so it’s a matter of whether you reverse at the end or not

benoit 2018-12-05T14:49:57.597500Z

ok

2018-12-05T14:50:16.597900Z

iiuc

Björn Ebbinghaus 2018-12-05T14:51:03.598800Z

@me1740 Are you timing it multiple times?

benoit 2018-12-05T14:51:28.599500Z

I think if you switch from list to vec, you will have a few things to change in the code to make sure all the internal ops return vecs for example.

2018-12-05T14:51:37.600Z

I ran criterium on it: getting an avg of 9ms on my sample in 66 calls

adammiller 2018-12-05T14:52:06.601400Z

Just in case anyone else has this issue, there is an extra space (or possibly return char) at the end of their input file. All my tests were working but I was getting the wrong answer and starting to get a bit frustrated before I checked that!

2018-12-05T14:52:24.602200Z

@me1740 ah I did one more thing!

adammiller 2018-12-05T14:52:27.602400Z

Guess I should've verified my input!

benoit 2018-12-05T14:52:35.602600Z

@mroerni No as soon as I get the result in a "reasonable" amount of time, I'm done.

2018-12-05T14:52:42.603Z

I changed rest and first to pop and peek respectively

2018-12-05T14:53:09.604200Z

so everything returns the same data structure

Björn Ebbinghaus 2018-12-05T14:53:22.604600Z

I am getting 6ms/400ms, but only on later runs (https://github.com/MrOerni/advent-of-code-2018/blob/master/src/advent_of_code_2018/day05.clj)

2018-12-05T14:53:32.605200Z

(just on operations on the reduced variable`)

2018-12-05T14:55:20.606300Z

@mroerni ah so, perhaps because you just return the count instead of allocating a new string?

benoit 2018-12-05T14:55:22.606500Z

@potetm good to know

2018-12-05T14:55:22.606600Z

lemme try

2018-12-05T14:58:15.606800Z

nope

2018-12-05T14:58:32.607200Z

it’s just faster

2018-12-05T15:08:01.608500Z

holy moly @mroerni

2018-12-05T15:08:12.608900Z

the quick-bench of yours is 3ms

Björn Ebbinghaus 2018-12-05T15:08:30.609300Z

Using pmap for task 2, halved the time. (on a 2/4 Core machine)

2018-12-05T15:08:33.609400Z

very clever btw

2018-12-05T15:09:23.610600Z

pre-emptive conj on tail, then compare and drop — ensures a single pass across the input

2018-12-05T15:09:41.610900Z

no backtracking

Björn Ebbinghaus 2018-12-05T15:10:58.611100Z

Thank you. 🙂

2018-12-05T15:15:58.612400Z

@me1740 and @mroerni I will be mentioning both of you on Friday’s stream. I’ll probably code Björn’s implementation actually.

benoit 2018-12-05T15:16:57.612600Z

:thumbsup:

ihabunek 2018-12-05T15:19:06.613Z

slightly offtopic, but this solution in elixir is delightful: https://gist.github.com/sasa1977/d70f0986bf72bb94048d37843d11b4a4#file-day5-exs-L21-L28

ihabunek 2018-12-05T15:19:24.613600Z

pure recursion, no "stepping back", under 1 s including VM startup

2018-12-05T15:28:53.614Z

I’m surprised that Elixir solution doesn’t blow the stack

2018-12-05T15:29:34.614700Z

I must not understand it. It looks like it recurs to the end of the string and unrolls.

2018-12-05T16:00:19.615900Z

found a pythonista that did a reduction version of Björn’s algo

pesterhazy 2018-12-05T16:23:09.616400Z

maybe it just has a large stack!

ihabunek 2018-12-05T16:32:34.617Z

tried doing the same in clojure, but blew the stack

ihabunek 2018-12-05T16:32:46.617300Z

so yeah, large stack i guess

ClashTheBunny 2018-12-05T16:33:27.617900Z

Tail recursion never blows the stack. Did you try to call the function, or did you recur?

2018-12-05T16:34:23.618200Z

I don't think this could be TCO

1☝️
2018-12-05T16:34:35.618600Z

each stack relies on the result of the next

2018-12-05T16:34:37.618800Z

iiuc

2018-12-05T16:41:13.620300Z

perhaps why^?

2018-12-05T16:42:17.620500Z

https://i.imgflip.com/2o8hh1.jpg

heyarne 2018-12-05T16:42:40.620800Z

i'm solving day 5 and it's super slow at the moment (part 1 takes ~30 seconds) + it causes a stack overflow error, even though i'm not sure where i even build up such a large stack. if anybody wants to take a look at it I'd appreciate that https://github.com/heyarne/advent-of-code-2018/blob/86360a4/src/day_5.clj

genmeblog 2018-12-05T17:21:39.621900Z

@arne-clojurians SO can be caused by concat in your case.

taylor 2018-12-05T17:24:05.623100Z

yeah, b/c when you recursively construct sequences with concat, it keeps building up unrealized "thunks" of the lazy sequences. It's definitely not something that looks obviously problematic

mfikes 2018-12-05T17:51:49.624400Z

Hah. Seeing all the great timings for this one in the backlog. I may revisit my solution (it is dog slow).

taylor 2018-12-05T17:54:30.625Z

my first solution used trampoline and part 1 took ~9s...

benoit 2018-12-05T18:12:56.626200Z

@mfikes The fixed-point thingy is nice. And I found your solution the most readable so far 🙂

benoit 2018-12-05T18:13:14.626600Z

But it means you do multiple pass I believe.

mfikes 2018-12-05T18:14:16.627800Z

Thanks. I'm sitting here pondering the fundamental algorithm, trying to avoid gleaning anything from the above. But yeah, I'm zeroing in on the concept that I'm making way too many multiple passes, without even any ability to backtrack a little.

benoit 2018-12-05T18:14:38.628Z

Ok I shut up 🙂

mfikes 2018-12-05T18:27:01.628700Z

Mostly, I want to get it to be fast enough to contribute to Advent of CLJC. (Otherwise, it could suck up 5 minutes of CI time.)

2018-12-05T18:50:04.629600Z

Posting cause I’m not sure I saw anyone w/ that particular approach.

ClashTheBunny 2018-12-05T19:27:16.630700Z

I'm much happier with my 2nd version of day 5, but it is still very slow and still has a bunch of cond: https://gitlab.com/randall.mason/advent-of-code/blob/master/src/advent_of_code_2018/day05.clj

pesterhazy 2018-12-05T19:55:13.632200Z

@mfikes my solution ended up very slow as well (33s for part II) - I wouldn't have predicted it

borkdude 2018-12-05T20:27:55.632500Z

Testing aoc.y2018.d05.bgrabow
part-1 took 33.00 msecs
part-2 took 359.00 msecs

Testing aoc.y2018.d05.borkdude
part-1 took 60.00 msecs
part-2 took 3392.00 msecs

Testing aoc.y2018.d05.mfikes
part-1 took 3505.00 msecs
part-2 took 75629.00 msecs

borkdude 2018-12-05T20:28:01.632700Z

(on node)

borkdude 2018-12-05T20:28:34.633200Z

I haven’t looked at bgrabow’s solution yet, but it’s killing all other solutions 😉

borkdude 2018-12-05T20:29:11.633500Z

on JVM:

Testing aoc.y2018.d05.bgrabow
part-2 took 110.05 msecs
part-1 took 12.13 msecs

Testing aoc.y2018.d05.borkdude
part-2 took 4697.16 msecs
part-1 took 166.39 msecs

Testing aoc.y2018.d05.mfikes
part-2 took 24970.50 msecs
part-1 took 1035.49 msecs

ClashTheBunny 2018-12-05T20:33:33.634Z

What are the specs on the machine that runs that?

2018-12-05T20:41:06.634800Z

@borkdude Wait, I'm making it faster! Borrowing heavily from the discussion above though.

genmeblog 2018-12-05T20:48:25.635100Z

faster than now? 🙂 wow

borkdude 2018-12-05T20:52:19.635600Z

@clashthebunny you can click the CircleCI icon on https://github.com/borkdude/advent-of-cljc

ClashTheBunny 2018-12-05T21:04:48.636100Z

It just says 4GB of ram and two vCPU.

borkdude 2018-12-05T21:06:38.636400Z

that’s all I know

2018-12-05T21:14:51.637800Z

I'm trying to introduce parallelism to part 1, but nothing I'm trying is faster than the single-threaded approach. I'm guessing it's because there are some reducible polymer chains that are very long, so they stretch beyond the boundaries of a parallel chunk.

2018-12-05T21:16:29.638600Z

So there is quite a bit of work to do in joining the chunks, which must be done serially.

jduhamel 2018-12-05T21:22:57.639800Z

@mfikes So I’m learning clojure by reading the advent of code stuff. Had a question about the “:log” in your day 4. also why {:log []} What does that do?

jduhamel 2018-12-05T21:23:37.640700Z

if there is a place you can point me to, that would be great.

mfikes 2018-12-05T21:24:47.642Z

There's an accumulator in there which essentially holds several things. When it is running the accumulator will have something like:

{:log [{:guard-id 1 :start 5 :end 12} {:guard-id 7 :start 7 :end 13}]
 :guard-id 3
 :start 12}

mfikes 2018-12-05T21:25:18.642700Z

So there's a vector under the :log key that grows

jduhamel 2018-12-05T21:25:44.643200Z

got it, so the first (:log kicks off the key.

jduhamel 2018-12-05T21:26:00.643700Z

the {:log [] } is the arg to the reducer. Thanks a bunch

mfikes 2018-12-05T21:27:09.644100Z

Yep, it just sets it up with a vector in there

jduhamel 2018-12-05T21:29:03.644300Z

Thanks back to work on day5.

fellshard 2018-12-05T21:44:33.644400Z

I'd considered fork-join for this, but yeah, that'd only be suitable if there's a lot of local adjacent pairs throughout the entire polymer.

adammiller 2018-12-05T22:04:53.645400Z

I have part 1 around 8ms but part 2 is still up in the 500's....Have some ideas to improve that but not sure if i'll get around to it today.

borkdude 2018-12-05T22:06:26.645900Z

@adammiller always welcome to make a PR with improvements, even next year

adammiller 2018-12-05T22:09:31.647100Z

looks like on the latest test it ran part 1 in 7.53ms.... Should definitely be able to improve part 2 since i use part 1 in that.

mfikes 2018-12-05T22:10:44.648200Z

Hammock time led me to the kernel of single pass idea. That took me a while to see. Presumably others in the backlog are using similar approach. (Will read solns.) https://github.com/mfikes/advent-of-code/blob/master/src/advent_2018/day_05.cljc

1🤘1👌
adammiller 2018-12-05T22:11:04.648900Z

interesting that mine was significantly slower than @borkdude and @ben.grabow on the cljs side though

adammiller 2018-12-05T22:11:59.649300Z

that looks pretty similar to what I had done on part 1 @mfikes

mfikes 2018-12-05T22:12:22.649600Z

Cool. My part 2 is naive reapplication of part 1.

adammiller 2018-12-05T22:12:34.649800Z

so is mine

borkdude 2018-12-05T22:12:57.650100Z

@mfikes clean solution!

adammiller 2018-12-05T22:14:01.651300Z

also, used this example to play with the bit- fns

mfikes 2018-12-05T22:26:14.652400Z

@ben.grabow If you end up optimizing for ClojureScript, == is much faster than = for numerics. (It could help numeric-anti-pair? perf.)

mfikes 2018-12-05T22:28:59.653500Z

I wonder if Clojure is also faster if using == on numerics... Hrm.

ihabunek 2018-12-05T22:32:12.654100Z

i'm not seeing any speedups if i change my solution to use ==

ihabunek 2018-12-05T22:32:58.655800Z

~120ms for part 1 on my laptop

2018-12-05T22:33:05.656200Z

Thanks @mfikes. I see about 30% speed up across the board with == but who knows how reliable that is across iterations.

mfikes 2018-12-05T22:33:13.656400Z

Nice trick Ben employed, pre-purging for part 2; makes sense 🙂

mfikes 2018-12-05T22:35:03.657500Z

The reason == is so much faster in ClojureScript is that it is essentially a macro, letting things compile straight down to direct numeric comparisons in JavaScript.

fellshard 2018-12-05T22:35:54.657600Z

Oh wow, you're right, you can leapfrog from part 1's solution into part 2

2018-12-05T22:36:12.658Z

Love the use of reduce in react/`add-unit`.

adammiller 2018-12-05T22:36:14.658300Z

made pretty big difference in my cljs times....didn't affect my clj times much.

fellshard 2018-12-05T22:36:31.658400Z

Now I'm hyped to head home and tinker more, that's the biggest macro-level optimization I can think of for what I've got right now

mfikes 2018-12-05T22:37:24.659200Z

Wow, that pre-purging trick leads to a speedup of 5.6 if applied to my naive aproach. 🙂

ihabunek 2018-12-05T22:38:15.659600Z

i'll be stealing that as well 😄

adammiller 2018-12-05T22:39:47.659900Z

yes, going to modify mine for that later this evening

fellshard 2018-12-05T22:42:07.660600Z

I'm sure you could map that onto category theory somehow...

mfikes 2018-12-05T22:42:34.661500Z

Hah. Yeah, a proof that that is a legitimate optimization is right at the edge of where my mind is.

fellshard 2018-12-05T22:43:11.662200Z

collapse . filter-unit = collapse . filter-unit . collapse ...

fellshard 2018-12-05T22:44:07.663200Z

A. collapse is already fixed-point B. filter-unit is structure-preserving, to the extent that you can run it at any point in the process without changing the end result

fellshard 2018-12-05T22:44:42.663600Z

But I don't have anywhere near the terminology to express what that actually means XD

mfikes 2018-12-05T22:45:14.664300Z

The single pass idea also could really use a proof. It felt a little sketchy, but I was convinced.

fellshard 2018-12-05T22:46:11.664800Z

Is that doing a sort of combinatoric split each time you ingest a letter, one with the letter in-place, and the other with it removed?

fellshard 2018-12-05T22:46:43.665500Z

Starts to remind me of... wait. Pumping lemma? Stack-based automata?

mfikes 2018-12-05T22:48:09.667300Z

To me the single pass idea feels like adding a stream of single units to the end of a growing/shrinking polymer, doing the reaction as you add each individual unit.

fellshard 2018-12-05T22:48:47.667800Z

Yeah, it's just a stack-based automata at that point, if even that, given it'd just be one node

fellshard 2018-12-05T22:48:59.668Z

Which is what your solution looks like

mfikes 2018-12-05T22:49:22.668400Z

Yeah, Ben's does some interesting things with two ends and a reverse mixed in there 🙂

mfikes 2018-12-05T22:50:19.668700Z

Or something with a left and right. I haven't grokked it yet.

fellshard 2018-12-05T22:52:05.669100Z

Have a link to Ben's?

fellshard 2018-12-05T22:52:40.669300Z

Ah nvm, I see it in the cljc

2018-12-05T22:53:51.670100Z

@mfikes It's the same thing you're doing but more opaque! My left is your reduce's accumulator.

fellshard 2018-12-05T22:56:46.672Z

Yeah; left is the accumulating stack, right is the remaining string stack

fellshard 2018-12-05T22:57:07.672500Z

Though I don't think the reverse at the end earns you anything, since the answer doesn't call for preserving the order

mfikes 2018-12-05T22:58:32.673900Z

You could probably prove the single pass leads to a fully reacted polymer by using proof by induction

2018-12-05T22:59:27.675100Z

True! Reverses are not necessary since a polymer reacts the same forwards and backwards. Feels dirty to me to remove them though.

fellshard 2018-12-05T22:59:54.675700Z

Intuitively, the only information adding a new unit to the end can add is that A. a new element exists, or B. the top element is removed

fellshard 2018-12-05T23:00:40.676300Z

Even if a new element is re-exposed from that interaction, it's inert still, because it cannot interact with the thing below it on the stack (having already been tested in a prior recursive step)

fellshard 2018-12-05T23:00:55.676700Z

therefore, it can only react with the following unused element in the remaining input string

fellshard 2018-12-05T23:01:23.677500Z

The key is that the relative ordering of the 'seen' portion of the string remains the same, and is stable while new elements are added to the end

mfikes 2018-12-05T23:01:55.678800Z

Yeah, base case (empty polymer) is inert, and each inductive step leads to a new inert polymer.

fellshard 2018-12-05T23:01:56.678900Z

This is very similar to the type of thing you'd see for validly matching nested bracketed statements 🙂

fellshard 2018-12-05T23:02:09.679200Z

That's the best way to put it, yeah

fellshard 2018-12-05T23:02:11.679400Z

inert

fellshard 2018-12-05T23:03:12.679600Z

https://en.wikipedia.org/wiki/Pushdown_automaton Good ol' days

2018-12-05T23:15:06.680300Z

I refactored to use reduce like @mfikes and got another 20-40% speed up

mfikes 2018-12-05T23:16:33.681600Z

If we were competing with other languages on perf, Ben’s would be a good candidate :)

2018-12-05T23:22:45.682500Z

It's like the anticipation of Christmas when waiting for CI perf test results to come back 🎄