Advent 2017 through Day 15
This commit is contained in:
1 parent
975b0bb1b0
commit
43dc88d8e0
1 file changed
+460
-79
+460
-79
@@ -45,6 +45,7 @@
|
||||
"\n",
|
||||
"from collections import Counter, defaultdict, namedtuple, deque, abc, OrderedDict\n",
|
||||
"from functools import lru_cache\n",
|
||||
"from statistics import mean, median, mode, stdev, variance\n",
|
||||
"from itertools import (permutations, combinations, chain, cycle, product, islice, \n",
|
||||
" takewhile, zip_longest, count as count_from)\n",
|
||||
"from heapq import heappop, heappush\n",
|
||||
@@ -75,6 +76,10 @@
|
||||
" \"Parse a str into a tuple of atoms (numbers or str tokens).\"\n",
|
||||
" return mapt(atom, line.replace(',', ' ').split())\n",
|
||||
"\n",
|
||||
"def integers(text): \n",
|
||||
" \"Return a tuple of all integers in a string.\"\n",
|
||||
" return mapt(int, re.findall(r'\\b[-+]?\\d+\\b', text))\n",
|
||||
"\n",
|
||||
"def atom(token):\n",
|
||||
" \"Parse a str token into a number, or leave it as a str.\"\n",
|
||||
" try:\n",
|
||||
@@ -175,11 +180,6 @@
|
||||
"def mapt(fn, *args): \n",
|
||||
" \"Do a map, and make the results into a tuple.\"\n",
|
||||
" return tuple(map(fn, *args))\n",
|
||||
"\n",
|
||||
"def most_common(seq):\n",
|
||||
" \"The item that appears most frequently in sequence.\"\n",
|
||||
" [(item, count)] = Counter(seq).most_common(1)\n",
|
||||
" return item\n",
|
||||
" \n",
|
||||
"################ Math Functions\n",
|
||||
" \n",
|
||||
@@ -457,7 +457,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"rows = array('''790\t99\t345\t1080\t32\t143\t1085\t984\t553\t98\t123\t97\t197\t886\t125\t947\n",
|
||||
"rows2 = array('''790\t99\t345\t1080\t32\t143\t1085\t984\t553\t98\t123\t97\t197\t886\t125\t947\n",
|
||||
"302\t463\t59\t58\t55\t87\t508\t54\t472\t63\t469\t419\t424\t331\t337\t72\n",
|
||||
"899\t962\t77\t1127\t62\t530\t78\t880\t129\t1014\t93\t148\t239\t288\t357\t424\n",
|
||||
"2417\t2755\t254\t3886\t5336\t3655\t5798\t3273\t5016\t178\t270\t6511\t223\t5391\t1342\t2377\n",
|
||||
@@ -494,7 +494,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"sum(abs(max(row) - min(row)) for row in rows)"
|
||||
"sum(abs(max(row) - min(row)) for row in rows2)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -526,7 +526,7 @@
|
||||
"def evendiv(row): \n",
|
||||
" return first(a // b for a in row for b in row if a > b and a // b == a / b)\n",
|
||||
"\n",
|
||||
"sum(map(evendiv, rows))"
|
||||
"sum(map(evendiv, rows2))"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -555,7 +555,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"N = 277678"
|
||||
"M = 277678"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -640,7 +640,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"nth(spiral(), N - 1)"
|
||||
"nth(spiral(), M - 1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -736,7 +736,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"first(x for x in spiralsums() if x > N)"
|
||||
"first(x for x in spiralsums() if x > M)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1193,7 +1193,7 @@
|
||||
" for line in lines:\n",
|
||||
" name, w, *rest = re.findall(r'\\w+', line)\n",
|
||||
" weight[name] = int(w)\n",
|
||||
" above[name] = rest\n",
|
||||
" above[name] = set(rest)\n",
|
||||
" return weight, above\n",
|
||||
"\n",
|
||||
"weight, above = towers(Input(7))\n",
|
||||
@@ -1265,14 +1265,16 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def tower_weight(p): return weight[p] + sum(map(tower_weight, above[p]))\n",
|
||||
"def tower_weight(p): \n",
|
||||
" \"Total weight for the tower whose root (bottom) is p.\"\n",
|
||||
" return weight[p] + sum(map(tower_weight, above[p]))\n",
|
||||
"\n",
|
||||
"def siblings(p): \n",
|
||||
" \"The other programs at the same level as this one.\"\n",
|
||||
" if p not in below:\n",
|
||||
" return [] # the root has no siblings\n",
|
||||
" return Ø # the root has no siblings\n",
|
||||
" else:\n",
|
||||
" return [s for s in above[below[p]] if s != p]\n",
|
||||
" return above[below[p]] - {p}\n",
|
||||
"\n",
|
||||
"below = {a: b for b in programs for a in above[b]}"
|
||||
]
|
||||
@@ -1337,7 +1339,7 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now what should we correct it to? To the weight that makes it the same weight as sibling towers:"
|
||||
"Now what should we correct it to? To the weight that makes it the same weight as the sibling towers:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1395,7 +1397,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"program = array(Input(8))\n",
|
||||
"program8 = array(Input(8))\n",
|
||||
"\n",
|
||||
"def run8(program):\n",
|
||||
" \"Run the program and return final value of registers.\"\n",
|
||||
@@ -1405,7 +1407,7 @@
|
||||
" registers[r] += delta * (+1 if inc == 'inc' else -1)\n",
|
||||
" return registers\n",
|
||||
"\n",
|
||||
"max(run8(program).values())"
|
||||
"max(run8(program8).values())"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1445,7 +1447,7 @@
|
||||
" highest = max(highest, registers[r])\n",
|
||||
" return highest\n",
|
||||
"\n",
|
||||
"run82(program)"
|
||||
"run82(program8)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1486,12 +1488,12 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now I can deal with the nested braces (which can't be handled with regular expressions):"
|
||||
"Now I can deal with the nested braces (which can't be handled with regular expressions). The puzzle says \"*Each group is assigned a score which is one more than the score of the group that immediately contains it,*\" which is the same as saying that a group's score is its nesting level, a quantity that increases with each open-brace character, and decreases with each close-brace:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 54,
|
||||
"execution_count": 38,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
@@ -1502,7 +1504,7 @@
|
||||
"9662"
|
||||
]
|
||||
},
|
||||
"execution_count": 54,
|
||||
"execution_count": 38,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -1514,8 +1516,8 @@
|
||||
" level = 0 # Level of nesting\n",
|
||||
" for c in text:\n",
|
||||
" if c == '{':\n",
|
||||
" total += level + 1\n",
|
||||
" level += 1\n",
|
||||
" total += level\n",
|
||||
" elif c == '}':\n",
|
||||
" level -= 1\n",
|
||||
" return total\n",
|
||||
@@ -1635,21 +1637,10 @@
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[2, 1, 0, 3, 4]"
|
||||
]
|
||||
},
|
||||
"execution_count": 42,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Reverse [0, 1, 2]:\n",
|
||||
"rev(list(range(5)), 0, 3)"
|
||||
"assert rev(list(range(5)), 0, 3) == [2, 1, 0, 3, 4]"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1658,21 +1649,10 @@
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[0, 4, 2, 3, 1]"
|
||||
]
|
||||
},
|
||||
"execution_count": 43,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Test that rev works when we wrap around: reverse [4, 0, 1]\n",
|
||||
"rev(list(range(5)), 4, 3) "
|
||||
"# Reverse [4, 0, 1], wrapping around:\n",
|
||||
"assert rev(list(range(5)), 4, 3) == [0, 4, 2, 3, 1]"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1691,21 +1671,11 @@
|
||||
"[4, 3, 0, 1, 2]\n",
|
||||
"[3, 4, 2, 1, 0]\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"12"
|
||||
]
|
||||
},
|
||||
"execution_count": 44,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Duplicate the example output\n",
|
||||
"knothash((3, 4, 1, 5), N=5, verbose=True)"
|
||||
"assert knothash((3, 4, 1, 5), N=5, verbose=True) == 12"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1797,6 +1767,7 @@
|
||||
" return result\n",
|
||||
" \n",
|
||||
"assert XOR([65, 27, 9, 1, 4, 3, 40, 50, 91, 7, 6, 0, 2, 5, 68, 22]) == 64\n",
|
||||
"assert hexstr([255, 0, 17]) == 'ff0011'\n",
|
||||
"\n",
|
||||
"assert knothash2('') == 'a2582a3a0e66e6e86e3812dcb672a272'\n",
|
||||
"\n",
|
||||
@@ -1816,7 +1787,7 @@
|
||||
"source": [
|
||||
"# [Day 11](https://adventofcode.com/2017/day/11): Hex Ed\n",
|
||||
"\n",
|
||||
"The first thing I did was search `[hex coordinates]`, and the #1 result (as I expected) was Amit Patel's \"[Hexagonal Grids](https://www.redblobgames.com/grids/hexagons/)\" page. I chose his \"odd-q vertical layout\" to define the six directions as (dx, dy) deltas:"
|
||||
"The first thing I did was search [`[hex coordinates]`](https://www.google.com/search?source=hp&ei=Ft4xWoOqKcy4jAOs76a4CQ&q=hex+coordinates), and the #1 result (as I expected) was Amit Patel's \"[Hexagonal Grids](https://www.redblobgames.com/grids/hexagons/)\" page. I chose his \"odd-q vertical layout\" to define the six directions as (dx, dy) deltas:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1856,7 +1827,7 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"path = Input(11).read().strip().split(',')\n",
|
||||
"path = vector(Input(11).read())\n",
|
||||
"\n",
|
||||
"def follow(path):\n",
|
||||
" \"Follow each step of the path; return final distance to origin.\"\n",
|
||||
@@ -1926,25 +1897,14 @@
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{0, 659, 737}"
|
||||
]
|
||||
},
|
||||
"execution_count": 50,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def groups(lines):\n",
|
||||
" \"Dict of {i: {directly_connected_to_i}\"\n",
|
||||
" return {lhs: {lhs} | set(rhs)\n",
|
||||
" for (lhs, _, *rhs) in array(lines)}\n",
|
||||
" \n",
|
||||
"groups(Input(12))[0]"
|
||||
"assert groups(Input(12))[0] == {0, 659, 737}"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2004,7 +1964,95 @@
|
||||
"\n",
|
||||
"**Part Two**\n",
|
||||
"\n",
|
||||
"I did almost all the work; I just need to count the number of distinct groups. That's a set of sets, so I have to use my `Set` class (because regular `set`s are not hashable):"
|
||||
"I did almost all the work; I just need to count the number of distinct groups. That's a set of sets, and regular `set`s are not hashable, so I use my `Set` class:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 53,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"221"
|
||||
]
|
||||
},
|
||||
"execution_count": 53,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"len({Set(G[i]) for i in G})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# [Day 13](https://adventofcode.com/2017/day/13): Packet Scanners\n",
|
||||
"\n",
|
||||
"First thing: The puzzle says the data is *depth: range*, but `range` has a meaning in Python, so I'll use the term *width* instead.\n",
|
||||
"\n",
|
||||
"Second thing: I misread the puzzle description and mistakenly thought the scanners were going in a circular route,\n",
|
||||
"so that they'd be at the top at any time that is 0 mod *width*. That gave the wrong answer and I realized the scanners are actually going back-and-forth, so with a width of size *n*, it takes *n* - 1 steps to get to the bottom, and *n* - 1 steps to get back to the top, so the scanner will be \n",
|
||||
"at the top at times that are multiples of 2(*n* - 1). For example, with width 3, that would be times 0, 4, 8, ... "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 54,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def trip_severity(scanners): \n",
|
||||
" \"The sum of sevrities for each time the packet is caught.\"\n",
|
||||
" return sum((d * w if caught(d, w) else 0) \n",
|
||||
" for (d, w) in scanners)\n",
|
||||
"\n",
|
||||
"def caught(depth, width):\n",
|
||||
" \"Does the scanner at this depth/width catch the packet?\"\n",
|
||||
" return depth % (2 * (width - 1)) == 0\n",
|
||||
"\n",
|
||||
"example = ((0, 3), (1, 2), (4, 4), (6, 4))\n",
|
||||
"assert trip_severity(example) == 24"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 55,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1504"
|
||||
]
|
||||
},
|
||||
"execution_count": 55,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"scanners = mapt(integers, Input(13))\n",
|
||||
"trip_severity(scanners)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Part Two**\n",
|
||||
"\n",
|
||||
"A packet is safe if no scanner catches it. We now have the possibility of a delay, so I update `caught` to allow for an optional delay, and define `safe_delay`: "
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2017,7 +2065,7 @@
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"221"
|
||||
"10"
|
||||
]
|
||||
},
|
||||
"execution_count": 56,
|
||||
@@ -2026,7 +2074,340 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"len({Set(G[i]) for i in G})"
|
||||
"def caught(depth, width, delay=0):\n",
|
||||
" \"Does the scanner at this depth/width catch the packet with this delay?\"\n",
|
||||
" return (depth + delay) % (2 * (width - 1)) == 0 \n",
|
||||
"\n",
|
||||
"def safe_delay(scanners):\n",
|
||||
" \"Find the first delay such that no scanner catches the packet.\"\n",
|
||||
" safe = lambda delay: not any(caught(d, w, delay) for (d, w) in scanners)\n",
|
||||
" return first(filter(safe, count_from(0)))\n",
|
||||
"\n",
|
||||
"safe_delay(example)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 57,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"3823370"
|
||||
]
|
||||
},
|
||||
"execution_count": 57,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"safe_delay(scanners)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# [Day 14](https://adventofcode.com/2017/day/14): Disk Defragmentation\n",
|
||||
"\n",
|
||||
"I found this puzzle description confusing: are they talking about what I call `knothash`, or is it `knothash2`? I decided for the latter, which turned out to be right:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 58,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"key = 'ljoxqyyw'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 72,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"8316"
|
||||
]
|
||||
},
|
||||
"execution_count": 72,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def bits(key, i):\n",
|
||||
" \"The bits in the hash of this key with this row number.\"\n",
|
||||
" hash = knothash2(key + '-' + str(i))\n",
|
||||
" return format(int(hash, base=16), '0128b')\n",
|
||||
"\n",
|
||||
"sum(bits(key, i).count('1') for i in range(128))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Part Two**\n",
|
||||
"\n",
|
||||
"So as not to worry about running off the edge of the grid, I'll surround the grid with `'0'` bits:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 60,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def Grid(key, N=128+2):\n",
|
||||
" \"Make a grid, with a border around it.\"\n",
|
||||
" rows = [['0'] + list(bits(key, i)) + ['0'] for i in range(128)]\n",
|
||||
" empty = ['0'] * len(rows[0])\n",
|
||||
" return [empty] + rows + [empty]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"To find a region, start at some `(x, y)` position and [flood fill](https://en.wikipedia.org/wiki/Flood_fill) to neighbors that have the same value (a `'1'` bit)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 61,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def flood(grid, x, y, val, R):\n",
|
||||
" \"For all cells with value val connected to grid[x][y], give them region number R.\"\n",
|
||||
" if grid[y][x] == val:\n",
|
||||
" grid[y][x] = R\n",
|
||||
" for x2, y2 in neighbors4((x, y)):\n",
|
||||
" flood(grid, x2, y2, val, R)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 62,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def flood_all(grid, val='1'):\n",
|
||||
" \"Label all regions with consecutive ints starting at 1.\"\n",
|
||||
" R = 0 # R is the region number\n",
|
||||
" for y in range(1, len(grid) - 1):\n",
|
||||
" for x in range(1, len(grid) - 1):\n",
|
||||
" if grid[y][x] == val:\n",
|
||||
" R += 1\n",
|
||||
" flood(grid, x, y, val, R)\n",
|
||||
" return R "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 73,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1074"
|
||||
]
|
||||
},
|
||||
"execution_count": 73,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"flood_all(Grid(key))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# [Day 15](https://adventofcode.com/2017/day/15): Dueling Generators\n",
|
||||
"\n",
|
||||
"There are lots of arbitrary integers below: my personalized inputs are `516` and `190`; the other numbers are shared by all puzzle-solvers. I decided to make infinite generators of numbers, using `gen`:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 64,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"597"
|
||||
]
|
||||
},
|
||||
"execution_count": 64,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def gen(prev, factor, m=2147483647):\n",
|
||||
" \"Generate an infinite sequence of numbers according to the rules.\"\n",
|
||||
" while True:\n",
|
||||
" prev = (prev * factor) % m\n",
|
||||
" yield prev\n",
|
||||
" \n",
|
||||
"def judge(A, B, N=40*10**6, b=16): \n",
|
||||
" \"How many of the first N pairs from A and B agree in the last b bits?\"\n",
|
||||
" m = 2 ** b\n",
|
||||
" return quantify(next(A) % m == next(B) % m\n",
|
||||
" for _ in range(N))\n",
|
||||
" \n",
|
||||
"A = lambda: gen(516, 16807)\n",
|
||||
"B = lambda: gen(190, 48271)\n",
|
||||
"\n",
|
||||
"judge(A(), B())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Part Two**\n",
|
||||
"\n",
|
||||
"A small change: only consider numbers that match the **criteria** of being divisible by 4 or 8, respectively;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 65,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"303"
|
||||
]
|
||||
},
|
||||
"execution_count": 65,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def criteria(m, iterable): \n",
|
||||
" \"Elements of iterable that are divisible by m\"\n",
|
||||
" return (n for n in iterable if n % m == 0)\n",
|
||||
" \n",
|
||||
"judge(criteria(4, A()), criteria(8, B()), 5*10**6)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Wrapping Up: Verification and Timing\n",
|
||||
"\n",
|
||||
"Here is a little test harness to verify that I still get the right answers (even if I refactor some of the code):"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 68,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def day(n, compute1, answer1, compute2, answer2):\n",
|
||||
" \"Assert that we get the right answers for this day.\"\n",
|
||||
" assert compute1 == answer1\n",
|
||||
" assert compute2 == answer2\n",
|
||||
"\n",
|
||||
"day(1, sum(digits[i] for i in range(N) if digits[i] == digits[i - 1]), 1158,\n",
|
||||
" sum(digits[i] for i in range(N) if digits[i] == digits[i - N // 2]), 1132)\n",
|
||||
"day(2, sum(abs(max(row) - min(row)) for row in rows2), 46402, \n",
|
||||
" sum(map(evendiv, rows2)), 265)\n",
|
||||
"day(3, cityblock_distance(nth(spiral(), M - 1)), 475, \n",
|
||||
" first(x for x in spiralsums() if x > M), 279138)\n",
|
||||
"day(4, quantify(Input(4), is_valid), 337, quantify(Input(4), is_valid2), 231)\n",
|
||||
"day(5, run(program), 364539, run2(program), 27477714)\n",
|
||||
"day(6, realloc(banks), 12841, realloc2(banks), 8038)\n",
|
||||
"day(7, first(programs - set(flatten(above.values()))), 'wiapj', \n",
|
||||
" correct(wrongest(programs)), 1072)\n",
|
||||
"day(8, max(run8(program8).values()), 6828, run82(program8), 7234)\n",
|
||||
"day(9, total_score(text2), 9662, len(text1) - len(text3), 4903)\n",
|
||||
"day(10, knothash(stream), 4480, \n",
|
||||
" knothash2(stream2), 'c500ffe015c83b60fad2e4b7d59dabc4')\n",
|
||||
"day(11, follow(path), 705, follow2(path), 1469)\n",
|
||||
"day(12, len(G[0]), 115, len({Set(G[i]) for i in G}), 221)\n",
|
||||
"day(13, trip_severity(scanners), 1504, safe_delay(scanners), 3823370)\n",
|
||||
"day(14, sum(bits(key, i).count('1') for i in range(128)), 8316, \n",
|
||||
" flood_all(Grid(key)), 1074)\n",
|
||||
"day(15, judge(A(), B()), 597, \n",
|
||||
" judge(criteria(4, A()), criteria(8, B()), 5*10**6), 303)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"And here is a plot of the time taken to completely solve both parts of each puzzle each day, for me, the first person to finish, and the hundredth person. On days when I started late, I estimate my time and mark it with parens below:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 69,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"image/png": "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 truncated
|
||||
"text/plain": [
|
||||
"<matplotlib.figure.Figure at 0x1177ff518>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def plot_times(times):\n",
|
||||
" X = ints(1, len(times[0][2:]))\n",
|
||||
" for (label, mark, *Y) in times:\n",
|
||||
" plt.plot(X, Y, mark, label=label)\n",
|
||||
" plt.xlabel('Day Number'); plt.ylabel('Minutes')\n",
|
||||
" plt.legend()\n",
|
||||
"\n",
|
||||
"x = None\n",
|
||||
"plot_times([\n",
|
||||
" ('Me', 'd:', (4), 6,(20), 5, 12, 30, 33,(10), 21, 40, 13, 12,(30),(40), 13),\n",
|
||||
" ('100th', 'v:', 6, 6, 23, 4, 5, 9, 25, 8, 12, 25, 12, 9, 22, 25, 10),\n",
|
||||
" ('1st', '^:', 1, 1, 4, 1, 2, 3, 10, 3, 4, 6, 3, 2, 6, 5, 2)])"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
Reference in new issue
Block a user