Advent of Code 2023 Day 12: Hot Springs

แชร์
ฝัง
  • เผยแพร่เมื่อ 3 ธ.ค. 2024

ความคิดเห็น • 38

  • @hyper-neutrino
    @hyper-neutrino  11 หลายเดือนก่อน +8

    This is my second take of today's video. I really didn't like my first solution as it was overcomplicated and inefficient, so I decided to do it again. You can find my unedited live solve VOD for today here: th-cam.com/video/GVcER1GavNQ/w-d-xo.html

    • @professornumbskull5555
      @professornumbskull5555 11 หลายเดือนก่อน

      I was on the old video and suddenly saw [OLD] after it, as I reloaded the site, the solution had changed, I thought I was witnessing a Mandela effect LOL

  • @CurtisAutery
    @CurtisAutery 11 หลายเดือนก่อน +2

    This was a remarkable solution. I was similarly stymied, but the damn finally broke when I saw that by tracking fewer moving parts in the recursive function, it made previous counts much more cacheable. Really well done, man.

  • @olaamigoify
    @olaamigoify 11 หลายเดือนก่อน +2

    You are a gentleman and a scholar. Thanks for explaining so clearly and concisely.

  • @lazeman7003
    @lazeman7003 11 หลายเดือนก่อน +8

    tips for part two, just do this, it does the same :)
    from functools import cache
    @cache
    def count(cfg,nums):
    ...

  • @henryschreiner
    @henryschreiner 11 หลายเดือนก่อน +5

    My Rust-based solution was very similar to yours, and it took 10.5 hours (multithreaded on eight cores, but it's really

    • @olafschluter706
      @olafschluter706 5 หลายเดือนก่อน

      The effect of memoization is very impressive with this problem.

  • @ferenisles
    @ferenisles 11 หลายเดือนก่อน

    I'm working in javascript and was completely lost with how to approach day 12. I adopted your logic and built a cache using the Map() constructor and it worked beautifully! Thanks for making this video, helped me understand the problem better!

  • @drgold1999
    @drgold1999 11 หลายเดือนก่อน

    the best solution thats out there by far, i was super stuck with this one and looking through a lot of solutions but after i saw yours i just instantly knew this is the way to do it

  • @JamesBaker41
    @JamesBaker41 8 หลายเดือนก่อน

    Really appreciate the explanation. I understand recursion, but often have difficulty thinking of the base cases. I really struggle to break down the problem in the way you did. My understanding of dynamic programming is that it's essentially just recursion with memoization. Is there more to it than that? Found it interesting that you said you avoid DP, but I felt like your solution was basically DP. Thanks again for the upload and congrats on your high finish in 2023!

  • @m1geo
    @m1geo 11 หลายเดือนก่อน +2

    That "cache" trick though! Wow! That made a huge difference to my time. Very nice hack! I wouldn't have ever thought of that! Not in that simple a way!

  • @cherubim7
    @cherubim7 11 หลายเดือนก่อน +6

    The best explanation I have seen so far. The code was also quite understandable. Great work. I just added a simple optimization on top of your approach. Instead of mutating cfg, nums and using them as key in the cache, you can maintain indices i and j to see how far we have come in parsing cfg and nums respectively. And now, we can use (i, j) as the cache for the key.

  • @shrugalic
    @shrugalic 11 หลายเดือนก่อน

    Wow, what an elegant solution, thanks for sharing! I also like your visual style, as in colors and typography.
    I finally solved part 2 of this yesterday (the last one missing to finish AOC 23), but bit less elegantly than this, by not processing a whole damaged cluster at once, but char by char instead, while passing along a requirement for the next char, such as Any, NotDamaged or DamageOfLength(remaining size).
    And for a while I forgot to memo the parts that didn't have an arrangement and only remembered the good ones. Worked fine for part 2 examples, but not the real input.

  • @LinhPham-bx9fd
    @LinhPham-bx9fd 11 หลายเดือนก่อน

    I often have a very hard time trying to understand recursions just in general but you made it very clear in your explanations and solution. Thanks a lot

  • @olafschluter706
    @olafschluter706 5 หลายเดือนก่อน

    Brute force part2 w/o memoization runs forever even in Rust. I was away getting stuff from the super market and returned, and the computation had not finished then even running in parallel on all my cores (Mac mini M1). I tried to reason that only some conjugation patterns matter to reduce the problem, but could not get the math on my side The memoization cut it. Brilliant.

  • @tomatsaladonion
    @tomatsaladonion 11 หลายเดือนก่อน +1

    I just spent one hour understanding the first video haha thank you though for sharing your resolutions, I am learning a lot from it

    • @hyper-neutrino
      @hyper-neutrino  11 หลายเดือนก่อน +5

      Haha, yeah I wasn't thinking clearly today and didn't realize at first that my idea was totally overcomplicated xD glad to hear that my videos are helpful though! 🙌

  • @TheStainlessSteelCat42
    @TheStainlessSteelCat42 11 หลายเดือนก่อน

    Brilliant solution, clearly explained. Does anyone know which font is being used for the editor here?

    • @hyper-neutrino
      @hyper-neutrino  10 หลายเดือนก่อน +1

      It's Monaspace Radon

  • @Max-rz8br
    @Max-rz8br หลายเดือนก่อน

    What font are you using? I looks really nice.

  • @kenhaley4
    @kenhaley4 8 หลายเดือนก่อน

    Nicely explained! 2 questions:
    (1) why not just use the @cache decorator (from functools) to memoize the count function?
    (2) I was surprised by the not-equal and less-than-or-equal symbols that you used. What's inserting those into your code and does python recognize them as is? Or is it something just the IDE is doing?

    • @JamesBaker41
      @JamesBaker41 8 หลายเดือนก่อน

      Regarding #1, he very likely just didn't know about it. Regarding #2, that's a VSCode thing.

  • @olatrials
    @olatrials 11 หลายเดือนก่อน +2

    For part one, I (for each line of input) created every integer from 0 to 2^(number of "?"s in input), and converted the binary representation of each into "."s and "#"s instead of 0s and 1s. For each of those, I checked if replacing all the "?"s with the binary thing gave a valid solution. How did I check? With a regex generated like this:
    "^\.*" + ("#" repeated N times for every N in the counts list) joined by "\.+" + "\.*$"
    I am ashamed to commit it to my git history.

    • @code-kiwi
      @code-kiwi 11 หลายเดือนก่อน +1

      I did exactly the same.
      I knew that brute forcing like this was clearly not the best solution, especially because part 2 would make the brute force useless. I spent a lot of time trying to make my brute force more efficient, using dynamic programming, but I failed.
      To me, the challenges are an opportunity to learn a lot, should we be ashamed to have things to learn? :)

    • @piroliroblack1219
      @piroliroblack1219 11 หลายเดือนก่อน

      regex takes a lot of time, if you just split the string by '.', filter out empty spaces and then check to see if each group lengths corresponds to the grouping lengths. From 110sec with regex to 2sec without it.

    • @olatrials
      @olatrials 11 หลายเดือนก่อน +1

      @@code-kiwi I absolutely agree, it was a bit tounge in cheek. It's amazing how much I learn from this stuff!

    • @olatrials
      @olatrials 11 หลายเดือนก่อน

      @@piroliroblack1219 Hmm, I guess that depends on efficiency of the language used, and on the regex interpreter. The only extra thing the regex does is verify that the groups only have "#"s in them, and depending on the implementation, it could be run in highly efficient machine code, whereas the other solution could be wastefully interpreted. Anyway, I tried to change it in my solution and got a ~30% speedup, which isn't bad! But I do enjoy a good regex.

  • @neuro476
    @neuro476 11 หลายเดือนก่อน

    Great tutorial! I've tried porting your solution to C# and was wondering how does Python handle cases like "?????????##????? 1,9,1"? My program runs for a couple of minutes and uses up all the available RAM (20 GB+) before completely hanging Windows :)

  • @SharunKumar
    @SharunKumar 11 หลายเดือนก่อน

    3:51 Windows :D

  • @aspzx
    @aspzx 11 หลายเดือนก่อน

    7:39 - isn't there an error here? If nums[0] is 5 and len(cfg) is also 5 when we enter the if block, then cfg[5 + 1:] will produce an invalid range into the cfg string.
    Edit: ok apparently Python does not behave the same as other languages when it comes to array bounds checking. Yes the start of the string index is out of bounds but it doesn't matter in python as the result is always an empty string.

    • @hyper-neutrino
      @hyper-neutrino  11 หลายเดือนก่อน +1

      Yeah, slices work differently. If you try to access a single out-of-bounds index, you get an error, but if your slice lies out of range, it will just return an empty result. I find it very convenient TBH.

  • @ImAdonai
    @ImAdonai 11 หลายเดือนก่อน

    I think I'm going insane. Doesn't this solution allow for later lengths to be counted before prior ones had chance to fit in the segment? For example, wouldn't that count #.#.####.# as a valid solution for ???????????? 1, 4, 1, 1, 1? The task description says: "After the list of springs for a given row, the size of each contiguous group of damaged springs is listed in the order those groups appear in the row.", but it seems that this order isn't respected at all. I had somewhat similar solution that worked, though it was iterative instead of recursive, and my answer was accepted despite this apparent flaw. Is it just the input that never made this being relevant, or am I actually just going mad?

    • @hyper-neutrino
      @hyper-neutrino  11 หลายเดือนก่อน

      I don't think this flaw exists, at least not in my solution. When I see a # (or attempt to treat a ? as one), I check nums[0] so the block size must match the next existing block; you can't skip past # and you can't skip past a block size in the nums array.

  • @Stasakusok
    @Stasakusok 11 หลายเดือนก่อน

    what

    • @Stasakusok
      @Stasakusok 11 หลายเดือนก่อน +1

      ok, i got it