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": [
{
"cell_type": "code",
"execution_count": 26,
"execution_count": 1,
"metadata": {
"collapsed": true
},
@ -10,7 +10,8 @@
"source": [
"import re\n",
"import itertools\n",
"from collections import defaultdict"
"from collections import defaultdict\n",
"from functools import lru_cache"
]
},
{
@ -19,11 +20,13 @@
"source": [
"# How to Count Things\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",
"Consider this problem:\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",
"> (2) Write a function to calculate the number of attendance records of length N that are OK.\n",
"\n",
@ -32,7 +35,7 @@
},
{
"cell_type": "code",
"execution_count": 27,
"execution_count": 2,
"metadata": {
"collapsed": true
},
@ -43,18 +46,16 @@
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {
"collapsed": false
},
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'ok'"
"'pass'"
]
},
"execution_count": 57,
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
@ -62,12 +63,12 @@
"source": [
"def test_ok():\n",
" assert ok(\"LAPLLP\")\n",
" assert not ok(\"LAPLLL\") # 3 Ls in a row\n",
" assert not ok(\"LAPLLA\") # 2 As overall\n",
" assert not ok(\"LAPLLL\") # 3 Ls in a row\n",
" assert not ok(\"LAPLLA\") # 2 As overall\n",
" assert ok(\"APLLPLLP\")\n",
" assert not ok(\"APLLPLLL\") # 3 Ls in a row\n",
" assert not ok(\"APLLPLLA\") # 2 As overall\n",
" return 'ok'\n",
" return 'pass'\n",
" \n",
"test_ok() "
]
@ -76,36 +77,36 @@
"cell_type": "markdown",
"metadata": {},
"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",
"execution_count": 29,
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"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",
" \"How many strings over 'LAP' of length N are ok?\"\n",
" return quantify(all_strings('LAP', N), ok)\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",
" return sum(map(pred, iterable))\n",
"\n",
"cat = ''.join\n",
"\n",
"def all_strings(alphabet, N): return map(cat, itertools.product(alphabet, repeat=N))"
"cat = ''.join"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"collapsed": false
},
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
@ -123,7 +124,7 @@
" 10: 3536}"
]
},
"execution_count": 30,
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
@ -147,28 +148,28 @@
"\n",
"* For *N* = 2, the summary looks like this:\n",
"\n",
"\n",
" #(A, L): count\n",
" {(0, 0): 2, # LP, PP\n",
" (0, 1): 1, # PL\n",
" (0, 2): 1, # LL\n",
" (1, 0): 1, # AP, LA, PA\n",
" (1, 1): 1} # AL\n",
"\n",
" #(number_of_A_in_string, number_of_L_at_end_of_string): count\n",
" #(A, L): c\n",
" {(0, 0): 2, # LP, PP\n",
" (0, 1): 1, # PL\n",
" (0, 2): 1, # LL\n",
" (1, 0): 1, # AP, LA, PA\n",
" (1, 1): 1} # AL\n",
" \n",
"\n",
"Here is a function to create the summary for `N+1`, given the summary for `N`:"
]
},
{
"cell_type": "code",
"execution_count": 33,
"execution_count": 6,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def next_summary(prev_summary):\n",
" \"Given a summary of the form {(A, L): count, ...}, return the summary for strings one character longer.\"\n",
"def next_summary(prev_summary: dict) -> dict:\n",
" \"Given a summary of the form {(A, L): count, ...}, return summary for strings one char longer.\"\n",
" summary = defaultdict(int)\n",
" for (A, L), c in prev_summary.items():\n",
" if A < 1: summary[A+1, 0] += c # transition with 'A'\n",
@ -193,13 +194,13 @@
},
{
"cell_type": "code",
"execution_count": 34,
"execution_count": 7,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def total_ok(N):\n",
"def total_ok(N) -> int:\n",
" \"How many strings of length N are ok?\"\n",
" summary = {(0, 0): 1}\n",
" for _ in range(N):\n",
@ -216,17 +217,15 @@
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {
"collapsed": false
},
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 2.43 ms, sys: 50 µs, total: 2.48 ms\n",
"Wall time: 2.48 ms\n"
"CPU times: user 1.28 ms, sys: 16 µs, total: 1.29 ms\n",
"Wall time: 1.32 ms\n"
]
},
{
@ -235,7 +234,7 @@
"5261545087067582125179062608958232695543100705754634272071166414871321070487675367"
]
},
"execution_count": 35,
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
@ -250,37 +249,35 @@
"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",
"\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",
"execution_count": 36,
"metadata": {
"collapsed": true
},
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"def total_ok(N):\n",
"def total_ok(N) -> int:\n",
" \"How many strings of length N are ok?\"\n",
" return sum(summary_for(N).values())\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",
"execution_count": 37,
"metadata": {
"collapsed": false
},
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 2.33 ms, sys: 538 µs, total: 2.87 ms\n",
"Wall time: 4.83 ms\n"
"CPU times: user 1.7 ms, sys: 78 µs, total: 1.77 ms\n",
"Wall time: 1.81 ms\n"
]
},
{
@ -289,7 +286,7 @@
"5261545087067582125179062608958232695543100705754634272071166414871321070487675367"
]
},
"execution_count": 37,
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
@ -302,20 +299,22 @@
"cell_type": "markdown",
"metadata": {},
"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",
"execution_count": 38,
"metadata": {
"collapsed": false
},
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" N ok summary(N)\n",
"-- ---- ----------\n",
" 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",
@ -331,6 +330,8 @@
}
],
"source": [
"print(' N ok summary(N)')\n",
"print('-- ---- ----------')\n",
"for N in range(11): \n",
" assert total_ok(N) == total_ok_slow(N)\n",
" print('{:2} {:4} {}'.format(N, total_ok(N), dict(summary_for(N))))"
@ -340,39 +341,50 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# >>> Count Strings with Alphabetic First Occurences\n",
"# Count Strings with Alphabetic First Occurences\n",
"\n",
"Here's another problem:\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",
"\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",
"execution_count": 39,
"execution_count": 12,
"metadata": {
"collapsed": true
},
"outputs": [],
"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",
"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",
"def first_occurrences(s):\n",
" \"The unique elements of s, in the order they appear.\" \n",
"def first_occurrences(s) -> list:\n",
" \"The unique elements of s, in the order they first appear.\" \n",
" firsts = []\n",
" for x in s:\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",
"execution_count": 40,
"metadata": {
"collapsed": false
},
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
@ -380,24 +392,30 @@
"'ok'"
]
},
"execution_count": 40,
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def test(): # s firsts(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",
"def test(): \n",
" assert valid([0, 1, 2]) and valid([0, 0, 1])\n",
" assert not valid([0, 0, 2])\n",
" assert is_prefix([0, 1, 2])\n",
" assert first_occurrences([0, 0, 2]) == [0, 2]\n",
" assert set(all_strings(2)) == {(0, 0), (0, 1), (1, 0), (1, 1)}\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, 4, 5, 3], [0, 1, 2, 3, 4, 5], True)\n",
" return 'ok'\n",
"\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",
" \n",
"test()"
@ -412,10 +430,8 @@
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {
"collapsed": false
},
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
@ -423,19 +439,15 @@
"[1, 1, 2, 5, 15, 52, 203]"
]
},
"execution_count": 41,
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import itertools \n",
"\n",
"all_strings = itertools.product\n",
"\n",
"def how_many_slow(k): \n",
"def how_many_slow(k) -> int: \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",
"[how_many_slow(k) for k in range(7)]"
]
@ -444,23 +456,21 @@
"cell_type": "markdown",
"metadata": {},
"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",
"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",
"execution_count": 42,
"execution_count": 15,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from functools import lru_cache\n",
" \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",
" return (1 if k == 0 == m else\n",
" 0 if k == 0 != m else\n",
@ -471,10 +481,8 @@
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {
"collapsed": false
},
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
@ -482,230 +490,64 @@
"47585391276764833658790768841387207826363669686825611466616334637559114497892442622672724044217756306953557882560751"
]
},
"execution_count": 43,
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"how_many(100)\n"
"how_many(100)"
]
},
{
"cell_type": "code",
"execution_count": 44,
"execution_count": 17,
"metadata": {
"collapsed": true
},
"outputs": [],
"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",
"execution_count": 68,
"metadata": {
"collapsed": false
},
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 0 10^0 1\n",
" 1 10^0 1\n",
" 2 10^0 2\n",
" 3 10^1 5\n",
" 4 10^1 15\n",
" 5 10^2 52\n",
" 6 10^2 203\n",
" 7 10^3 877\n",
" 8 10^4 4140\n",
" 9 10^4 21147\n",
" 10 10^5 115975\n",
" 20 10^14 51724158235372\n",
" 30 10^24 846749014511809332450147\n",
" 40 10^35 157450588391204931289324344702531067\n",
" 50 10^47 185724268771078270438257767181908917499221852770\n",
" 60 10^60 976939307467007552986994066961675455550246347757474482558637\n",
" 70 10^73 18075003898340511237556784424498369141305841234468097908227993035088029195\n",
" 80 10^87 991267988808424794443839434655920239360814764000951599022939879419136287216681744888844\n",
" 90 10^101 141580318123392930464192819123202606981284563291786545804370223525364095085412667328027643050802912567\n"
" 0 1\n",
" 1 1\n",
" 2 2\n",
" 3 5\n",
" 4 15\n",
" 5 52\n",
" 6 203\n",
" 7 877\n",
" 8 4140\n",
" 9 21147\n",
" 10 115975\n",
" 20 5.17242e+13\n",
" 30 8.46749e+23\n",
" 40 1.57451e+35\n",
" 50 1.85724e+47\n",
" 60 9.76939e+59\n",
" 70 1.8075e+73\n",
" 80 9.91268e+86\n",
" 90 1.4158e+101\n",
"100 4.75854e+115\n",
"110 3.46846e+130\n",
"120 5.1263e+145\n"
]
}
],
"source": [
"import math\n",
"\n",
"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))"
"for k in itertools.chain(range(10), range(10, 121, 10)):\n",
" print('{:3} {:12g}'.format(k, how_many(k)))"
]
},
{
"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": {
@ -724,9 +566,9 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.1"
"version": "3.5.3"
}
},
"nbformat": 4,
"nbformat_minor": 0
"nbformat_minor": 1
}