From 93c3322770a962ecbd22f7dbe03740ffcb2ad790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Ytterstr=C3=B6m?= Date: Tue, 14 Dec 2021 07:44:16 +0100 Subject: [PATCH] Solve 2021:14 "Extended Polymerization" part 2 Solved by total rewrite, since part 1 solution was too slow and was killed by the OS. I tried to use deque and prepending instead of appending, but no luck. I looked for inspiration on AOC subreddit, and realized I could solve the puzzle without constructing a new polymer string. Instead, **it should be solved by counting pairs**. First of all, a little zip trick is used to construct an initial list of pairs from the initial polymer. collections.defaultdicts are excellent tools for storing the counts, since they will default to a value of 0 for any key that is added. the initial count of the initial polymer is this: { ('N', 'N'): 1, ('N', 'C'): 1, ('C', 'B'): 1 } And the initial character count looks like this, and was constructed by collections.Counter. This is what we increase and use after the 40 steps. { 'N': 2, 'C': 1, 'B': 1 } So, lets see what happens after the first step. First of all, all existing pair counts should be reset each step, since this will be faster. nxt is used to store the next step pairs. the current pairs are iterated. - for NN, the new pairs NC and CN are added for the next step. Since NN exists 1 time, the pairs NC and CN also will exist exactly 1 more time each. The char C will also exist 1 more time, so add it to the char counter. - for NC, the new pairs NB and BC are added for the next step. Pair counter is once again synced with NC, and B is added to the char counter. - and the same for CB, where CH and HB is added. The count looks like this after step 1. { ('N', 'C'): 1, ('C', 'N'): 1, ('N', 'B'): 1, ('B', 'C'): 1, ('C', 'H'): 1, ('H', 'B'): 1, } The pair counts are all 1 for step 2, since **no pairs have reoccured yet**. The char counts looks like this, and matches polymer 'NCNBCHB', even though it does not matter in which order the chars are written: { 'N': 2, 'C': 2, 'B': 2, 'H': 1 } Lets see the counts for step 2. { ('N', 'B'): 2, ('B', 'C'): 2, ('C', 'C'): 1, ('C', 'N'): 1, ('B', 'B'): 2, ('C', 'B'): 2, ('B', 'H'): 1, ('H', 'C'): 1 } Polymer NBCCNBBBCBHCB are present in the char count: { 'N': 2, 'C': 4, 'B': 6, 'H': 1 } Here some pairs have started to reoccour, and the char counts have increased. So it will continue up to 40 steps, smooth and fast. It works, since the char count are increased to match the polymer, and it is fast, since the program does not care about the order of the chars. --- 2021-python/solutions/day_14.py | 35 ++++++++++++++------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/2021-python/solutions/day_14.py b/2021-python/solutions/day_14.py index 6422d99..faee97b 100644 --- a/2021-python/solutions/day_14.py +++ b/2021-python/solutions/day_14.py @@ -1,5 +1,5 @@ from solutions import BaseSolution -from collections import Counter +from collections import Counter, defaultdict class Solution(BaseSolution): @@ -21,28 +21,23 @@ class Solution(BaseSolution): def _solve(self, puzzle_input, steps=10): polymer, pir = puzzle_input pir = dict(pir) + pairs = defaultdict(int) + chars = defaultdict(int, Counter(polymer)) + for l, r in zip(polymer[:-1], polymer[1:]): + pairs[(l, r)] += 1 for _ in range(steps): - p = [] - for i in range(len(polymer) - 1): - a, b = polymer[i : i + 2] - p.append(a) - s = pir.get(a + b) - if s: - p.append(s) - p.append(polymer[-1]) - polymer = p - c = [v for k, v in Counter(polymer).most_common()] - return max(c) - min(c) + nxt = defaultdict(int) + for lr, count in pairs.items(): + l, r = lr + m = pir["".join(lr)] + nxt[(l, m)] += count + nxt[(m, r)] += count + chars[m] += count + pairs = nxt + cv = chars.values() + return max(cv) - min(cv) if __name__ == "__main__": solution = Solution() solution.show_results() - -""" -Template: NNCB -After step 1: NCNBCHB -After step 2: NBCCNBBBCBHCB -After step 3: NBBBCNCCNBBNBNBBCHBHHBCHB -After step 4: NBBNBNBBCCNBCNCCNBBNBBNBBBNBBNBBCBHCBHHNHCBBCBHCB -"""