Peter Norvig"
]
},
{
@@ -13,15 +15,15 @@
"source": [
"This problem by Solomon Golomb was presented by Gary Antonik in his 14/4/14 New York Times [Numberplay column](http://wordplay.blogs.nytimes.com/2014/04/14/rectangle):\n",
"\n",
- ">Say you’re given the following challenge: create a set of five rectangles that have sides of length 1, 2, 3, 4, 5, 6, 7, 8, 9 and 10 units. You can combine sides in a variety of ways: for example, you could create a set of rectangles with dimensions 1 x 3, 2 x 4, 5 x 7, 6 x 8 and 9 x 10.\n",
+ ">*Say you’re given the following challenge: create a set of five rectangles that have sides of length 1, 2, 3, 4, 5, 6, 7, 8, 9 and 10 units. You can combine sides in a variety of ways: for example, you could create a set of rectangles with dimensions 1 x 3, 2 x 4, 5 x 7, 6 x 8 and 9 x 10.*\n",
">\n",
- ">1. How many different sets of five rectangles are possible?\n",
+ ">1. *How many different sets of five rectangles are possible?*\n",
">\n",
- ">2. What are the maximum and minimum values for the total areas of the five rectangles?\n",
+ ">2. *What are the maximum and minimum values for the total areas of the five rectangles?*\n",
">\n",
- ">3. What other values for the total areas of the five rectangles are possible?\n",
+ ">3. *What other values for the total areas of the five rectangles are possible?*\n",
">\n",
- ">4. Which sets of rectangles may be assembled to form a square?\n",
+ ">4. *Which sets of rectangles may be assembled to form a square?*\n",
"\n",
"To me, these are interesting questions because, first, I have a (slight) personal connection to Solomon Golomb (my former colleague at USC) and to Nelson Blachman (the father of my colleague Nancy Blachman), who presented the problem to Antonik, and second, I find it interesting that the problems span the range from mathematical to computational. Let's answer them."
]
@@ -70,7 +72,9 @@
{
"cell_type": "code",
"execution_count": 1,
- "metadata": {},
+ "metadata": {
+ "collapsed": true
+ },
"outputs": [],
"source": [
"def rectangle_sets(sides):\n",
@@ -87,7 +91,9 @@
{
"cell_type": "code",
"execution_count": 2,
- "metadata": {},
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
@@ -114,7 +120,9 @@
{
"cell_type": "code",
"execution_count": 3,
- "metadata": {},
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
@@ -147,7 +155,9 @@
{
"cell_type": "code",
"execution_count": 4,
- "metadata": {},
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
@@ -188,13 +198,15 @@
}
},
"source": [
- "I think I know this one, but I'm not completely sure. I know that a rectangle with a fixed perimeter has maximum area when it is a square. My guess is that the maximum total area occurs when each rectangle is *almost* square: a 9 × 10 rectangle; 7 × 8; and so on. So that would give us a maximum area of:"
+ "I think I know this one, but I'm not sure. I know that a rectangle with a fixed perimeter has maximum area when it is a square. My guess is that the maximum *total* area occurs when each rectangle is *almost* square: a 9 × 10 rectangle; 7 × 8; and so on. So that would give us a maximum area of:"
]
},
{
"cell_type": "code",
"execution_count": 5,
- "metadata": {},
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
@@ -221,7 +233,9 @@
{
"cell_type": "code",
"execution_count": 6,
- "metadata": {},
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
@@ -248,7 +262,9 @@
{
"cell_type": "code",
"execution_count": 7,
- "metadata": {},
+ "metadata": {
+ "collapsed": true
+ },
"outputs": [],
"source": [
"def total_area(rectangles): return sum(w * h for (w, h) in rectangles)"
@@ -257,7 +273,9 @@
{
"cell_type": "code",
"execution_count": 8,
- "metadata": {},
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
@@ -277,7 +295,9 @@
{
"cell_type": "code",
"execution_count": 9,
- "metadata": {},
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
@@ -318,7 +338,9 @@
{
"cell_type": "code",
"execution_count": 10,
- "metadata": {},
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"name": "stdout",
@@ -343,7 +365,9 @@
{
"cell_type": "code",
"execution_count": 11,
- "metadata": {},
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
@@ -364,7 +388,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "The answer is: \"All the integers between 110 and 190 inclusive, except 176, 185, 188, and 189.\""
+ "The answer is: \"All the integers between the min (110) and max (190), except 176, 185, 188, and 189.\""
]
},
{
@@ -378,13 +402,15 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "The only way I can think about this is to write a program; I don't see any way to work it out by hand. I do know that the total area will have to be a perfect square, and since the total area is between 110 and 191, that means either 121, 144, or 169:"
+ "The only way I can think about this is to write a program; I don't see any way to work it out by hand. I do know that the total area will have to be a perfect square, so:"
]
},
{
"cell_type": "code",
"execution_count": 12,
- "metadata": {},
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
@@ -432,30 +458,11 @@
}
],
"source": [
- "perfect_squares = {i ** 2 for i in range(100)}\n",
+ "def is_perfect_square(n): return is_integer(n ** 0.5)\n",
+ "def is_integer(n): return (int(n) == n)\n",
"\n",
"sorted((total_area(s), s) \n",
- " for s in all_sets if total_area(s) in perfect_squares)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "35"
- ]
- },
- "execution_count": 13,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "len(_)"
+ " for s in all_sets if is_perfect_square(total_area(s)))"
]
},
{
@@ -472,8 +479,10 @@
},
{
"cell_type": "code",
- "execution_count": 14,
- "metadata": {},
+ "execution_count": 13,
+ "metadata": {
+ "collapsed": true
+ },
"outputs": [],
"source": [
"empty = (0, 0)\n",
@@ -486,8 +495,10 @@
},
{
"cell_type": "code",
- "execution_count": 15,
- "metadata": {},
+ "execution_count": 14,
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
@@ -499,7 +510,7 @@
" [(0, 0), (0, 0), (0, 0), (0, 0), (0, 0)]]"
]
},
- "execution_count": 15,
+ "execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
@@ -517,8 +528,10 @@
},
{
"cell_type": "code",
- "execution_count": 16,
- "metadata": {},
+ "execution_count": 15,
+ "metadata": {
+ "collapsed": true
+ },
"outputs": [],
"source": [
"def place_rectangle_at(rect, grid, pos):\n",
@@ -537,8 +550,10 @@
},
{
"cell_type": "code",
- "execution_count": 17,
- "metadata": {},
+ "execution_count": 16,
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
@@ -550,7 +565,7 @@
" [(0, 0), (0, 0), (3, 4), (3, 4), (3, 4)]]"
]
},
- "execution_count": 17,
+ "execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
@@ -562,8 +577,10 @@
},
{
"cell_type": "code",
- "execution_count": 18,
- "metadata": {},
+ "execution_count": 17,
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
@@ -575,7 +592,7 @@
" [(2, 5), (2, 5), (3, 4), (3, 4), (3, 4)]]"
]
},
- "execution_count": 18,
+ "execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
@@ -590,8 +607,10 @@
},
{
"cell_type": "code",
- "execution_count": 19,
- "metadata": {},
+ "execution_count": 18,
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
@@ -603,7 +622,7 @@
" [(2, 5), (2, 5), (3, 4), (3, 4), (3, 4)]]"
]
},
- "execution_count": 19,
+ "execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
@@ -615,8 +634,10 @@
},
{
"cell_type": "code",
- "execution_count": 20,
- "metadata": {},
+ "execution_count": 19,
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"name": "stdout",
@@ -635,6 +656,8 @@
"cell_type": "markdown",
"metadata": {},
"source": [
+ "# Packing Strategy\n",
+ "\n",
"Now we need a strategy for packing a set of rectangles onto a grid. I know that many variants of [bin packing problems](http://en.wikipedia.org/wiki/Bin_packing_problem) are NP-hard, but we only have 5 rectangles, so it should be easy: just exhaustively try each rectangle in each possible position, and in both possible orientations (horizontal and vertical). But placing rectangles is commutative, so we can do this two ways:\n",
"\n",
"> Way 1: Considering the *rectangles* in a fixed order, try every possible *position* for each *rectangle*.\n",
@@ -645,37 +668,34 @@
"\n",
"In Way 1, we could pre-sort the rectangles (say, biggest first). Then we try to put the biggest rectangle in all possible positions on the grid, and for each position that fits, try putting the second biggest rectangle in all remaining positions, and so on. As a rough estimate, assume there are on average about 10 ways to place a rectangle. Then this way will look at about 10
5 = 100,000 combinations.\n",
"\n",
- "In Way 2, we consider the positions in some fixed order; say top-to-bottom, left-to right. Take the first empty position (say, the upper left corner). Try putting each of the rectangles there, and for each one that fits, try all possible rectangles in the next empty position, and so on. There are only 5! permutations of rectangles, and each rectangle can go either horizontaly or vertically, so we would have to consider 5! × 2
5 = 3840 combinations. Since 3840 < 100,000, I'll go with Way 2. Here is a more precise description:\n",
+ "In Way 2, we consider the positions in some fixed order; say top-to-bottom, left-to right. Take the first empty position (say, the upper left corner). Try putting each of the rectangles there, and for each one that fits, try all possible rectangles in the next empty position, and so on. There are only 5! permutations of rectangles, and each rectangle can go either horizontally or vertically, so we would have to consider 5! × 2
5 = 3840 combinations. Since 3840 < 100,000, I'll go with Way 2. Here is a more precise description:\n",
"\n",
"> Way 2: To `pack` a set of rectangles onto a grid, find the first empty cell on the grid. Try in turn all possible placements of any rectangle (in either orientation) at that position. For each one that fits, try to `pack` the remaining rectangles, and return the resulting grid if one of these packings succeeds. "
]
},
{
"cell_type": "code",
- "execution_count": 21,
- "metadata": {},
+ "execution_count": 20,
+ "metadata": {
+ "collapsed": true
+ },
"outputs": [],
"source": [
"def pack(rectangles, grid):\n",
" \"\"\"Find a way to pack all rectangles onto grid and return the packed grid,\n",
" or return None if not possible.\"\"\"\n",
- " if not rectangles:\n",
+ " if not rectangles or grid is None:\n",
" return grid \n",
" pos = first_empty_cell(grid)\n",
- " if grid and pos:\n",
- " for (rectangles2, grid2) in rectangle_placements(rectangles, grid, pos):\n",
- " solution = pack(rectangles2, grid2)\n",
+ " if pos is None:\n",
+ " return None\n",
+ " for rect in rectangles:\n",
+ " for (w, h) in [rect, reversed(rect)]:\n",
+ " grid2 = place_rectangle_at((w, h), grid, pos)\n",
+ " solution = pack(rectangles - {rect}, grid2)\n",
" if solution:\n",
" return solution\n",
"\n",
- "def rectangle_placements(rectangles, grid, pos):\n",
- " \"Yield all (rect, grid) pairs that result from placing a rectangle at pos on grid.\"\n",
- " for (w, h) in rectangles:\n",
- " for rect in [(w, h), (h, w)]:\n",
- " grid2 = place_rectangle_at(rect, grid, pos)\n",
- " if grid2: \n",
- " yield rectangles - {(w, h)}, grid2 \n",
- " \n",
"def first_empty_cell(grid):\n",
" \"The uppermost, leftmost empty cell.\"\n",
" for (y, row) in enumerate(grid):\n",
@@ -693,8 +713,10 @@
},
{
"cell_type": "code",
- "execution_count": 22,
- "metadata": {},
+ "execution_count": 21,
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
@@ -706,7 +728,7 @@
" [(2, 5), (2, 5), (3, 4), (3, 4), (3, 4)]]"
]
},
- "execution_count": 22,
+ "execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
@@ -728,13 +750,15 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "It would be nicer to have a graphical display of colored rectangles. I will define the function `show` which displays a grid as colored rectangles, by calling upon `html_table`, which formats any grid into HTML text. (*Note:* Github is conservative in the javascript and even CSS that it allows, so if you don't see colors in the grids below, look at this file on [nbviewer](https://nbviewer.jupyter.org/github/norvig/pytudes/blob/master/ipynb/Golomb-Puzzle.ipynb); same file, but the rendering will definitely show the colors.)"
+ "It would be nicer to have a graphical display of colored rectangles. I will define the function `show` which displays a grid as colored rectangles, by calling upon `html_table`, which formats any grid into HTML text."
]
},
{
"cell_type": "code",
- "execution_count": 23,
- "metadata": {},
+ "execution_count": 22,
+ "metadata": {
+ "collapsed": true
+ },
"outputs": [],
"source": [
"from IPython.display import HTML, display, clear_output\n",
@@ -745,8 +769,7 @@
" display(html_table(grid, colored_cell))\n",
" \n",
"def html_table(grid, cell_function='
{}'.format):\n",
- " \"\"\"Return an HTML , where each cell's contents comes from calling \n",
- " cell_function(grid[y][x])\"\"\"\n",
+ " \"Return an HTML , where each cell's contents comes from calling cell_function(grid[y][x])\"\n",
" return HTML(''\n",
" .format(cat('\\n' + cat(map(cell_function, row)) \n",
" for row in grid)))\n",
@@ -755,16 +778,17 @@
" x, y = sorted(rect)\n",
" return '{}{}'.format(colors[x], x%10, y%10)\n",
"\n",
- "colors = ('lightgrey yellow plum chartreuse cyan coral red olive slateblue lightgrey wheat'\n",
- " .split())\n",
+ "colors = 'lightgrey yellow plum chartreuse cyan coral red olive slateblue lightgrey wheat'.split()\n",
"\n",
"cat = ''.join"
]
},
{
"cell_type": "code",
- "execution_count": 24,
- "metadata": {},
+ "execution_count": 23,
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
@@ -798,26 +822,28 @@
},
{
"cell_type": "code",
- "execution_count": 25,
- "metadata": {},
+ "execution_count": 24,
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
"text/html": [
"\n",
- "37 | 37 | 37 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50\n",
- " | 37 | 37 | 37 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50\n",
- " | 37 | 37 | 37 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50\n",
- " | 37 | 37 | 37 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50\n",
- " | 37 | 37 | 37 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50\n",
- " | 37 | 37 | 37 | 12 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
- " | 37 | 37 | 37 | 12 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
- " | 46 | 46 | 46 | 46 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
- " | 46 | 46 | 46 | 46 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
- " | 46 | 46 | 46 | 46 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
- " | 46 | 46 | 46 | 46 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
- " | 46 | 46 | 46 | 46 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
- " | 46 | 46 | 46 | 46 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | "
+ " | 50 | 50 | 50 | 50 | 50 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
+ " | 50 | 50 | 50 | 50 | 50 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
+ " | 50 | 50 | 50 | 50 | 50 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
+ " | 50 | 50 | 50 | 50 | 50 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
+ " | 50 | 50 | 50 | 50 | 50 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
+ " | 50 | 50 | 50 | 50 | 50 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
+ " | 50 | 50 | 50 | 50 | 50 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
+ " | 50 | 50 | 50 | 50 | 50 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
+ " | 50 | 50 | 50 | 50 | 50 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
+ " | 50 | 50 | 50 | 50 | 50 | 12 | 12 | 46 | 46 | 46 | 46 | 46 | 46\n",
+ " | 37 | 37 | 37 | 37 | 37 | 37 | 37 | 46 | 46 | 46 | 46 | 46 | 46\n",
+ " | 37 | 37 | 37 | 37 | 37 | 37 | 37 | 46 | 46 | 46 | 46 | 46 | 46\n",
+ " | 37 | 37 | 37 | 37 | 37 | 37 | 37 | 46 | 46 | 46 | 46 | 46 | 46 | "
],
"text/plain": [
""
@@ -830,19 +856,19 @@
"data": {
"text/html": [
"\n",
- "79 | 79 | 79 | 79 | 79 | 79 | 79 | 60 | 60 | 60 | 60 | 60 | 60\n",
- " | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 60 | 60 | 60 | 60 | 60 | 60\n",
- " | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 60 | 60 | 60 | 60 | 60 | 60\n",
- " | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 60 | 60 | 60 | 60 | 60 | 60\n",
- " | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 60 | 60 | 60 | 60 | 60 | 60\n",
- " | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 60 | 60 | 60 | 60 | 60 | 60\n",
- " | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 60 | 60 | 60 | 60 | 60 | 60\n",
- " | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 60 | 60 | 60 | 60 | 60 | 60\n",
- " | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 60 | 60 | 60 | 60 | 60 | 60\n",
- " | 45 | 45 | 45 | 45 | 45 | 12 | 12 | 60 | 60 | 60 | 60 | 60 | 60\n",
- " | 45 | 45 | 45 | 45 | 45 | 38 | 38 | 38 | 38 | 38 | 38 | 38 | 38\n",
- " | 45 | 45 | 45 | 45 | 45 | 38 | 38 | 38 | 38 | 38 | 38 | 38 | 38\n",
- " | 45 | 45 | 45 | 45 | 45 | 38 | 38 | 38 | 38 | 38 | 38 | 38 | 38 | "
+ "45 | 45 | 45 | 45 | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 79\n",
+ " | 45 | 45 | 45 | 45 | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 79\n",
+ " | 45 | 45 | 45 | 45 | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 79\n",
+ " | 45 | 45 | 45 | 45 | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 79\n",
+ " | 45 | 45 | 45 | 45 | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 79\n",
+ " | 38 | 38 | 38 | 12 | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 79\n",
+ " | 38 | 38 | 38 | 12 | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 79 | 79\n",
+ " | 38 | 38 | 38 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60\n",
+ " | 38 | 38 | 38 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60\n",
+ " | 38 | 38 | 38 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60\n",
+ " | 38 | 38 | 38 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60\n",
+ " | 38 | 38 | 38 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60\n",
+ " | 38 | 38 | 38 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | 60 | "
],
"text/plain": [
""
@@ -855,17 +881,17 @@
"data": {
"text/html": [
"\n",
- "39 | 39 | 39 | 58 | 58 | 58 | 58 | 58 | 58 | 58 | 58\n",
- " | 39 | 39 | 39 | 58 | 58 | 58 | 58 | 58 | 58 | 58 | 58\n",
- " | 39 | 39 | 39 | 58 | 58 | 58 | 58 | 58 | 58 | 58 | 58\n",
- " | 39 | 39 | 39 | 58 | 58 | 58 | 58 | 58 | 58 | 58 | 58\n",
- " | 39 | 39 | 39 | 58 | 58 | 58 | 58 | 58 | 58 | 58 | 58\n",
- " | 39 | 39 | 39 | 47 | 47 | 47 | 47 | 47 | 47 | 47 | 16\n",
- " | 39 | 39 | 39 | 47 | 47 | 47 | 47 | 47 | 47 | 47 | 16\n",
- " | 39 | 39 | 39 | 47 | 47 | 47 | 47 | 47 | 47 | 47 | 16\n",
- " | 39 | 39 | 39 | 47 | 47 | 47 | 47 | 47 | 47 | 47 | 16\n",
- " | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 16\n",
- " | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 16 | "
+ "20 | 20 | 39 | 39 | 39 | 39 | 39 | 39 | 39 | 39 | 39\n",
+ " | 20 | 20 | 39 | 39 | 39 | 39 | 39 | 39 | 39 | 39 | 39\n",
+ " | 20 | 20 | 39 | 39 | 39 | 39 | 39 | 39 | 39 | 39 | 39\n",
+ " | 20 | 20 | 47 | 47 | 47 | 47 | 58 | 58 | 58 | 58 | 58\n",
+ " | 20 | 20 | 47 | 47 | 47 | 47 | 58 | 58 | 58 | 58 | 58\n",
+ " | 20 | 20 | 47 | 47 | 47 | 47 | 58 | 58 | 58 | 58 | 58\n",
+ " | 20 | 20 | 47 | 47 | 47 | 47 | 58 | 58 | 58 | 58 | 58\n",
+ " | 20 | 20 | 47 | 47 | 47 | 47 | 58 | 58 | 58 | 58 | 58\n",
+ " | 20 | 20 | 47 | 47 | 47 | 47 | 58 | 58 | 58 | 58 | 58\n",
+ " | 20 | 20 | 47 | 47 | 47 | 47 | 58 | 58 | 58 | 58 | 58\n",
+ " | 16 | 16 | 16 | 16 | 16 | 16 | 58 | 58 | 58 | 58 | 58"
],
"text/plain": [
""
@@ -878,17 +904,17 @@
"data": {
"text/html": [
"\n",
- "28 | 28 | 19 | 19 | 19 | 19 | 19 | 19 | 19 | 19 | 19\n",
- " | 28 | 28 | 47 | 47 | 47 | 47 | 50 | 50 | 50 | 50 | 50\n",
- " | 28 | 28 | 47 | 47 | 47 | 47 | 50 | 50 | 50 | 50 | 50\n",
- " | 28 | 28 | 47 | 47 | 47 | 47 | 50 | 50 | 50 | 50 | 50\n",
- " | 28 | 28 | 47 | 47 | 47 | 47 | 50 | 50 | 50 | 50 | 50\n",
- " | 28 | 28 | 47 | 47 | 47 | 47 | 50 | 50 | 50 | 50 | 50\n",
- " | 28 | 28 | 47 | 47 | 47 | 47 | 50 | 50 | 50 | 50 | 50\n",
- " | 28 | 28 | 47 | 47 | 47 | 47 | 50 | 50 | 50 | 50 | 50\n",
- " | 36 | 36 | 36 | 36 | 36 | 36 | 50 | 50 | 50 | 50 | 50\n",
- " | 36 | 36 | 36 | 36 | 36 | 36 | 50 | 50 | 50 | 50 | 50\n",
- " | 36 | 36 | 36 | 36 | 36 | 36 | 50 | 50 | 50 | 50 | 50 | "
+ "50 | 50 | 50 | 50 | 50 | 36 | 36 | 36 | 36 | 36 | 36\n",
+ " | 50 | 50 | 50 | 50 | 50 | 36 | 36 | 36 | 36 | 36 | 36\n",
+ " | 50 | 50 | 50 | 50 | 50 | 36 | 36 | 36 | 36 | 36 | 36\n",
+ " | 50 | 50 | 50 | 50 | 50 | 47 | 47 | 47 | 47 | 28 | 28\n",
+ " | 50 | 50 | 50 | 50 | 50 | 47 | 47 | 47 | 47 | 28 | 28\n",
+ " | 50 | 50 | 50 | 50 | 50 | 47 | 47 | 47 | 47 | 28 | 28\n",
+ " | 50 | 50 | 50 | 50 | 50 | 47 | 47 | 47 | 47 | 28 | 28\n",
+ " | 50 | 50 | 50 | 50 | 50 | 47 | 47 | 47 | 47 | 28 | 28\n",
+ " | 50 | 50 | 50 | 50 | 50 | 47 | 47 | 47 | 47 | 28 | 28\n",
+ " | 50 | 50 | 50 | 50 | 50 | 47 | 47 | 47 | 47 | 28 | 28\n",
+ " | 19 | 19 | 19 | 19 | 19 | 19 | 19 | 19 | 19 | 28 | 28"
],
"text/plain": [
""
@@ -902,7 +928,7 @@
"def pack_square(rectangles):\n",
" \"Pack rectangles into a square of appropriate size, if possible.\"\n",
" A = total_area(rectangles)\n",
- " if A in perfect_squares:\n",
+ " if is_perfect_square(A):\n",
" return pack(rectangles, Square(int(A ** 0.5)))\n",
"\n",
"for s in all_sets:\n",
@@ -916,88 +942,6 @@
"So there are 4 sets of rectangles that work."
]
},
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Animated Colored Rectangles"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "It is gratifying to see the final results, and have the computation be so fast, but I'd like to get a better understanding of the process of finding the results. I can try to visualize the process by *animating* the search that `pack` does: every time we successfully call `place_rectangle_at`, I can show the new grid, and then pause for a short period. I accomplish this by modifying `pack` to take an optional argument, `animation`, which when false gives the same behavior as before, but when it is a number, pauses for that number of seconds in between displaying each step of the packing."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "metadata": {},
- "outputs": [],
- "source": [
- "import time\n",
- "\n",
- "def pack(rectangles, grid, animation=False): \n",
- " \"\"\"Find a way to pack all rectangles onto grid and return the packed grid,\n",
- " or return None if not possible. Pause `animation` seconds between \n",
- " displaying each rectangle placement if `animation` is not false.\"\"\"\n",
- " if animation: \n",
- " clear_output()\n",
- " show(grid)\n",
- " time.sleep(animation) \n",
- " if not rectangles:\n",
- " return grid \n",
- " pos = first_empty_cell(grid)\n",
- " if grid and pos:\n",
- " for (rectangles2, grid2) in rectangle_placements(rectangles, grid, pos): \n",
- " solution = pack(rectangles2, grid2, animation)\n",
- " if solution:\n",
- " return solution"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "If you are running this in a live IPython notebook (not in nbviewer), you can see for yourself by re-running this cell:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "\n",
- "37 | 37 | 37 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50\n",
- " | 37 | 37 | 37 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50\n",
- " | 37 | 37 | 37 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50\n",
- " | 37 | 37 | 37 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50\n",
- " | 37 | 37 | 37 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50 | 50\n",
- " | 37 | 37 | 37 | 12 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
- " | 37 | 37 | 37 | 12 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
- " | 46 | 46 | 46 | 46 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
- " | 46 | 46 | 46 | 46 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
- " | 46 | 46 | 46 | 46 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
- " | 46 | 46 | 46 | 46 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
- " | 46 | 46 | 46 | 46 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89\n",
- " | 46 | 46 | 46 | 46 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | 89 | "
- ],
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "pack({(1, 2), (3, 7), (4, 6), (5, 10), (8, 9)}, Square(13), 1);"
- ]
- },
{
"cell_type": "markdown",
"metadata": {},
@@ -1032,8 +976,10 @@
},
{
"cell_type": "code",
- "execution_count": 28,
- "metadata": {},
+ "execution_count": 29,
+ "metadata": {
+ "collapsed": true
+ },
"outputs": [],
"source": [
"from __future__ import division\n",
@@ -1067,16 +1013,18 @@
},
{
"cell_type": "code",
- "execution_count": 29,
- "metadata": {},
+ "execution_count": 30,
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
"text/plain": [
- "'325 + 764 == 1089'"
+ "'352 + 746 == 1098'"
]
},
- "execution_count": 29,
+ "execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
@@ -1087,15 +1035,17 @@
},
{
"cell_type": "code",
- "execution_count": 30,
- "metadata": {},
+ "execution_count": 31,
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "CPU times: user 30 s, sys: 56.5 ms, total: 30 s\n",
- "Wall time: 30.1 s\n"
+ "CPU times: user 41.4 s, sys: 452 ms, total: 41.9 s\n",
+ "Wall time: 44.2 s\n"
]
},
{
@@ -1104,7 +1054,7 @@
"96"
]
},
- "execution_count": 30,
+ "execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
@@ -1135,8 +1085,10 @@
},
{
"cell_type": "code",
- "execution_count": 31,
- "metadata": {},
+ "execution_count": 32,
+ "metadata": {
+ "collapsed": true
+ },
"outputs": [],
"source": [
"def compile_formula(formula, verbose=False):\n",
@@ -1164,23 +1116,25 @@
},
{
"cell_type": "code",
- "execution_count": 32,
- "metadata": {},
+ "execution_count": 33,
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "lambda M,U,E,O,Y: M and Y and ((100*Y+10*O+U) == (10*M+E)**2)\n"
+ "lambda O,Y,E,U,M: M and Y and ((100*Y+10*O+U) == (10*M+E)**2)\n"
]
},
{
"data": {
"text/plain": [
- "(>, 'MUEOY')"
+ "(>, 'OYEUM')"
]
},
- "execution_count": 32,
+ "execution_count": 33,
"metadata": {},
"output_type": "execute_result"
}
@@ -1191,23 +1145,25 @@
},
{
"cell_type": "code",
- "execution_count": 33,
- "metadata": {},
+ "execution_count": 34,
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "lambda U,A,R,E,L,Y,N,B,M,P: B and N and P and ((100*N+10*U+M) + (100*B+10*E+R) == (1000*P+100*L+10*A+Y))\n"
+ "lambda M,L,P,R,Y,A,E,N,U,B: P and B and N and ((100*N+10*U+M) + (100*B+10*E+R) == (1000*P+100*L+10*A+Y))\n"
]
},
{
"data": {
"text/plain": [
- "(>, 'UARELYNBMP')"
+ "(>, 'MLPRYAENUB')"
]
},
- "execution_count": 33,
+ "execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
@@ -1218,15 +1174,16 @@
},
{
"cell_type": "code",
- "execution_count": 34,
- "metadata": {},
+ "execution_count": 35,
+ "metadata": {
+ "collapsed": true
+ },
"outputs": [],
"source": [
"def faster_solve_all(formula):\n",
" \"\"\"Given a formula like 'ODD + ODD == EVEN', fill in digits to solve it.\n",
" Input formula is a string; output is a digit-filled-in string or None.\n",
- " Capital letters are variables. This version precompiles the formula; \n",
- " only one eval per formula.\"\"\"\n",
+ " Capital letters are variables. This version precompiles the formula; only one eval per formula.\"\"\"\n",
" fn, letters = compile_formula(formula)\n",
" for digits in itertools.permutations((1,2,3,4,5,6,7,8,9,0), len(letters)):\n",
" try:\n",
@@ -1236,7 +1193,7 @@
" pass\n",
" \n",
"def replace_all(text, olds, news):\n",
- " \"Replace each occurence of each old in text with the corresponding new.\"\n",
+ " \"Replace each occurrence of each old in text with the corresponding new.\"\n",
" # E.g. replace_all('A + B', ['A', 'B'], [1, 2]) == '1 + 2'\n",
" for (old, new) in zip(olds, news):\n",
" text = text.replace(str(old), str(new))\n",
@@ -1245,16 +1202,18 @@
},
{
"cell_type": "code",
- "execution_count": 35,
- "metadata": {},
+ "execution_count": 36,
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"data": {
"text/plain": [
- "'325 + 764 = 1089'"
+ "'352 + 746 = 1098'"
]
},
- "execution_count": 35,
+ "execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
@@ -1265,15 +1224,17 @@
},
{
"cell_type": "code",
- "execution_count": 36,
- "metadata": {},
+ "execution_count": 37,
+ "metadata": {
+ "collapsed": false
+ },
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "CPU times: user 1.77 s, sys: 4.05 ms, total: 1.77 s\n",
- "Wall time: 1.77 s\n"
+ "CPU times: user 2.57 s, sys: 31.5 ms, total: 2.6 s\n",
+ "Wall time: 2.7 s\n"
]
},
{
@@ -1282,7 +1243,7 @@
"96"
]
},
- "execution_count": 36,
+ "execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
@@ -1315,7 +1276,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.5.3"
+ "version": "3.6.0"
}
},
"nbformat": 4,
| | |