Add files via upload

This commit is contained in:
Peter Norvig 2017-08-27 18:05:26 -07:00 committed by GitHub
parent d795381879
commit 189d864cec

View File

@ -21,9 +21,9 @@
"source": [
"# How to Count Things\n",
"\n",
"This notebook contains problems designed to show how to count things. Right now there are three example problems.\n",
"This notebook contains problems designed to show how to count things. Right now there are four example problems.\n",
"\n",
"# Student Records: Late, Absent, Present\n",
"# (1) Student Records: Late, Absent, Present\n",
"\n",
"Consider this problem:\n",
"\n",
@ -263,8 +263,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 1.61 ms, sys: 8 µs, total: 1.62 ms\n",
"Wall time: 1.63 ms\n"
"CPU times: user 1.14 ms, sys: 0 ns, total: 1.14 ms\n",
"Wall time: 1.15 ms\n"
]
},
{
@ -337,8 +337,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 1.22 ms, sys: 39 µs, total: 1.26 ms\n",
"Wall time: 1.27 ms\n"
"CPU times: user 1.65 ms, sys: 43 µs, total: 1.7 ms\n",
"Wall time: 1.7 ms\n"
]
},
{
@ -402,7 +402,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Count Strings with Alphabetic First Occurences\n",
"# (2) Count Strings with Alphabetic First Occurences\n",
"\n",
"Here's another problem:\n",
"\n",
@ -614,7 +614,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Sol Golombs Rectangle Puzzle\n",
"# (3) Sol Golombs Rectangle Puzzle\n",
"\n",
"This problem is covered in depth in [another notebook](Golomb-puzzle.ipynb), so here I present just the part that has to do with counting things:\n",
"\n",
@ -768,6 +768,304 @@
"source": [
"rectangle_sets({1, 2, 3, 4, 5, 6})"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# (4) Counting Paths on a Grid\n",
"\n",
"Consider the following grid, where the goal is to get from `S` to `G`, making only \"right\" or \"down\" moves:\n",
"\n",
" S..........\n",
" ...........\n",
" ...........\n",
" ...........\n",
" ...........\n",
" ..........G\n",
" \n",
"One solution path would be to go right 10 times, then go down 5 times. But you could also go down 3 times, then right 10 times, then down 2 times; or take many other paths. How many paths are there? We can use the same three methods we used for the previous puzzle:\n",
"\n",
"**Method 1: Count all permutations and divide by repetitions:** Any path must consist of 10 right and 5 down moves, but they can appear in any order. Arranging 15 things in any order gives 15! = 1,307,674,368,000 possible paths. But that counts all the moves as being distinct, when actually the 10 right moves are indistinguishable, as are the 5 right moves, so we need to divide by the number of ways that they can be arranged. That gives us:"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"3003.0"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"factorial(15) / factorial(10) / factorial(5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Method 2: Count without repetitions**: Another way to look at it is that there will be 15 moves, so start with all 15 being \"right\" moves and then choose 5 of them to become \"up\" moves. So the answer is (15 choose 5), which leads to the same formula we just used.\n",
"\n",
"**Method 3: Write a program to count the paths:** We can define the function `paths(start, goal)` to count the number of paths from start location to goal location, where a location is a `(column, row)` pair of integers.\n",
"In general, the number of paths to the goal is the number of paths to the location just to the left of the goal, plus the number of paths to the location just above the goal. But there are two special cases: there is only one path (the empty path) when the start is equal to the goal, and there are zero possible paths when the goal is off the board."
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"3003"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"@lru_cache()\n",
"def paths(start, goal):\n",
" \"Number of paths to goal, using only 'right' and 'down' moves.\"\n",
" (col, row) = goal\n",
" return (1 if goal == start else\n",
" 0 if col < 0 or row < 0 else\n",
" paths(start, (col - 1, row)) + paths(start, (col, row - 1)))\n",
" \n",
"paths((0, 0), (5, 10))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can handle much larger grids (while checking the time taken, and then verifying that the answer agrees with the factorial formula):"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 220 ms, sys: 2.84 ms, total: 223 ms\n",
"Wall time: 222 ms\n"
]
},
{
"data": {
"text/plain": [
"4158251463258564744783383526326405580280466005743648708663033657304756328324008620"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"goal = (C, R) = (100, 200)\n",
"\n",
"%time paths((0, 0), goal)"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"_ == factorial(C + R) // factorial(C) // factorial(R)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Why bother with the recursive function when the formula works so well? Good question. One reason is that the different approaches reinforce each other by giving the same answer. Another reason is that we can modify the `paths` function to handle grids that have obstacles in them. I'll define a `Grid` data type, and any cell in the grid that is not a `'.'` will be considered an impassible barrier."
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def Grid(text): return tuple(text.split())\n",
"\n",
"@lru_cache()\n",
"def paths2(grid, start, goal):\n",
" \"Number of paths to goal, using only 'right' and 'down' moves.\"\n",
" (col, row) = goal\n",
" return (1 if goal == start else\n",
" 0 if col < 0 or row < 0 or grid[col][row] != '.' else\n",
" paths2(grid, start, (col - 1, row)) + \n",
" paths2(grid, start, (col, row - 1)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can verify that we get the same answer on the 11 by 6 empty grid:"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"3003"
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"paths2(Grid(\"\"\"\n",
"...........\n",
"...........\n",
"...........\n",
"...........\n",
"...........\n",
"...........\"\"\"), (0, 0), (5, 10))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here's a grid where there should be only two paths:"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"paths2(Grid(\"\"\"\n",
"...........\n",
".........|.\n",
".........|.\n",
".........|.\n",
".--------+.\n",
"...........\"\"\"), (0, 0), (5, 10))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If we break down the wall, there should be many paths (but less than 3003):"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"992"
]
},
"execution_count": 33,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"paths2(Grid(\"\"\"\n",
"...........\n",
".........|.\n",
".........|.\n",
"...........\n",
".-------...\n",
"...........\"\"\"), (0, 0), (5, 10))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can work on a larger (20 by 10) grid, but I can't verify for sure that this answer is correct:"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"58975"
]
},
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"paths2(Grid(r\"\"\"\n",
"................\\---\n",
"../......|..........\n",
"./..[]...|.[].|...\\.\n",
".\\............|.....\n",
"..\\----....|..|.....\n",
".......\\...|........\n",
"\\.......\\...........\n",
"-\\.............[]...\n",
"--\\.................\n",
"---\\....../\\........\"\"\"), (0, 0), (9, 19))"
]
}
],
"metadata": {