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",
"# Advent of Code 2021\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",
"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",
@@ -44,25 +44,7 @@
"cell_type": "markdown",
"metadata": {},
"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",
"\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`:"
"Now two functions that I will use each day, `parse` and `answer`, and two parser functions, `ints` and `atoms`:"
]
},
{
@@ -79,30 +61,17 @@
"def answer(puzzle_number, got, expected) -> bool:\n",
" \"\"\"Verify the answer we got was expected.\"\"\"\n",
" assert got == expected, f'For {puzzle_number}, expected {expected} but got {got}.'\n",
" return True"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Two useful parsers are `ints` and `atoms`:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
" return True\n",
"\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",
"\n",
"Atom = Union[float, int, str]\n",
"\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",
"\n",
"def atom(text: str) -> Atom:\n",
@@ -127,7 +96,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
@@ -168,13 +137,38 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"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",
"execution_count": 6,
@@ -191,31 +185,6 @@
"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": [
"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",
@@ -225,6 +194,25 @@
"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",
"metadata": {},
@@ -239,7 +227,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
@@ -248,7 +236,7 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 8,
"metadata": {},
"outputs": [
{
@@ -257,7 +245,7 @@
"True"
]
},
"execution_count": 9,
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
@@ -277,7 +265,7 @@
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 9,
"metadata": {},
"outputs": [
{
@@ -286,7 +274,7 @@
"True"
]
},
"execution_count": 10,
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
@@ -319,7 +307,7 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
@@ -328,7 +316,7 @@
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": 11,
"metadata": {},
"outputs": [
{
@@ -337,7 +325,7 @@
"True"
]
},
"execution_count": 12,
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
@@ -369,7 +357,7 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 12,
"metadata": {},
"outputs": [
{
@@ -378,7 +366,7 @@
"True"
]
},
"execution_count": 13,
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
@@ -419,7 +407,7 @@
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
@@ -428,7 +416,7 @@
},
{
"cell_type": "code",
"execution_count": 15,
"execution_count": 14,
"metadata": {},
"outputs": [
{
@@ -437,7 +425,7 @@
"True"
]
},
"execution_count": 15,
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
@@ -479,7 +467,7 @@
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": 15,
"metadata": {},
"outputs": [
{
@@ -488,7 +476,7 @@
"True"
]
},
"execution_count": 16,
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
@@ -522,7 +510,7 @@
},
{
"cell_type": "code",
"execution_count": 17,
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
@@ -531,7 +519,7 @@
},
{
"cell_type": "code",
"execution_count": 18,
"execution_count": 17,
"metadata": {},
"outputs": [
{
@@ -540,7 +528,7 @@
"True"
]
},
"execution_count": 18,
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
@@ -577,7 +565,7 @@
},
{
"cell_type": "code",
"execution_count": 19,
"execution_count": 18,
"metadata": {},
"outputs": [
{
@@ -586,7 +574,7 @@
"True"
]
},
"execution_count": 19,
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
@@ -629,7 +617,7 @@
},
{
"cell_type": "code",
"execution_count": 20,
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
@@ -645,7 +633,7 @@
},
{
"cell_type": "code",
"execution_count": 21,
"execution_count": 20,
"metadata": {},
"outputs": [
{
@@ -654,7 +642,7 @@
"True"
]
},
"execution_count": 21,
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
@@ -688,7 +676,7 @@
},
{
"cell_type": "code",
"execution_count": 22,
"execution_count": 21,
"metadata": {},
"outputs": [
{
@@ -697,7 +685,7 @@
"True"
]
},
"execution_count": 22,
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
@@ -720,13 +708,39 @@
},
{
"cell_type": "code",
"execution_count": 23,
"execution_count": 22,
"metadata": {},
"outputs": [],
"source": [
"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",
"execution_count": 24,
@@ -743,39 +757,13 @@
"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": [
"def fuel_cost2(positions) -> int:\n",
" \"\"\"How much fuel does it cost to get everyone to the best alignment point, \n",
" with nonlinear fuel costs?\"\"\"\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",
" for align in range(min(positions), max(positions)))\n",
" for align in range(min(positions), max(positions) + 1))\n",
"\n",
"def burn_rate2(p, align) -> int:\n",
" \"\"\"The first step costs 1, the second 2, etc. (i.e. triangular numbers).\"\"\"\n",
@@ -789,12 +777,12 @@
"cell_type": "markdown",
"metadata": {},
"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",
"execution_count": 26,
"execution_count": 25,
"metadata": {},
"outputs": [
{
@@ -803,7 +791,7 @@
"490.543"
]
},
"execution_count": 26,
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
@@ -822,7 +810,7 @@
},
{
"cell_type": "code",
"execution_count": 27,
"execution_count": 26,
"metadata": {},
"outputs": [
{
@@ -831,7 +819,7 @@
"95519693"
]
},
"execution_count": 27,
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
@@ -842,7 +830,7 @@
},
{
"cell_type": "code",
"execution_count": 28,
"execution_count": 27,
"metadata": {},
"outputs": [
{
@@ -851,7 +839,7 @@
"95519083.0"
]
},
"execution_count": 28,
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
@@ -862,7 +850,7 @@
},
{
"cell_type": "code",
"execution_count": 29,
"execution_count": 28,
"metadata": {},
"outputs": [
{
@@ -871,7 +859,7 @@
"95519725"
]
},
"execution_count": 29,
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
@@ -884,7 +872,7 @@
"cell_type": "markdown",
"metadata": {},
"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."
]
},
{