Add files via upload

This commit is contained in:
Peter Norvig
2017-08-25 10:15:42 -07:00
committed by GitHub
parent 1cce1fe2ee
commit 231b9e8062

View File

@@ -2,7 +2,7 @@
"cells": [ "cells": [
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 26, "execution_count": 1,
"metadata": { "metadata": {
"collapsed": true "collapsed": true
}, },
@@ -10,7 +10,8 @@
"source": [ "source": [
"import re\n", "import re\n",
"import itertools\n", "import itertools\n",
"from collections import defaultdict" "from collections import defaultdict\n",
"from functools import lru_cache"
] ]
}, },
{ {
@@ -19,11 +20,13 @@
"source": [ "source": [
"# How to Count Things\n", "# How to Count Things\n",
"\n", "\n",
"## Student Records: Late, Absent, Present\n", "This notebook lists problems designed to show how to count things. Right now there are two problems.\n",
"\n",
"# Student Records: Late, Absent, Present\n",
"\n", "\n",
"Consider this problem:\n", "Consider this problem:\n",
"\n", "\n",
"> (1) Students at a school must meet with the guidance counselor if they have two absences, or three consecutive late days. Each student's attendance record consists of a string of 'A' for absent, 'L' for late, or 'P' for present. For example: \"LAPLPA\" requires a meeting (because there are two absences), and \"LAPLPL\" is OK (there are three late days, but they are not consecutive). Write a function that takes such a string as input and that true if the student's record is OK. \n", "> (1) Students at a school must meet with the guidance counselor if they have two absences, or three consecutive late days. Each student's attendance record consists of a string of 'A' for absent, 'L' for late, or 'P' for present. For example: \"LAPLPA\" requires a meeting (because there are two absences), and \"LAPLPL\" is OK (there are three late days, but they are not consecutive). Write a function that takes such a string as input and returns `True` if the student's record is OK. \n",
"\n", "\n",
"> (2) Write a function to calculate the number of attendance records of length N that are OK.\n", "> (2) Write a function to calculate the number of attendance records of length N that are OK.\n",
"\n", "\n",
@@ -32,7 +35,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 27, "execution_count": 2,
"metadata": { "metadata": {
"collapsed": true "collapsed": true
}, },
@@ -43,18 +46,16 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 57, "execution_count": 3,
"metadata": { "metadata": {},
"collapsed": false
},
"outputs": [ "outputs": [
{ {
"data": { "data": {
"text/plain": [ "text/plain": [
"'ok'" "'pass'"
] ]
}, },
"execution_count": 57, "execution_count": 3,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -62,12 +63,12 @@
"source": [ "source": [
"def test_ok():\n", "def test_ok():\n",
" assert ok(\"LAPLLP\")\n", " assert ok(\"LAPLLP\")\n",
" assert not ok(\"LAPLLL\") # 3 Ls in a row\n", " assert not ok(\"LAPLLL\") # 3 Ls in a row\n",
" assert not ok(\"LAPLLA\") # 2 As overall\n", " assert not ok(\"LAPLLA\") # 2 As overall\n",
" assert ok(\"APLLPLLP\")\n", " assert ok(\"APLLPLLP\")\n",
" assert not ok(\"APLLPLLL\") # 3 Ls in a row\n", " assert not ok(\"APLLPLLL\") # 3 Ls in a row\n",
" assert not ok(\"APLLPLLA\") # 2 As overall\n", " assert not ok(\"APLLPLLA\") # 2 As overall\n",
" return 'ok'\n", " return 'pass'\n",
" \n", " \n",
"test_ok() " "test_ok() "
] ]
@@ -76,36 +77,36 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"For part (2), I'll start with a simple (but slow) solution that enumerates all possible strings (using `itertools.product`) and checks each one. I use the `quantify` recipe from `itertools` to count them:" "For part (2), I'll start with a simple (but slow) solution called `total_ok_slow` that enumerates `all_strings` (using `itertools.product`) and counts how many are `ok`. I use the `quantify` recipe ([from `itertools`](https://docs.python.org/3.6/library/itertools.html#itertools-recipes)) to count them:"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 29, "execution_count": 4,
"metadata": { "metadata": {
"collapsed": true "collapsed": true
}, },
"outputs": [], "outputs": [],
"source": [ "source": [
"def all_strings(alphabet, N): \n",
" \"All length-N strings over the given alphabet.\"\n",
" return map(cat, itertools.product(alphabet, repeat=N))\n",
"\n",
"def total_ok_slow(N: int) -> int:\n", "def total_ok_slow(N: int) -> int:\n",
" \"How many strings over 'LAP' of length N are ok?\"\n", " \"How many strings over 'LAP' of length N are ok?\"\n",
" return quantify(all_strings('LAP', N), ok)\n", " return quantify(all_strings('LAP', N), ok)\n",
"\n", "\n",
"def quantify(iterable, pred=bool):\n", "def quantify(iterable, pred=bool) -> int:\n",
" \"Count how many times the predicate is true of items in iterable.\"\n", " \"Count how many times the predicate is true of items in iterable.\"\n",
" return sum(map(pred, iterable))\n", " return sum(map(pred, iterable))\n",
"\n", "\n",
"cat = ''.join\n", "cat = ''.join"
"\n",
"def all_strings(alphabet, N): return map(cat, itertools.product(alphabet, repeat=N))"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 30, "execution_count": 5,
"metadata": { "metadata": {},
"collapsed": false
},
"outputs": [ "outputs": [
{ {
"data": { "data": {
@@ -123,7 +124,7 @@
" 10: 3536}" " 10: 3536}"
] ]
}, },
"execution_count": 30, "execution_count": 5,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -147,28 +148,28 @@
"\n", "\n",
"* For *N* = 2, the summary looks like this:\n", "* For *N* = 2, the summary looks like this:\n",
"\n", "\n",
"\n", " #(number_of_A_in_string, number_of_L_at_end_of_string): count\n",
" #(A, L): count\n", " #(A, L): c\n",
" {(0, 0): 2, # LP, PP\n", " {(0, 0): 2, # LP, PP\n",
" (0, 1): 1, # PL\n", " (0, 1): 1, # PL\n",
" (0, 2): 1, # LL\n", " (0, 2): 1, # LL\n",
" (1, 0): 1, # AP, LA, PA\n", " (1, 0): 1, # AP, LA, PA\n",
" (1, 1): 1} # AL\n", " (1, 1): 1} # AL\n",
"\n", " \n",
"\n", "\n",
"Here is a function to create the summary for `N+1`, given the summary for `N`:" "Here is a function to create the summary for `N+1`, given the summary for `N`:"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 33, "execution_count": 6,
"metadata": { "metadata": {
"collapsed": true "collapsed": true
}, },
"outputs": [], "outputs": [],
"source": [ "source": [
"def next_summary(prev_summary):\n", "def next_summary(prev_summary: dict) -> dict:\n",
" \"Given a summary of the form {(A, L): count, ...}, return the summary for strings one character longer.\"\n", " \"Given a summary of the form {(A, L): count, ...}, return summary for strings one char longer.\"\n",
" summary = defaultdict(int)\n", " summary = defaultdict(int)\n",
" for (A, L), c in prev_summary.items():\n", " for (A, L), c in prev_summary.items():\n",
" if A < 1: summary[A+1, 0] += c # transition with 'A'\n", " if A < 1: summary[A+1, 0] += c # transition with 'A'\n",
@@ -193,13 +194,13 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 34, "execution_count": 7,
"metadata": { "metadata": {
"collapsed": true "collapsed": true
}, },
"outputs": [], "outputs": [],
"source": [ "source": [
"def total_ok(N):\n", "def total_ok(N) -> int:\n",
" \"How many strings of length N are ok?\"\n", " \"How many strings of length N are ok?\"\n",
" summary = {(0, 0): 1}\n", " summary = {(0, 0): 1}\n",
" for _ in range(N):\n", " for _ in range(N):\n",
@@ -216,17 +217,15 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 35, "execution_count": 8,
"metadata": { "metadata": {},
"collapsed": false
},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"CPU times: user 2.43 ms, sys: 50 µs, total: 2.48 ms\n", "CPU times: user 1.28 ms, sys: 16 µs, total: 1.29 ms\n",
"Wall time: 2.48 ms\n" "Wall time: 1.32 ms\n"
] ]
}, },
{ {
@@ -235,7 +234,7 @@
"5261545087067582125179062608958232695543100705754634272071166414871321070487675367" "5261545087067582125179062608958232695543100705754634272071166414871321070487675367"
] ]
}, },
"execution_count": 35, "execution_count": 8,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -250,37 +249,35 @@
"source": [ "source": [
"There are over 10<sup>80</sup> ok strings of length 300; more than the number of atoms in the universe. But it only took around a millisecond to count them.\n", "There are over 10<sup>80</sup> ok strings of length 300; more than the number of atoms in the universe. But it only took around a millisecond to count them.\n",
"\n", "\n",
"Dynamic programming can be done top-down (where we start at `N` and work down to `0`):" "Dynamic programming can also be done top-down (where we start at `N` and work down to `0`):"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 36, "execution_count": 9,
"metadata": { "metadata": {},
"collapsed": true
},
"outputs": [], "outputs": [],
"source": [ "source": [
"def total_ok(N):\n", "def total_ok(N) -> int:\n",
" \"How many strings of length N are ok?\"\n", " \"How many strings of length N are ok?\"\n",
" return sum(summary_for(N).values())\n", " return sum(summary_for(N).values())\n",
" \n", " \n",
"def summary_for(N): return ({(0, 0): 1} if N == 0 else next_summary(summary_for(N - 1)))" "def summary_for(N) -> dict: \n",
" \"The {(A, L): count} summary for strings of length N.\"\n",
" return ({(0, 0): 1} if N == 0 else next_summary(summary_for(N - 1)))"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 37, "execution_count": 10,
"metadata": { "metadata": {},
"collapsed": false
},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"CPU times: user 2.33 ms, sys: 538 µs, total: 2.87 ms\n", "CPU times: user 1.7 ms, sys: 78 µs, total: 1.77 ms\n",
"Wall time: 4.83 ms\n" "Wall time: 1.81 ms\n"
] ]
}, },
{ {
@@ -289,7 +286,7 @@
"5261545087067582125179062608958232695543100705754634272071166414871321070487675367" "5261545087067582125179062608958232695543100705754634272071166414871321070487675367"
] ]
}, },
"execution_count": 37, "execution_count": 10,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
@@ -302,20 +299,22 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Let's make sure we're getting the same results as before, and take a look at the summaries for the first few values of `N`:" "We get the same answer in about the same amopunt of time.\n",
"\n",
"Let's verify our results against the slow, reliable `total_ok_slow`, and look at the summaries for the first few values of `N`:"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 38, "execution_count": 11,
"metadata": { "metadata": {},
"collapsed": false
},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
" N ok summary(N)\n",
"-- ---- ----------\n",
" 0 1 {(0, 0): 1}\n", " 0 1 {(0, 0): 1}\n",
" 1 3 {(0, 1): 1, (1, 0): 1, (0, 0): 1}\n", " 1 3 {(0, 1): 1, (1, 0): 1, (0, 0): 1}\n",
" 2 8 {(0, 1): 1, (1, 0): 3, (0, 0): 2, (0, 2): 1, (1, 1): 1}\n", " 2 8 {(0, 1): 1, (1, 0): 3, (0, 0): 2, (0, 2): 1, (1, 1): 1}\n",
@@ -331,6 +330,8 @@
} }
], ],
"source": [ "source": [
"print(' N ok summary(N)')\n",
"print('-- ---- ----------')\n",
"for N in range(11): \n", "for N in range(11): \n",
" assert total_ok(N) == total_ok_slow(N)\n", " assert total_ok(N) == total_ok_slow(N)\n",
" print('{:2} {:4} {}'.format(N, total_ok(N), dict(summary_for(N))))" " print('{:2} {:4} {}'.format(N, total_ok(N), dict(summary_for(N))))"
@@ -340,39 +341,50 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"# >>> Count Strings with Alphabetic First Occurences\n", "# Count Strings with Alphabetic First Occurences\n",
"\n",
"Here's another problem:\n",
"\n", "\n",
"> Given an alphabet of length k, how many strings of length k can be formed such that the first occurrences of each character in the string are a prefix of the alphabet?\n", "> Given an alphabet of length k, how many strings of length k can be formed such that the first occurrences of each character in the string are a prefix of the alphabet?\n",
"\n", "\n",
"Let's first make sure we understand the problem. I will choose to represent a string as a list of integers, like `[0, 1, 2]` rather than as a `str` like `\"abc\"`, and the alphabet will always be `range(k)`. So, the string `[0, 1, 0, 2]` would be valid, because the first occurrences are `[0, 1, 2]`, but `[0, 1, 0, 3]` would not be valid, since `[0, 1, 3]` is not a prefix of `range(4)`. I'll define three key concepts:" "Let's first make sure we understand the problem. Since *k* could go well beyond 26, I will choose as my alphabet the integers, not the letters `'abc...'`. An alphabet of length *k* is `range(k)`, and a valid string of length 3 could be\n",
"`[0, 1, 2]` or `[0, 0, 1]` (or other possibilities). These are valid because the first occurrence of each character for these strings are `[0, 1, 2]` and `[0, 1]`, respectively, and these are prefixes of `range(3)`. But `[0, 0, 2]` is not valid, because the first occurrences are `[0, 2]`, and this is not a prefix (because it is missing the `1`). \n",
"\n",
"I'll define four key concepts:"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 39, "execution_count": 12,
"metadata": { "metadata": {
"collapsed": true "collapsed": true
}, },
"outputs": [], "outputs": [],
"source": [ "source": [
"def valid(s): return is_prefix(first_occurrences(s))\n", "def valid(s) -> bool: \n",
" \"A string is valid if its first occurrences are a prefix of the alphabet.\"\n",
" return is_prefix(first_occurrences(s))\n",
"\n", "\n",
"def is_prefix(s): return s == list(range(len(s)))\n", "def is_prefix(s) -> bool: \n",
" \"A string is a valid prefix if it is consecutive integers starting from 0.\"\n",
" return s == list(range(len(s)))\n",
"\n", "\n",
"def first_occurrences(s):\n", "def first_occurrences(s) -> list:\n",
" \"The unique elements of s, in the order they appear.\" \n", " \"The unique elements of s, in the order they first appear.\" \n",
" firsts = []\n", " firsts = []\n",
" for x in s:\n", " for x in s:\n",
" if x not in firsts: firsts.append(x)\n", " if x not in firsts: firsts.append(x)\n",
" return firsts " " return firsts \n",
"\n",
"def all_strings(k): \n",
" \"All strings of length k over an alphabet of k ints.\"\n",
" return itertools.product(range(k), repeat=k)"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 40, "execution_count": 13,
"metadata": { "metadata": {},
"collapsed": false
},
"outputs": [ "outputs": [
{ {
"data": { "data": {
@@ -380,24 +392,30 @@
"'ok'" "'ok'"
] ]
}, },
"execution_count": 40, "execution_count": 13,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
], ],
"source": [ "source": [
"def test(): # s firsts(s) valid(s)\n", "def test(): \n",
" assert test1([0, 1, 2], [0, 1, 2], True) \n", " assert valid([0, 1, 2]) and valid([0, 0, 1])\n",
" assert test1([0, 0, 0], [0], True) \n", " assert not valid([0, 0, 2])\n",
" assert test1([1], [1], False) \n", " assert is_prefix([0, 1, 2])\n",
" assert test1([0, 1, 3], [0, 1, 3], False)\n", " assert first_occurrences([0, 0, 2]) == [0, 2]\n",
" assert test1([0, 1, 3, 2], [0, 1, 3, 2], False)\n", " assert set(all_strings(2)) == {(0, 0), (0, 1), (1, 0), (1, 1)}\n",
" assert test1([0, 1, 0, 1, 0, 2, 1], [0, 1, 2], True)\n", " # s first_occurrences(s) valid(s)\n",
" assert test1([0, 1, 2], [0, 1, 2], True) \n",
" assert test1([0, 0, 0], [0], True) \n",
" assert test1([1], [1], False) \n",
" assert test1([0, 1, 3], [0, 1, 3], False)\n",
" assert test1([0, 1, 3, 2], [0, 1, 3, 2], False)\n",
" assert test1([0, 1, 0, 1, 0, 2, 1], [0, 1, 2], True)\n",
" assert test1([0, 1, 0, 2, 1, 3, 1, 2, 5, 4, 3], [0, 1, 2, 3, 5, 4], False)\n", " assert test1([0, 1, 0, 2, 1, 3, 1, 2, 5, 4, 3], [0, 1, 2, 3, 5, 4], False)\n",
" assert test1([0, 1, 0, 2, 1, 3, 1, 2, 4, 5, 3], [0, 1, 2, 3, 4, 5], True)\n",
" return 'ok'\n", " return 'ok'\n",
"\n", "\n",
"def test1(s, firsts, is_valid):\n", "def test1(s, firsts, is_valid):\n",
" \"\"\"Test whether first_occurrences(s) == firsts and valid(s) == is_valid\"\"\"\n",
" return first_occurrences(s) == firsts and valid(s) == is_valid\n", " return first_occurrences(s) == firsts and valid(s) == is_valid\n",
" \n", " \n",
"test()" "test()"
@@ -412,10 +430,8 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 41, "execution_count": 14,
"metadata": { "metadata": {},
"collapsed": false
},
"outputs": [ "outputs": [
{ {
"data": { "data": {
@@ -423,19 +439,15 @@
"[1, 1, 2, 5, 15, 52, 203]" "[1, 1, 2, 5, 15, 52, 203]"
] ]
}, },
"execution_count": 41, "execution_count": 14,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
], ],
"source": [ "source": [
"import itertools \n", "def how_many_slow(k) -> int: \n",
"\n",
"all_strings = itertools.product\n",
"\n",
"def how_many_slow(k): \n",
" \"\"\"Count the number of valid strings. (Try all possible strings.)\"\"\"\n", " \"\"\"Count the number of valid strings. (Try all possible strings.)\"\"\"\n",
" return sum(valid(s) for s in all_strings(range(k), repeat=k))\n", " return quantify(all_strings(k), valid)\n",
"\n", "\n",
"[how_many_slow(k) for k in range(7)]" "[how_many_slow(k) for k in range(7)]"
] ]
@@ -444,23 +456,21 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Now let's think about how to speed that up.I don't want to have to consider every possible string, because there are too many ($k^k$) of them. Can I group together many strings and just count the number of them, without enumerating each one? For example, if I knew there were 52 valid strings of length $k-1$ (and didn't know anything else about them), can I tell how many valid strings of length $k$ there are? I don't see a way to do this, because the number of ways to extend a valid string is dependent on the number of distinct characters in the string. If a string has $m$ distinct characters, then I can extend it by repeating any of those $m$ characters, or by introducing a first occurrence of character number $m+1$.\n", "Now let's think about how to speed that up. I don't want to have to consider every possible string, because there are too many ($k^k$) of them. Can I group together many strings and just count the number of them, without enumerating each one? For example, if I knew there were 52 valid strings of length $k-1$ (and didn't know anything else about them), can I tell how many valid strings of length $k$ there are? I don't see a way to do this directly, because the number of ways to extend a valid string is dependent on the number of distinct characters in the string. If a string has $m$ distinct characters, then I can extend it in $m$ waysby repeating any of those $m$ characters, or I can introduce a first occurrence of character number $m+1$ in just 1 way.\n",
"\n", "\n",
"So I need to keep track of the number of valid strings of length $k$ that have exactly $m$ distinct characters (which, by definition, must be `range(m)`). I'll call that number `C(k, m)`. Then I can define `how_many(k)` as the sum over all values of `m` of `C(k, m)`:" "So I need to keep track of the number of valid strings of length $k$ that have exactly $m$ distinct characters (those characters must be exactly `range(m)`). I'll call that number `C(k, m)`. Because I can reach a recursive call to `C(k, m)` by many paths, I will use the `lru_cache` decorator to keep track of the computations that I have already done. Then I can define `how_many(k)` as the sum over all values of `m` of `C(k, m)`:"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 42, "execution_count": 15,
"metadata": { "metadata": {
"collapsed": true "collapsed": true
}, },
"outputs": [], "outputs": [],
"source": [ "source": [
"from functools import lru_cache\n",
" \n",
"@lru_cache()\n", "@lru_cache()\n",
"def C(k, m):\n", "def C(k, m) -> int:\n",
" \"Count the number of valid strings of length k, that use m distinct characters.\"\n", " \"Count the number of valid strings of length k, that use m distinct characters.\"\n",
" return (1 if k == 0 == m else\n", " return (1 if k == 0 == m else\n",
" 0 if k == 0 != m else\n", " 0 if k == 0 != m else\n",
@@ -471,10 +481,8 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 43, "execution_count": 16,
"metadata": { "metadata": {},
"collapsed": false
},
"outputs": [ "outputs": [
{ {
"data": { "data": {
@@ -482,230 +490,64 @@
"47585391276764833658790768841387207826363669686825611466616334637559114497892442622672724044217756306953557882560751" "47585391276764833658790768841387207826363669686825611466616334637559114497892442622672724044217756306953557882560751"
] ]
}, },
"execution_count": 43, "execution_count": 16,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }
], ],
"source": [ "source": [
"how_many(100)\n" "how_many(100)"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 44, "execution_count": 17,
"metadata": { "metadata": {
"collapsed": true "collapsed": true
}, },
"outputs": [], "outputs": [],
"source": [ "source": [
"assert(all(how_many(k) == how_many_slow(k) for k in range(7)))" "assert all(how_many(k) == how_many_slow(k) for k in range(7))"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 68, "execution_count": 18,
"metadata": { "metadata": {},
"collapsed": false
},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
" 0 10^0 1\n", " 0 1\n",
" 1 10^0 1\n", " 1 1\n",
" 2 10^0 2\n", " 2 2\n",
" 3 10^1 5\n", " 3 5\n",
" 4 10^1 15\n", " 4 15\n",
" 5 10^2 52\n", " 5 52\n",
" 6 10^2 203\n", " 6 203\n",
" 7 10^3 877\n", " 7 877\n",
" 8 10^4 4140\n", " 8 4140\n",
" 9 10^4 21147\n", " 9 21147\n",
" 10 10^5 115975\n", " 10 115975\n",
" 20 10^14 51724158235372\n", " 20 5.17242e+13\n",
" 30 10^24 846749014511809332450147\n", " 30 8.46749e+23\n",
" 40 10^35 157450588391204931289324344702531067\n", " 40 1.57451e+35\n",
" 50 10^47 185724268771078270438257767181908917499221852770\n", " 50 1.85724e+47\n",
" 60 10^60 976939307467007552986994066961675455550246347757474482558637\n", " 60 9.76939e+59\n",
" 70 10^73 18075003898340511237556784424498369141305841234468097908227993035088029195\n", " 70 1.8075e+73\n",
" 80 10^87 991267988808424794443839434655920239360814764000951599022939879419136287216681744888844\n", " 80 9.91268e+86\n",
" 90 10^101 141580318123392930464192819123202606981284563291786545804370223525364095085412667328027643050802912567\n" " 90 1.4158e+101\n",
"100 4.75854e+115\n",
"110 3.46846e+130\n",
"120 5.1263e+145\n"
] ]
} }
], ],
"source": [ "source": [
"import math\n", "for k in itertools.chain(range(10), range(10, 121, 10)):\n",
"\n", " print('{:3} {:12g}'.format(k, how_many(k)))"
"for k in itertools.chain(range(10), range(10, 100, 10)):\n",
" n = how_many(k)\n",
" print('{:3} 10^{:<3} {:d}'.format(k, round(math.log10(n)), n))"
] ]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 74,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"-11 -9 5\n",
"-5 9 11\n",
"-1 4 11\n",
"done 20\n"
]
}
],
"source": [
"N = 20\n",
"for a, b, p in itertools.combinations(range(-N, N), 3):\n",
" if (b + p) * (a + p) * (a + b) == 0: continue\n",
" if a/(b + p) + b/(a + p) + p/(a + b) == 4:\n",
" print(a, b, p)\n",
"print('done', N)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"(a^3 + a^2 b + a^2 p + a b^2 + 3 a b p + a p^2 + b^3 + b^2 p + b p^2 + p^3)/((a + b) (a + p) (b + p)) = 4"
]
},
{
"cell_type": "code",
"execution_count": 104,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 25 s, sys: 106 ms, total: 25.1 s\n",
"Wall time: 25.3 s\n"
]
},
{
"data": {
"text/plain": [
"945"
]
},
"execution_count": 104,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sides = range(1, 11)\n",
"from itertools import permutations\n",
"\n",
"def sortuple(items): return tuple(sorted(items))\n",
"\n",
"def sets_of_rectangles(sides): return set(*map(set_of_rectangles, permutations(sides)))\n",
"\n",
"def set_of_rectangles(sides): return sortuple(sortuple(sides[i:i+2]) for i in range(0, len(sides), 2))\n",
"\n",
"%time len(sets_of_rectangles(sides))"
]
},
{
"cell_type": "code",
"execution_count": 99,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"((1, 2), (3, 4), (5, 6), (7, 8), (9, 10))"
]
},
"execution_count": 99,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from itertools import zip_longest\n",
"\n",
"def grouper(iterable, n, fillvalue=None):\n",
" \"Collect data into fixed-length chunks or blocks\"\n",
" # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx\"\n",
" args = [iter(iterable)] * n\n",
" return zip_longest(*args, fillvalue=fillvalue)\n",
"\n",
"def grouper(iterable, n):\n",
" \n",
"\n",
"tuple(grouper(sides, 2))"
]
},
{
"cell_type": "code",
"execution_count": 93,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"{frozenset({(1, 3), (2, 5), (4, 6)}),\n",
" frozenset({(1, 2), (3, 5), (4, 6)}),\n",
" frozenset({(1, 3), (2, 6), (4, 5)}),\n",
" frozenset({(1, 6), (2, 3), (4, 5)}),\n",
" frozenset({(1, 4), (2, 3), (5, 6)}),\n",
" frozenset({(1, 6), (2, 4), (3, 5)}),\n",
" frozenset({(1, 6), (2, 5), (3, 4)}),\n",
" frozenset({(1, 5), (2, 3), (4, 6)}),\n",
" frozenset({(1, 5), (2, 6), (3, 4)}),\n",
" frozenset({(1, 3), (2, 4), (5, 6)}),\n",
" frozenset({(1, 4), (2, 6), (3, 5)}),\n",
" frozenset({(1, 2), (3, 4), (5, 6)}),\n",
" frozenset({(1, 5), (2, 4), (3, 6)}),\n",
" frozenset({(1, 2), (3, 6), (4, 5)}),\n",
" frozenset({(1, 4), (2, 5), (3, 6)})}"
]
},
"execution_count": 93,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sets_of_rectangles(sides)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
} }
], ],
"metadata": { "metadata": {
@@ -724,9 +566,9 @@
"name": "python", "name": "python",
"nbconvert_exporter": "python", "nbconvert_exporter": "python",
"pygments_lexer": "ipython3", "pygments_lexer": "ipython3",
"version": "3.5.1" "version": "3.5.3"
} }
}, },
"nbformat": 4, "nbformat": 4,
"nbformat_minor": 0 "nbformat_minor": 1
} }