Add files via upload

This commit is contained in:
Peter Norvig
2021-12-07 14:44:35 -08:00
committed by GitHub
parent 3471b327f5
commit 8fba543ca8

View File

@@ -8,7 +8,7 @@
"\n", "\n",
"# Advent of Code 2021\n", "# Advent of Code 2021\n",
"\n", "\n",
"I'm going to do [Advent of Code 2021](https://adventofcode.com/) (AoC), but I won't be competing for points. \n", "I'm doing [Advent of Code 2021](https://adventofcode.com/) (AoC) this year, but I won't be competing for points. \n",
"\n", "\n",
"I won't explain each puzzle here; you'll have to click on each day's link (e.g. [Day 1](https://adventofcode.com/2021/day/1)) to understand the puzzle. \n", "I won't explain each puzzle here; you'll have to click on each day's link (e.g. [Day 1](https://adventofcode.com/2021/day/1)) to understand the puzzle. \n",
"\n", "\n",
@@ -44,25 +44,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Now two functions that I will use each day, `parse` and `answer`. I will start by writing something like this for part 1 of Day 1:\n", "Now two functions that I will use each day, `parse` and `answer`, and two parser functions, `ints` and `atoms`:"
"\n",
" in1 = parse(1, int)\n",
" def solve_it(numbers) -> int: return ...\n",
" solve(in1)\n",
" \n",
"That is, `parse(1, int)` will parse the data file for day 1 in the format of one `int` per line and return those ints as a tuple. Then a new function I define for the day (here, `solve_it`) will (hopefully) compute the correct answer. I'll then submit the answer to AoC and verify it is correct. If it is, I'll move on to solve part 2. When I get them both done, I'll use the function `answers` to assert that the correct answers are computed. So it will look like this:\n",
"\n",
" in1 = parse(1, int)\n",
" \n",
" def solve_it(numbers) -> int: return ...\n",
" answer(1.1, solve_it(in1), 123)\n",
" \n",
" def solve_it2(numbers) -> int: return ...\n",
" answer(1.2, solve_it2(in1), 123456)\n",
" \n",
"For more complex puzzles, I will include some `assert` statements to show that I am getting the right partial results on the small example in the puzzle description.\n",
"\n",
"Here's `parse` and `answer`:"
] ]
}, },
{ {
@@ -79,30 +61,17 @@
"def answer(puzzle_number, got, expected) -> bool:\n", "def answer(puzzle_number, got, expected) -> bool:\n",
" \"\"\"Verify the answer we got was expected.\"\"\"\n", " \"\"\"Verify the answer we got was expected.\"\"\"\n",
" assert got == expected, f'For {puzzle_number}, expected {expected} but got {got}.'\n", " assert got == expected, f'For {puzzle_number}, expected {expected} but got {got}.'\n",
" return True" " return True\n",
] "\n",
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Two useful parsers are `ints` and `atoms`:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def ints(text: str) -> Tuple[int]:\n", "def ints(text: str) -> Tuple[int]:\n",
" \"\"\"Return a tuple of all the integers in text, ignoring non-number characters.\"\"\"\n", " \"\"\"A tuple of all the integers in text, ignoring non-number characters.\"\"\"\n",
" return mapt(int, re.findall('-?[0-9]+', text))\n", " return mapt(int, re.findall('-?[0-9]+', text))\n",
"\n", "\n",
"Atom = Union[float, int, str]\n", "Atom = Union[float, int, str]\n",
"\n", "\n",
"def atoms(text: str, sep=None) -> Tuple[Atom]:\n", "def atoms(text: str, sep=None) -> Tuple[Atom]:\n",
" \"\"\"Parse text into atoms (numbers or strs).\"\"\"\n", " \"\"\"A tuple of all the atoms (numbers or strs) in text.\n",
" By default, atoms are space-separated but you can change that with `sep`.\"\"\"\n",
" return tuple(map(atom, text.split(sep)))\n", " return tuple(map(atom, text.split(sep)))\n",
"\n", "\n",
"def atom(text: str) -> Atom:\n", "def atom(text: str) -> Atom:\n",
@@ -127,7 +96,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 4, "execution_count": 3,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -168,13 +137,38 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 5, "execution_count": 4,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"in1 = parse(1, int)" "in1 = parse(1, int)"
] ]
}, },
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def increases(nums: List[int]) -> int:\n",
" \"\"\"How many numbers are bigger than the previous one?\"\"\"\n",
" return quantify(nums[i] > nums[i - 1] \n",
" for i in range(1, len(nums)))\n",
"\n",
"answer(1.1, increases(in1), 1400)"
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 6, "execution_count": 6,
@@ -191,31 +185,6 @@
"output_type": "execute_result" "output_type": "execute_result"
} }
], ],
"source": [
"def increases(nums: List[int]) -> int:\n",
" \"\"\"How many numbers are bigger than the previous one?\"\"\"\n",
" return quantify(nums[i] > nums[i - 1] \n",
" for i in range(1, len(nums)))\n",
"\n",
"answer(1.1, increases(in1), 1400)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [ "source": [
"def window_increases(nums: List[int], w=3) -> int:\n", "def window_increases(nums: List[int], w=3) -> int:\n",
" \"\"\"How many sliding windows of w numbers have a sum bigger than the previous window?\"\"\"\n", " \"\"\"How many sliding windows of w numbers have a sum bigger than the previous window?\"\"\"\n",
@@ -225,6 +194,25 @@
"answer(1.2, window_increases(in1), 1429)" "answer(1.2, window_increases(in1), 1429)"
] ]
}, },
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Note**: each day will follow the same pattern of code as written above:\n",
"\n",
"1. I'll first parse the input file, in this case with `in1 = parse(1, int)`. The \"`1`\" means the file for day 1; `int` means each line of the input file should be parsed as an `int`; the collection of integers will be returned as a tuple. The `sep` keyword on `parse` can be used for inputs in which it is not the case that each line is a separate record.\n",
"\n",
"2. I'll write a function to solve the problem. In this case, the function call is `increases(in1)`.\n",
"\n",
"3. I'll then submit the answer to AoC and verify it is correct. \n",
"\n",
"4. If it is correct, I'll solve part 2 in a similar way. \n",
"\n",
"5. When both parts are correct, I'll use the function `answers` to assert that the correct answers are computed. \n",
" \n",
"6. For more complex puzzles, I will include some `assert` statements to show that I am getting the right partial results on the small example in the puzzle description."
]
},
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
@@ -239,7 +227,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 8, "execution_count": 7,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -248,7 +236,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 9, "execution_count": 8,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@@ -257,7 +245,7 @@
"True" "True"
] ]
}, },
"execution_count": 9, "execution_count": 8,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -277,7 +265,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 10, "execution_count": 9,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@@ -286,7 +274,7 @@
"True" "True"
] ]
}, },
"execution_count": 10, "execution_count": 9,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -319,7 +307,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 11, "execution_count": 10,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -328,7 +316,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 12, "execution_count": 11,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@@ -337,7 +325,7 @@
"True" "True"
] ]
}, },
"execution_count": 12, "execution_count": 11,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -369,7 +357,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 13, "execution_count": 12,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@@ -378,7 +366,7 @@
"True" "True"
] ]
}, },
"execution_count": 13, "execution_count": 12,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -419,7 +407,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 14, "execution_count": 13,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -428,7 +416,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 15, "execution_count": 14,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@@ -437,7 +425,7 @@
"True" "True"
] ]
}, },
"execution_count": 15, "execution_count": 14,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -479,7 +467,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 16, "execution_count": 15,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@@ -488,7 +476,7 @@
"True" "True"
] ]
}, },
"execution_count": 16, "execution_count": 15,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -522,7 +510,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 17, "execution_count": 16,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -531,7 +519,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 18, "execution_count": 17,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@@ -540,7 +528,7 @@
"True" "True"
] ]
}, },
"execution_count": 18, "execution_count": 17,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -577,7 +565,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 19, "execution_count": 18,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@@ -586,7 +574,7 @@
"True" "True"
] ]
}, },
"execution_count": 19, "execution_count": 18,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -629,7 +617,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 20, "execution_count": 19,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@@ -645,7 +633,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 21, "execution_count": 20,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@@ -654,7 +642,7 @@
"True" "True"
] ]
}, },
"execution_count": 21, "execution_count": 20,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -688,7 +676,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 22, "execution_count": 21,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@@ -697,7 +685,7 @@
"True" "True"
] ]
}, },
"execution_count": 22, "execution_count": 21,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -720,13 +708,39 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 23, "execution_count": 22,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"in7 = parse(7, ints)[0]" "in7 = parse(7, ints)[0]"
] ]
}, },
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def fuel_cost(positions) -> int:\n",
" \"\"\"How much fuel does it cost to get everyone to the best alignment point?\"\"\"\n",
" # I happen to know that the best alignment point is the median\n",
" align = median(positions)\n",
" return sum(abs(p - align) for p in positions)\n",
"\n",
"answer(7.1, fuel_cost(in7), 352707)"
]
},
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 24, "execution_count": 24,
@@ -743,39 +757,13 @@
"output_type": "execute_result" "output_type": "execute_result"
} }
], ],
"source": [
"def fuel_cost(positions) -> int:\n",
" \"\"\"How much fuel does it cost to get everyone to the best alignment point?\"\"\"\n",
" # I happen to know that the best alignment point is the median\n",
" align = median(positions)\n",
" return sum(abs(p - align) for p in positions)\n",
"\n",
"answer(7.1, fuel_cost(in7), 352707)"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [ "source": [
"def fuel_cost2(positions) -> int:\n", "def fuel_cost2(positions) -> int:\n",
" \"\"\"How much fuel does it cost to get everyone to the best alignment point, \n", " \"\"\"How much fuel does it cost to get everyone to the best alignment point, \n",
" with nonlinear fuel costs?\"\"\"\n", " with nonlinear fuel costs?\"\"\"\n",
" # I don't know the best alignment point, so I'll try all of them\n", " # I don't know the best alignment point, so I'll try all of them\n",
" return min(sum(burn_rate2(p, align) for p in positions)\n", " return min(sum(burn_rate2(p, align) for p in positions)\n",
" for align in range(min(positions), max(positions)))\n", " for align in range(min(positions), max(positions) + 1))\n",
"\n", "\n",
"def burn_rate2(p, align) -> int:\n", "def burn_rate2(p, align) -> int:\n",
" \"\"\"The first step costs 1, the second 2, etc. (i.e. triangular numbers).\"\"\"\n", " \"\"\"The first step costs 1, the second 2, etc. (i.e. triangular numbers).\"\"\"\n",
@@ -789,12 +777,12 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Now that I got the right answer and have some time to think about it, if the travel cost were exactly quadratic, we would be minimizing the sum of square distances, and Legendre and Gauss knew that the **mean**, not the **median**, is the alignment point that does that. What's the mean?" "Now that I got the right answer and have some time to think about it, if the travel cost were exactly quadratic, we would be minimizing the sum of square distances, and Legendre and Gauss knew that the **mean**, not the **median**, is the alignment point that does that. What's the mean of the positions?"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 26, "execution_count": 25,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@@ -803,7 +791,7 @@
"490.543" "490.543"
] ]
}, },
"execution_count": 26, "execution_count": 25,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -822,7 +810,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 27, "execution_count": 26,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@@ -831,7 +819,7 @@
"95519693" "95519693"
] ]
}, },
"execution_count": 27, "execution_count": 26,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -842,7 +830,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 28, "execution_count": 27,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@@ -851,7 +839,7 @@
"95519083.0" "95519083.0"
] ]
}, },
"execution_count": 28, "execution_count": 27,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -862,7 +850,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 29, "execution_count": 28,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@@ -871,7 +859,7 @@
"95519725" "95519725"
] ]
}, },
"execution_count": 29, "execution_count": 28,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -884,7 +872,7 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"We see that rounding down gives the right answer, keeping the mean as is gives a total fuel cost that is *better* than the correct answer (but is apparently not a legal alignment point), and rounding up does a bit worse." "We see that rounding down gives the right answer, keeping the mean as is (490.543) gives a total fuel cost that is *better* than the correct answer (but is apparently not a legal alignment point), and rounding up does a bit worse."
] ]
}, },
{ {