From cbb8228d4a5b3c09058ea918ae8d8e5b3ae66500 Mon Sep 17 00:00:00 2001 From: Peter Norvig Date: Sat, 13 Apr 2024 23:39:54 -0700 Subject: [PATCH] Add files via upload --- ipynb/Paint.ipynb | 1073 +++++++++++++++++++++++++-------------------- 1 file changed, 607 insertions(+), 466 deletions(-) diff --git a/ipynb/Paint.ipynb b/ipynb/Paint.ipynb index 43a3479..bfae465 100644 --- a/ipynb/Paint.ipynb +++ b/ipynb/Paint.ipynb @@ -5,7 +5,7 @@ "id": "d3bc52a7-6c7d-49b5-b6d4-b43a74e0ef34", "metadata": {}, "source": [ - "
Peter Norvig, April 2024
\n", + "
Peter Norvig
April 2024
\n", "\n", "# Counting Cluster Sizes\n", "\n", @@ -15,70 +15,114 @@ "\n", "![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F079ab505-66c7-427b-ad6a-a2cf6a1794a6_1600x384.png)\n", "\n", - "Altogether there are 20 squares and 9 clusters, so the average cluster size is about 2.22 squares. Under the assumption that every configuration of red and blue squares is equally likely, Zach poses two questions (and I'll add two more):\n", + "Altogether there are 20 squares and 9 clusters, so the average cluster size is 20/9 ≈ 2.22 squares. Under the assumption that every square is equally likely to be red or blue, Zach poses two questions (and I'll add three more):\n", "\n", "- What is the average cluster size for a grid consisting of a single infinitely long row?\n", "- What is the average cluster size for a grid consisting of two infinitely long rows?\n", "- What is the average cluster size for a grid of any given size *w* × *h*?\n", "- What if there are three or more colors rather than just two?\n", + "- If you pick a random square, what is the average size of its cluster?\n", "\n", "# Code to Make Grids and to Count Clusters\n", "\n", - "My strategy will be to try consider every possible grid of a given size, compute the average cluster size for each grid, and then average across all of them. I'll define two data types and five functions:\n", - "- `Square`: a pair of `(x, y)` coordinates, e.g. `(2, 1)`.\n", - "- `Grid`: data type for a grid; a dict of `{square: contents}`, where `contents` initially starts as a color (e.g. `'R'` or `'B'` for red or blue).\n", - "- `grids(width, height)`: returns a list of all possible grids of the given size. Each grid has a different combination of colors. With *c* colors, there are *c*(*w* × *h*) possible grids of size *w* × *h*.\n", - "- `one_grid(width, height, colorseq)`: returns one grid of given size, filling in squares with entries from colorseq.\n", - "- `mean_cluster_size(grid)`: returns the average size of the clusters in a grid.\n", - "- `cluster(grid)`: Mutates the grid so that each square's contents is changed from a color (a string) to a cluster number, where 1 is the number of the first cluster, 2 of the next cluster, and so on. Uses a [flood fill](https://en.wikipedia.org/wiki/Flood_fill) algorithm.\n", - "- `neighbors(square)`: the four squares surrounding the given square.\n" + "I can see three approaches to answering these questions:\n", + "1) Enumerate all possible grids up to a certain size, compute the average cluster size for each grid, and average the averages. This gives an exact answer for grids of a specific size, but it can't say anything about infinite size grids. In fact it starts getting slow for grids with more than about 20 squares, because there are 2*n* grids with *n* squares. \n", + "2) Randomly select some grids and average cluster sizes over them. This can handle grids with thousands of squares, but the averages will be only estimates.\n", + "3) Come up with a mathematical proof that proves the answer for grids of any width *w*.\n", + "\n", + "\n", + "I can easily write code to implement the first two approaches. I'll start with some imports and the definition of three data types:" ] }, { "cell_type": "code", "execution_count": 1, - "id": "d3991ae8-4c2c-4e4f-8884-95fd74aec5be", + "id": "4ddf868c-8902-49f3-8bdd-79d9377d1a11", "metadata": {}, "outputs": [], "source": [ "import itertools \n", + "import random\n", "from statistics import mean, stdev\n", "from typing import *\n", "\n", - "Square = Tuple[int, int]\n", - "Grid = Dict[Square, Union[str, int]] # e.g. {(1, 1): 1, (2, 1): 'B'}\n", - "\n", - "def grids(width: int, height: int, colors='RB') -> List[Grid]:\n", + "Square = Tuple[int, int] # A square is a pair of `(x, y)` coordinates, e.g. `(2, 1)`.\n", + "Grid = Dict # A dict of `{square: contents}`e.g. {(1, 1): 1, (2, 1): 'B'}\n", + "Color = str # A color is represented by a one-character string, e.g. 'R' for red and 'B' for blue\n", + "COLORS = ('R', 'B')\n" + ] + }, + { + "cell_type": "markdown", + "id": "9a0980a5-2a66-4533-b16c-ad3ab810251a", + "metadata": {}, + "source": [ + "Now I'll define the function `all_grids` to make a list of all possible grids of a given size (all possible ways to color each square), and `random_grids` to sample `N` different grids of a given size. The helper function `one_grid` makes a single grid from a sequence of colors." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "3fa0280d-3e62-4645-8096-a9156b170ca1", + "metadata": {}, + "outputs": [], + "source": [ + "def all_grids(width: int, height: int, colors=COLORS) -> List[Grid]:\n", " \"\"\"All possible grids with given width, height, and color set.\"\"\"\n", " return [one_grid(width, height, colorseq) \n", " for colorseq in itertools.product(colors, repeat=(width * height))]\n", "\n", - "def one_grid(width: int, height: int, colorseq: Sequence[str]) -> Grid: \n", + "def random_grids(width: int, height: int, N: int, colors=COLORS) -> List[Grid]:\n", + " \"\"\"N grids of size width × height, filled with random colors.\"\"\"\n", + " return [random_grid(width, height, colors) for _ in range(N)]\n", + "\n", + "def random_grid(width: int, height: int, colors=COLORS) -> Grid:\n", + " \"\"\"A single random grid.\"\"\"\n", + " return one_grid(width, height, [random.choice(colors) for _ in range(width * height)])\n", + "\n", + "def one_grid(width: int, height: int, colorseq: Sequence[Color]) -> Grid: \n", " \"\"\"A grid of given size made from the sequence of colors.\"\"\"\n", " squares = [(x, y) for y in range(height) for x in range(width)]\n", - " return dict(zip(squares, colorseq))\n", - "\n", - "def mean_cluster_size(grid: Grid) -> float: \n", - " \"\"\"Mean size of clusters in a grid.\"\"\"\n", - " return len(grid) / max(cluster(grid).values())\n", - " \n", - "def cluster(grid: Grid) -> Grid:\n", - " \"\"\"Do a flood fill, replacing one cluster of adjacent characters with an integer,\n", - " then incrementing the integer for the next cluster and continuing.\"\"\"\n", + " return dict(zip(squares, colorseq))" + ] + }, + { + "cell_type": "markdown", + "id": "fcd29cb3-c7cf-4036-8659-6cd87c116c16", + "metadata": {}, + "source": [ + "Finally, the function `cluster` mutates the grid so that each square's contents is changed from a color to a cluster number, where 1 is the number of the first cluster, 2 of the next cluster, and so on. It uses a [flood fill](https://en.wikipedia.org/wiki/Flood_fill) algorithm." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "998ba6a6-8355-4474-9f78-92918f18656a", + "metadata": {}, + "outputs": [], + "source": [ + "def cluster(grid: Grid[Square, Color]) -> Grid[Square, int]:\n", + " \"\"\"Mutate grid, replacing colors with cluster numbers.\n", + " Do a flood fill, replacing one cluster of adjacent colors with an integer cluster number,\n", + " then incrementing the cluster number and continuing on to find the next cluster.\"\"\"\n", " cluster_number = 0 \n", " for square in grid:\n", " c = grid[square]\n", - " if isinstance(c, str):\n", + " if isinstance(c, Color):\n", " cluster_number += 1\n", - " # Assign cluster number to square and all its neighbors with the same contents\n", - " Q = [square] # queue of squares in cluster `i`\n", + " # Assign `cluster_number` to `square` and all its neighbors of the same color\n", + " Q = [square] # queue of squares in cluster `cluster_number`\n", " while Q: \n", " sq = Q.pop()\n", - " if grid.get(sq) == c: \n", + " if sq in grid and grid[sq] == c: \n", " grid[sq] = cluster_number\n", " Q.extend(neighbors(sq))\n", " return grid\n", "\n", + "def mean_cluster_size(grids: Collection[Grid]) -> float: \n", + " \"\"\"Mean size of the clusters in a collection of grids.\"\"\"\n", + " return mean(len(grid) / max(cluster(grid).values()) for grid in grids)\n", + " \n", "def neighbors(square: Square) -> List[Square]:\n", " \"\"\"The four neighbors of a square.\"\"\"\n", " (x, y) = square\n", @@ -92,21 +136,25 @@ "source": [ "# Answering the Questions\n", "\n", - "Here's one more function to help solve the questions:" + "Here's a function to help answer the questions:" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "id": "816f854a-7c99-4022-b539-98f87bc20369", "metadata": {}, "outputs": [], "source": [ - "def do(n, h=1, colors='RB') -> dict: \n", - " \"\"\"A dict of {(w, h): mean-cluster-size} for grids with width w from 1 to n, and with height h.\"\"\"\n", - " for w in range(1, n + 1):\n", - " μ = mean(map(mean_cluster_size, grids(w, h, colors)))\n", - " print(f'{w:2} × {h} grids: {μ:6.4f} mean cluster size')" + "def do(W, h, N=30_000, colors='RB') -> None: \n", + " \"\"\"For each width w from 1 to W, print the mean cluster size of w x h grids.\n", + " If `N` is an integer, randomly sample `N` grids.\n", + " If `N` is `all`, exhaustively enumerate all possible grids.\"\"\"\n", + " which = \"all possible\" if N is all else f\"{N:,d} randomly sampled\"\n", + " print(f' Average cluster size over {which} grids of width 1–{W} and height {h}:')\n", + " for w in range(1, W + 1):\n", + " grids = all_grids(w, h, colors) if N is all else random_grids(w, h, N, colors)\n", + " print(f'{w:2} × {h} grids: {mean_cluster_size(grids):6.4f}')" ] }, { @@ -116,12 +164,12 @@ "source": [ "# One-Row Grids\n", "\n", - "I can't make an infinitely long row (a grid with *n* squares has 2*n* possible color arrangments, it will be slow to investigate grids with more than about 20 squares (a million arrangements)). However, I can look at successively wider rows and see if the average cluster size seems to be converging:" + "Let's see what we get. First an exact calculation over all possible grids up to size 14 × 1:" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "id": "e004289f-0c81-46ab-8900-9312a0aa6f7c", "metadata": {}, "outputs": [ @@ -129,46 +177,97 @@ "name": "stdout", "output_type": "stream", "text": [ - " 1 × 1 grids: 1.0000 mean cluster size\n", - " 2 × 1 grids: 1.5000 mean cluster size\n", - " 3 × 1 grids: 1.7500 mean cluster size\n", - " 4 × 1 grids: 1.8750 mean cluster size\n", - " 5 × 1 grids: 1.9375 mean cluster size\n", - " 6 × 1 grids: 1.9688 mean cluster size\n", - " 7 × 1 grids: 1.9844 mean cluster size\n", - " 8 × 1 grids: 1.9922 mean cluster size\n", - " 9 × 1 grids: 1.9961 mean cluster size\n", - "10 × 1 grids: 1.9980 mean cluster size\n", - "11 × 1 grids: 1.9990 mean cluster size\n", - "12 × 1 grids: 1.9995 mean cluster size\n", - "13 × 1 grids: 1.9998 mean cluster size\n", - "14 × 1 grids: 1.9999 mean cluster size\n" + " Average cluster size over all possible grids of width 1–14 and height 1:\n", + " 1 × 1 grids: 1.0000\n", + " 2 × 1 grids: 1.5000\n", + " 3 × 1 grids: 1.7500\n", + " 4 × 1 grids: 1.8750\n", + " 5 × 1 grids: 1.9375\n", + " 6 × 1 grids: 1.9688\n", + " 7 × 1 grids: 1.9844\n", + " 8 × 1 grids: 1.9922\n", + " 9 × 1 grids: 1.9961\n", + "10 × 1 grids: 1.9980\n", + "11 × 1 grids: 1.9990\n", + "12 × 1 grids: 1.9995\n", + "13 × 1 grids: 1.9998\n", + "14 × 1 grids: 1.9999\n" ] } ], "source": [ - "do(14)" + "do(14, 1, all)" ] }, { "cell_type": "markdown", - "id": "13dd529d-519e-487e-9430-e782c8b23e0f", + "id": "dda7a929-b903-47e2-891a-d4544dfb86ec", "metadata": {}, "source": [ - "For rows that are at least 11 squares wide we're getting very close (within 0.001) to an average cluster size of 2.\n", - "\n", - "Now that I see the result, I can come up with a justification: Every cluster starts with one square. The next square in the row will be the same color half the time. Continuing, we would get three of the same color in a row a quarter of the time, four in a row an eigth of the time, and so on. So we get:\n", - "\n", - "mean cluster size   =   Σi∈{0,1,...∞} (1/2)n   =   2\n", - "\n", - "# Two-Row Grids\n", - "\n", - "Now consider grids that are two rows tall:" + "Let's compare that to the random sampling approach, which can handle wider grids:" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, + "id": "306d2161-07aa-4b8e-9c54-8462e0a0d6ac", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Average cluster size over 30,000 randomly sampled grids of width 1–20 and height 1:\n", + " 1 × 1 grids: 1.0000\n", + " 2 × 1 grids: 1.5025\n", + " 3 × 1 grids: 1.7502\n", + " 4 × 1 grids: 1.8762\n", + " 5 × 1 grids: 1.9374\n", + " 6 × 1 grids: 1.9726\n", + " 7 × 1 grids: 1.9912\n", + " 8 × 1 grids: 1.9987\n", + " 9 × 1 grids: 1.9960\n", + "10 × 1 grids: 2.0011\n", + "11 × 1 grids: 1.9928\n", + "12 × 1 grids: 1.9976\n", + "13 × 1 grids: 2.0020\n", + "14 × 1 grids: 1.9991\n", + "15 × 1 grids: 2.0034\n", + "16 × 1 grids: 1.9952\n", + "17 × 1 grids: 1.9995\n", + "18 × 1 grids: 1.9984\n", + "19 × 1 grids: 1.9925\n", + "20 × 1 grids: 2.0022\n" + ] + } + ], + "source": [ + "do(20, 1, 30_000)" + ] + }, + { + "cell_type": "markdown", + "id": "138d8441-b233-457f-97d6-81946e8a07fa", + "metadata": {}, + "source": [ + "The answer seems to be converging on 2. The random sampling approach has pretty good agreement with the exhaustive approach, always agreeing to within 0.01, and sometimes to within 0.001.\n", + "\n", + "Now that I see these results, I can describe a mathematical justification of why the limit is 2. In fact, I have two justifications:\n", + "\n", + "**First**: what is the average number of new clusters introduced per column? Half the time column *k* will be different from the previous column *k* - 1, and thus half the time a column will introduce a new cluster. So with *W* columns there will be *W*/2 clusters, and the average cluster size will be *W*/(*W*/2) = 2.\n", + "\n", + "**Second**: what is the average length of a cluster that starts in a given column? Every cluster starts with one first square. Half the time, the next square will be the same color, giving two in a row. Continuing, we would get three in a row a quarter of the time, four in a row an eigth of the time, and so on. So using the [formula for the sum of a geometric series](https://en.wikipedia.org/wiki/Geometric_series) we get:\n", + "\n", + "mean cluster size   =   Σi∈{0,1,...∞} (1/2)n   =   1 / (1 - (1/2))   =  2\n", + "\n", + "# Two-Row Grids\n", + "\n", + "Now consider grids that are two rows tall, using both approaches:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, "id": "e2f0c3ca-acac-4dd8-9970-c846e3389ad5", "metadata": {}, "outputs": [ @@ -176,39 +275,97 @@ "name": "stdout", "output_type": "stream", "text": [ - " 1 × 2 grids: 1.5000 mean cluster size\n", - " 2 × 2 grids: 2.1250 mean cluster size\n", - " 3 × 2 grids: 2.4688 mean cluster size\n", - " 4 × 2 grids: 2.6682 mean cluster size\n", - " 5 × 2 grids: 2.7920 mean cluster size\n", - " 6 × 2 grids: 2.8737 mean cluster size\n", - " 7 × 2 grids: 2.9305 mean cluster size\n", - " 8 × 2 grids: 2.9717 mean cluster size\n", - " 9 × 2 grids: 3.0027 mean cluster size\n" + " Average cluster size over all possible grids of width 1–9 and height 2:\n", + " 1 × 2 grids: 1.5000\n", + " 2 × 2 grids: 2.1250\n", + " 3 × 2 grids: 2.4688\n", + " 4 × 2 grids: 2.6682\n", + " 5 × 2 grids: 2.7920\n", + " 6 × 2 grids: 2.8737\n", + " 7 × 2 grids: 2.9305\n", + " 8 × 2 grids: 2.9717\n", + " 9 × 2 grids: 3.0027\n" ] } ], "source": [ - "do(9, 2)" + "do(9, 2, all)" ] }, { "cell_type": "markdown", - "id": "8e1ac7a1-f947-4b76-8c5d-d1b26974d018", + "id": "de68a7e0-ced6-4c23-a806-3abacfc0927c", "metadata": {}, "source": [ - "It looks like the answer is converging on 3, but it is not as clear as with the first question.\n", - "\n", - "Here's a justification: consider a cluster that extends a certain length all along the top row, with possibly some additional squares below it in the bootom row. You can call this a π-shape, because there is a horizontal bar at the top, and ero or more vertical bars extending down. By the argument above, the average length of the bar is 2. Now for each column in the bar there is a 50% chance that the square below will be the same color and thus join the cluster. So the average cluster size in total is 2 + 2 × 1/2 = 3. By a similar argument, clusters that extend along the bottom row with possibly some squares above also have an average size of 3. But what about an Z-shape or S-shape cluster, which starts along the top (or bottom) row, then switches to the bottom (or top)? The trick is that you can take any Z- or S-shape and turn it into a π-shape by just swapping top and bottom squares when the cluster is on the bottom. This swap doesn't change any probabilities, so a Z-, S-, and π-shaped clusters all have a mean cluster size of 3.\n", - "\n", - "# Arbitrary *w* × *h* Grids\n", - "\n", - "Let's consider grids of size *n* × 3 and *n* × 4:" + "With widths up to 9, the mean is getting close to 3, but I don't think it is converging to exactly 3. " ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 8, + "id": "c7b56724-5bb7-4395-87b5-0268abb30ff1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Average cluster size over 30,000 randomly sampled grids of width 1–25 and height 2:\n", + " 1 × 2 grids: 1.5005\n", + " 2 × 2 grids: 2.1190\n", + " 3 × 2 grids: 2.4684\n", + " 4 × 2 grids: 2.6611\n", + " 5 × 2 grids: 2.7899\n", + " 6 × 2 grids: 2.8772\n", + " 7 × 2 grids: 2.9399\n", + " 8 × 2 grids: 2.9784\n", + " 9 × 2 grids: 3.0066\n", + "10 × 2 grids: 3.0256\n", + "11 × 2 grids: 3.0587\n", + "12 × 2 grids: 3.0636\n", + "13 × 2 grids: 3.0728\n", + "14 × 2 grids: 3.0804\n", + "15 × 2 grids: 3.0969\n", + "16 × 2 grids: 3.1046\n", + "17 × 2 grids: 3.1094\n", + "18 × 2 grids: 3.1201\n", + "19 × 2 grids: 3.1187\n", + "20 × 2 grids: 3.1297\n", + "21 × 2 grids: 3.1326\n", + "22 × 2 grids: 3.1296\n", + "23 × 2 grids: 3.1403\n", + "24 × 2 grids: 3.1367\n", + "25 × 2 grids: 3.1383\n" + ] + } + ], + "source": [ + "do(25, 2)" + ] + }, + { + "cell_type": "markdown", + "id": "90a6dc1c-c570-4bfd-8fe9-210e80c6a455", + "metadata": {}, + "source": [ + "Now it is clear that the mean is well above 3. At this point I tried to come up with a mathematical analysis but made a mistake. Fortunately, Zach [provides the answer](https://thefiddler.substack.com/p/can-you-eclipse-via-ellipse) by breaking down the possibilities into cases. With the single-row grid, we only needed two cases: column *k* introduced a new cluster 1/2 the time. But with two rows there are 16 cases: each of the two squares in column *k* and the two in column *k* - 1 can be either red or blue. Overall, as aach's diagram below shows, there are 10 new clusters introduced in 16 cases, so the number of new clusters per column is 10/16, and the average cluster size over *W* columns is 2*W* / ((10/16)·*W*) = 16/5 = 3.2.\n", + "\n", + "![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb06d848c-37f6-4e9b-be38-a88bfe3e7316_1342x1154.png)" + ] + }, + { + "cell_type": "markdown", + "id": "9c0b08ba-7e6b-42ff-884a-5dd010bcfa3a", + "metadata": {}, + "source": [ + "# Three-Row Grids\n", + "\n", + "Let's next consider grids of size *n* × 3 :" + ] + }, + { + "cell_type": "code", + "execution_count": 9, "id": "b29ddec0-5fd2-4f8e-96af-f4b380c6eb59", "metadata": {}, "outputs": [ @@ -216,39 +373,71 @@ "name": "stdout", "output_type": "stream", "text": [ - " 1 × 3 grids: 1.7500 mean cluster size\n", - " 2 × 3 grids: 2.4688 mean cluster size\n", - " 3 × 3 grids: 2.8986 mean cluster size\n", - " 4 × 3 grids: 3.1591 mean cluster size\n", - " 5 × 3 grids: 3.3266 mean cluster size\n", - " 6 × 3 grids: 3.4408 mean cluster size\n" + " Average cluster size over all possible grids of width 1–6 and height 3:\n", + " 1 × 3 grids: 1.7500\n", + " 2 × 3 grids: 2.4688\n", + " 3 × 3 grids: 2.8986\n", + " 4 × 3 grids: 3.1591\n", + " 5 × 3 grids: 3.3266\n", + " 6 × 3 grids: 3.4408\n" ] } ], "source": [ - "do(6, 3)" + "do(6, 3, all)" ] }, { "cell_type": "code", - "execution_count": 6, - "id": "cb493f9f-734e-4767-b245-24cdf906a06a", + "execution_count": 10, + "id": "7e231093-9e10-446d-8b32-25952b286e1e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - " 1 × 4 grids: 1.8750 mean cluster size\n", - " 2 × 4 grids: 2.6682 mean cluster size\n", - " 3 × 4 grids: 3.1591 mean cluster size\n", - " 4 × 4 grids: 3.4616 mean cluster size\n", - " 5 × 4 grids: 3.6608 mean cluster size\n" + " Average cluster size over 30,000 randomly sampled grids of width 1–25 and height 3:\n", + " 1 × 3 grids: 1.7507\n", + " 2 × 3 grids: 2.4668\n", + " 3 × 3 grids: 2.8953\n", + " 4 × 3 grids: 3.1560\n", + " 5 × 3 grids: 3.3236\n", + " 6 × 3 grids: 3.4452\n", + " 7 × 3 grids: 3.5230\n", + " 8 × 3 grids: 3.5881\n", + " 9 × 3 grids: 3.6304\n", + "10 × 3 grids: 3.6695\n", + "11 × 3 grids: 3.6897\n", + "12 × 3 grids: 3.7266\n", + "13 × 3 grids: 3.7455\n", + "14 × 3 grids: 3.7636\n", + "15 × 3 grids: 3.7772\n", + "16 × 3 grids: 3.7995\n", + "17 × 3 grids: 3.8018\n", + "18 × 3 grids: 3.8090\n", + "19 × 3 grids: 3.8220\n", + "20 × 3 grids: 3.8290\n", + "21 × 3 grids: 3.8318\n", + "22 × 3 grids: 3.8514\n", + "23 × 3 grids: 3.8500\n", + "24 × 3 grids: 3.8580\n", + "25 × 3 grids: 3.8639\n" ] } ], "source": [ - "do(5, 4)" + "do(25, 3)" + ] + }, + { + "cell_type": "markdown", + "id": "bf56480e-4bb5-40f9-b1a9-c380eb698046", + "metadata": {}, + "source": [ + "The mathematical analysis here is much trickier. With two rows, we can tell how many new clusters are introduced in column *k* just by looking at column *k* - 1. But with three or more rows, that's no longer true. Suppose the top and bottom squares in a column are red. Is that one red cluster or two? We have to look a potentially *unbounded* number of columns away to see if they are connected (by a column that has red in all three squares). So no local analysis can determine the number of new clusters per column; we'll need some new method of analysis. \n", + "\n", + "Interestingly, this is the same limitation that Minsky and Papert noticed in the book [Perceptrons](https://direct.mit.edu/books/monograph/3132/PerceptronsAn-Introduction-to-Computational); their analysis caused many researchers to draw the conclusion that neural networks were no good; they should have instead drawn the conclusion that single-layer neural networks are no good but mult-layer ones avoid the limitations." ] }, { @@ -258,12 +447,12 @@ "source": [ "# Adding Colors\n", "\n", - "What if we add a third color? That should make the average cluster size smaller (since there is a 2/3 rather than 1/2 chance that the next square will be a different color and start a new cluster). I'll start with a single row:" + "What if we add a third color? That should make the average cluster size smaller (since there are more chances for neighboring squares to be different colors). I'll start with a single row:" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 11, "id": "dc4dc1f5-f5f4-4d69-8bdf-c00d21d978cd", "metadata": {}, "outputs": [ @@ -271,21 +460,23 @@ "name": "stdout", "output_type": "stream", "text": [ - " 1 × 1 grids: 1.0000 mean cluster size\n", - " 2 × 1 grids: 1.3333 mean cluster size\n", - " 3 × 1 grids: 1.4444 mean cluster size\n", - " 4 × 1 grids: 1.4815 mean cluster size\n", - " 5 × 1 grids: 1.4938 mean cluster size\n", - " 6 × 1 grids: 1.4979 mean cluster size\n", - " 7 × 1 grids: 1.4993 mean cluster size\n", - " 8 × 1 grids: 1.4998 mean cluster size\n", - " 9 × 1 grids: 1.4999 mean cluster size\n", - "10 × 1 grids: 1.5000 mean cluster size\n" + " Average cluster size over all possible grids of width 1–11 and height 1:\n", + " 1 × 1 grids: 1.0000\n", + " 2 × 1 grids: 1.3333\n", + " 3 × 1 grids: 1.4444\n", + " 4 × 1 grids: 1.4815\n", + " 5 × 1 grids: 1.4938\n", + " 6 × 1 grids: 1.4979\n", + " 7 × 1 grids: 1.4993\n", + " 8 × 1 grids: 1.4998\n", + " 9 × 1 grids: 1.4999\n", + "10 × 1 grids: 1.5000\n", + "11 × 1 grids: 1.5000\n" ] } ], "source": [ - "do(10, 1, 'RGB')" + "do(11, 1, all, colors='RGB')" ] }, { @@ -293,16 +484,14 @@ "id": "7afd16c2-99bd-410b-b5a8-d20862b7b1f5", "metadata": {}, "source": [ - "This is straightforward: the mean cluster size converges to 3/2. We can update the equation to deal with a single row with *c* equiprobable colors:\n", + "This is straightforward: the mean cluster size converges to 3/2; the analysis says that this is true because each column starts a new cluster 2/3 of the time, on average.\n", "\n", - "mean cluster size   =   Σi∈{0,1,...∞} (1/*c*)n   =   *c* / (*c* - 1)\n", - "\n", - "Now for a two-row grid with three colors:" + "Now for a two-row grid with three colors. I'll use random sampling:" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 12, "id": "37f4a825-d75c-4275-b2c4-6dd3de080b3f", "metadata": {}, "outputs": [ @@ -310,17 +499,37 @@ "name": "stdout", "output_type": "stream", "text": [ - " 1 × 2 grids: 1.3333 mean cluster size\n", - " 2 × 2 grids: 1.6543 mean cluster size\n", - " 3 × 2 grids: 1.7737 mean cluster size\n", - " 4 × 2 grids: 1.8261 mean cluster size\n", - " 5 × 2 grids: 1.8535 mean cluster size\n", - " 6 × 2 grids: 1.8698 mean cluster size\n" + " Average cluster size over 30,000 randomly sampled grids of width 1–25 and height 2:\n", + " 1 × 2 grids: 1.3320\n", + " 2 × 2 grids: 1.6609\n", + " 3 × 2 grids: 1.7763\n", + " 4 × 2 grids: 1.8300\n", + " 5 × 2 grids: 1.8569\n", + " 6 × 2 grids: 1.8685\n", + " 7 × 2 grids: 1.8816\n", + " 8 × 2 grids: 1.8874\n", + " 9 × 2 grids: 1.8936\n", + "10 × 2 grids: 1.9009\n", + "11 × 2 grids: 1.9026\n", + "12 × 2 grids: 1.9045\n", + "13 × 2 grids: 1.9031\n", + "14 × 2 grids: 1.9056\n", + "15 × 2 grids: 1.9151\n", + "16 × 2 grids: 1.9119\n", + "17 × 2 grids: 1.9110\n", + "18 × 2 grids: 1.9121\n", + "19 × 2 grids: 1.9106\n", + "20 × 2 grids: 1.9144\n", + "21 × 2 grids: 1.9152\n", + "22 × 2 grids: 1.9139\n", + "23 × 2 grids: 1.9156\n", + "24 × 2 grids: 1.9165\n", + "25 × 2 grids: 1.9156\n" ] } ], "source": [ - "do(6, 2, 'RGB')" + "do(25, 2, colors='RGB')" ] }, { @@ -328,7 +537,7 @@ "id": "3c18f723-447f-4cd6-ba27-59db37e87659", "metadata": {}, "source": [ - "By the same reasoning as we had last time, this should converge to 3/2 + 3/2 × 1/3 = 2. (But it would take a lot of computation time to consider the larger grids that would be needed to get closer to 2.)" + "This seems to converge to about 1.92. To analye this for the two-color case we had to look at 24 = 16 cases; with three colors we have 34 = 81 cases; I haven't gone through them all to do the calculation. (But you can.)" ] }, { @@ -336,197 +545,72 @@ "id": "6a36b07b-8e5c-4048-8046-7619b34f6b26", "metadata": {}, "source": [ - "# Random Sampling for Larger Grids\n", + "# Larger Grids\n", "\n", - "I can't analyze an infinite two-dimensional grid.\n", - "\n", - "And I can't even enumerate all the, say, 100 × 100 grids, because there are 210,000 of them.\n", - "\n", - "But I can **randomly sample** a bunch of 100 × 100 grids, and that should give me a good estimate of the full distribution of grids.\n", - "\n", - "First, a function to make a random grid of a given size:" + "What about a grid that is unbounded in all directions? I can't fit that into a finite computer, but I can easily examine random grids of size 100 × 100 or more:" ] }, { "cell_type": "code", - "execution_count": 9, - "id": "51cc5dca-f3b4-4a2b-96af-479683216d4b", - "metadata": {}, - "outputs": [], - "source": [ - "import random\n", - "\n", - "def random_grid(width: int, height: int, colors='RB') -> Grid:\n", - " \"\"\"A width × height grid filled with random colors.\"\"\"\n", - " return one_grid(width, height, [random.choice(colors) for _ in range(width * height)])" - ] - }, - { - "cell_type": "markdown", - "id": "50ac4938-fd46-4e27-89d0-82bca289f37d", - "metadata": {}, - "source": [ - "Here's the mean cluster size of one random grid:" - ] - }, - { - "cell_type": "code", - "execution_count": 10, + "execution_count": 13, "id": "6abd1ae5-852f-459c-ad6e-1e91d91566b0", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "7.037297677691766" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "mean_cluster_size(random_grid(100, 100))" - ] - }, - { - "cell_type": "markdown", - "id": "197a9c3e-58e4-4a4d-a37f-c793c98f36e8", - "metadata": {}, - "source": [ - "But that's just one grid. Better to sample *N* different grids (say, *N* = 200) and report on the mean cluster sizes for them:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "39fd3321-907d-45eb-b06b-70d590427a09", - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "\n", - "def report(numbers, bins=30) -> dict:\n", - " \"\"\"Plot a histogram and return statistics on these numbers.\"\"\"\n", - " plt.hist(numbers, bins=bins, rwidth=0.8, align='left')\n", - " return dict(mean=mean(numbers), max=max(numbers), min=min(numbers), \n", - " stdev=stdev(numbers), N=len(numbers))" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "083dc3ea-a395-4eae-b751-347e4384a373", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'mean': 7.239472697041692,\n", - " 'max': 7.9239302694136295,\n", - " 'min': 6.6711140760507,\n", - " 'stdev': 0.19030622926154506,\n", - " 'N': 200}" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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unVLDz74YN7BMyp2ja688sp08803p+effz69+eabacqUKalbt27p5Zdfzjt4JsWuzzbr169PBx54YBo7dmwaOnRollnbSkvWqDO9Txe7Pp3pPXpn2iw+rr322nTssccW9Zi//e1vqXfv3mnt2rUlmmr30pI1+uUvf5kOPPDAJvtuu+221L9//9Ycbbd05ZVXpoMOOiht3bp1h/dfc8016ZBDDmmy7+KLL07HHHNMjvF2C7taox059NBD0w033FDCqXYfzV2fs88+O/3sZz9L119/fYePjy/b1Rp1tvfpL9vV+nTm9+gvarOPXR5++OEYNmxYfP/734+Kioo48sgj46677mrWY2bMmBHf+MY34pvf/GZcffXVsXnz5kxT59WSNRo5cmS899578cgjj0RKKT788MN48MEHY/z48ZmmbhtbtmyJe+65Jy644IIdXsAwIuKZZ56JsWPHNtl30kknxYsvvhj//e9/c4zZppqzRl+2devW2LhxY/Tp06fE07W95q7P7Nmz4+23347rr78+43S7h+asUWd7n/6i5qxPZ32P3k5bVU+hUEiFQiFNmTIlvfzyy+mOO+5Ie+65Z5ozZ85XPuakk05KhUIhjR8/Pj333HNp/vz5qbq6Op1//vkZJ8+nJWuUUkp/+MMf0t57753Ky8tTRKRTTz01bdmyJdPUbeOBBx5IXbt2Te+///5XHjN48OB04403Ntn31FNPpYhIH3zwQalHbHPNWaMvmzFjRurTp0/68MMPSzjZ7qE56/Pmm2+mioqK9MYbb6SUUqc789GcNeps79Nf1Nw/Y53xPfrL2iw+unXrlkaMGNFk3+WXX77TU+BjxoxJe+65Z1q/fn3jvj/+8Y+prKwsffrppyWbta20ZI2WLVuWqqqq0owZM9I///nP9Oijj6bDDz88XXDBBaUet02NHTs2ffe7393pMYMHD07Tpk1rsu/JJ59MEZFWrVpVyvF2C81Zoy+aO3du6tGjR6qtrS3hVLuPXa3PZ599loYNG5Zuv/32xn2dLT6a83uos71Pf1Fz1qezvkd/WZvFxwEHHJAuvPDCJvtmzZqV+vXr95WP+fGPf5wOOuigJvtee+21FBHpzTffLMmcbakla3Tuueems846q8m+JUuWdOj/u1+5cmXq0qVLeuihh3Z63HHHHZeuuOKKJvvmzZuXysvLO/z/dTR3jba5//77U/fu3dNf//rXEk+2e2jO+qxbty5FROratWvjVlZW1rjv73//e8aJ82vu76HO9j69TXPXpzO+R+9Im33n49vf/na88cYbTfa9+eabUV1dvdPHfPDBB7Fp06Ymj+nSpUv079+/ZLO2lZas0aeffhpdujT919q1a9eIiEgd9DI+s2fPjoqKil1+ZjpixIiora1tsu+xxx6LYcOGRbdu3Uo5Yptr7hpFRNx3330xceLEmDt3bqf5HLo569OrV6949dVXY+nSpY3bJZdcEgcffHAsXbo0hg8fnnHi/Jr7e6izvU9v09z16Yzv0TvUVtXz/PPPp/Ly8nTjjTem5cuXp3vvvTf16NEj3XPPPY3HXHfddem8885rvL1x48bUv3//dNZZZ6Vly5alRYsWpcGDB6eLLrqoLX4JJdeSNZo9e3YqLy9Ps2bNSm+//XZ68skn07Bhw9K3vvWttvgllNznn3+eDjjggHTttddud9+X12bbj9r+5Cc/Sa+99lr67W9/2+F/1Dal4tZo7ty5qby8PP3mN79p8uODXzyF3tEUsz5f1lk+dilmjTrb+3RKxa1PZ3uP/iptFh8ppfSXv/wlDRkyJBUKhXTIIYekO++8s8n9EyZMSKNGjWqy7/XXX08nnnhi6t69e+rfv3+aPHlyh/4csSVrdNttt6VDDz00de/ePVVVVaUf/ehH6b333ss4dT4LFixIEdH4BcAv2tHaLFy4MB155JFpjz32SAMHDmzy+X1HVcwajRo1KkXEdtuECRPyDZxZsb+HvqizxEexa9TZ3qeLXZ/O9B79VcpS6kzneQCAtubaLgBAVuIDAMhKfAAAWYkPACAr8QEAZCU+AICsxAcAkJX4AACyEh8AQFbiAwDISnwAAFn9PxOJZYoQPFMEAAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "N = 200\n", - "sizes = [mean_cluster_size(random_grid(100, 100)) for _ in range(N)]\n", - "report(sizes)" - ] - }, - { - "cell_type": "markdown", - "id": "9f7b9a04-31ff-44ed-bbf4-19b56c910859", - "metadata": {}, - "source": [ - "A 100 x 100 grid isn't infinite, so let's look at larger grids:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "9e1f69b9-870f-4439-ad62-92d91684392d", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'mean': 7.4134560772843425,\n", - " 'max': 7.651109410864575,\n", - " 'min': 7.116171499733143,\n", - " 'stdev': 0.10453350361308941,\n", - " 'N': 200}" + "7.2028626172827535" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" - }, - { - "data": { - "image/png": "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cldbgDLg7rJ0JK58AABJiQ8AICnxAQAkJT4AgKTEBwCQlPgAAJISHwBAUuIDAEhKfAAASYkPACAp8QEAJCU+AICkxAcAkJS72sJxzt1S4b8K/f0Q4fdEa7jyAQAkJT4AgKTEBwCQlPgAAJISHwBAUuIDAEhKfAAASYkPACAp8QEAJCU+AICkxAcAkJT4AACScmM5aAfczAo4nrjyAQAkJT4AgKTEBwCQlPgAAJISHwBAUuIDAEhKfAAASYkPACCposdHfX19VFRUtNiqq6uLfRoAoEyV5BNOhwwZEs8991zz486dO5fiNABAGSpJfHTp0sXVDgDgiEryno+NGzdGv379YsCAAXHttdfGpk2bSnEaAKAMFf3Kx3nnnReLFy+OwYMHx/vvvx933HFHjBgxIt58883o1avXYcfn8/nI5/PNj5uamoo9EgDQjhQ9PsaNG9f832eccUacf/75MXDgwHjggQdi5syZhx0/b968uO2224o9BgBtrNC7NRfrTs1tdV6OXsn/qW23bt3ijDPOiI0bNx7x+dmzZ8euXbuat4aGhlKPBAC0oZK84fST8vl8/PWvf42LLrroiM/ncrnI5XKlHgMAaCeKfuVj1qxZsWrVqti8eXO88sor8c1vfjOamppi0qRJxT4VAFCGin7l45///Gd861vfig8//DB69+4dw4cPj5dffjnq6uqKfSoAoAwVPT4efvjhYn9JAKADcW8XACAp8QEAJCU+AICkxAcAkJT4AACSEh8AQFLiAwBIquQfr055c2MogPav3P7MdOUDAEhKfAAASYkPACAp8QEAJCU+AICkxAcAkJT4AACSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApNzVlg7nWO7u2FavBToGfw4cHVc+AICkxAcAkJT4AACSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBSx92N5crxxmGFnreY5waAYnPlAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBS4gMASEp8AABJiQ8AICnxAQAkJT4AgKSOu7vaHo/K5W687sQLHHI8/vlxPP2aXfkAAJISHwBAUuIDAEhKfAAASYkPACAp8QEAJCU+AICkxAcAkFTJ4uPee++NAQMGRNeuXeOcc86JF198sVSnAgDKSEni45FHHokZM2bEnDlz4rXXXouLLrooxo0bF1u3bi3F6QCAMlKS+Ljnnnviu9/9blx//fXx5S9/OebPnx+1tbWxcOHCUpwOACgjRb+3y/79+2P9+vVxyy23tNg/duzYWLt27WHH5/P5yOfzzY937doVERFNTU3FHi0iIg7m/1XQ8Z+c41heeywKPe//nrutfs1eW7rXtuW5vbZ1r23Lc3tt6V7bluduD38/HelrZln2+QdnRfbuu+9mEZG99NJLLfb/7Gc/ywYPHnzY8bfeemsWETabzWaz2TrA1tDQ8LmtULK72lZUVLR4nGXZYfsiImbPnh0zZ85sfnzw4MH4+OOPo1evXkc8/rM0NTVFbW1tNDQ0RPfu3Vs3OC1Y0+KzpsVnTYvPmhZfR1/TLMti9+7d0a9fv889tujxccopp0Tnzp2jsbGxxf7t27dH3759Dzs+l8tFLpdrse/kk08+phm6d+/eIf/HtiVrWnzWtPisafFZ0+LryGvao0ePozqu6G84PeGEE+Kcc86JFStWtNi/YsWKGDFiRLFPBwCUmZL82GXmzJnx7W9/O4YNGxbnn39+3HfffbF169b4/ve/X4rTAQBlpCTxcc0118RHH30UP/3pT2Pbtm0xdOjQeOqpp6Kurq4Up2uWy+Xi1ltvPezHOLSeNS0+a1p81rT4rGnxWdP/qsiyo/k3MQAAxeHeLgBAUuIDAEhKfAAASYkPACCpsomP0047LSoqKg7bpk2bdsTjt23bFhMnTowvfelL0alTp5gxY0bagctAoWv62GOPxZgxY6J3797RvXv3OP/88+OZZ55JPHX7VuiarlmzJi644ILo1atXVFZWxumnnx6/+MUvEk/dvhW6pp/00ksvRZcuXeKss84q/aBlpNA1Xbly5RGP/9vf/pZ48varNd+n+Xw+5syZE3V1dZHL5WLgwIHxm9/8JuHUbadkH69ebOvWrYt///vfzY///Oc/x5gxY+Kqq6464vH5fD569+4dc+bM8Yf5pyh0TVevXh1jxoyJuXPnxsknnxyLFi2K8ePHxyuvvBJnn312qrHbtULXtFu3bnHTTTfFV7/61ejWrVusWbMmpk6dGt26dYvvfe97qcZu1wpd00N27doV1113XVxyySXx/vvvl3rMstLaNX3rrbdafDJn7969SzZjuWnNml599dXx/vvvx69//ev44he/GNu3b48DBw6kGLfNle0/tZ0xY0Y8+eSTsXHjxs+9B8yoUaPirLPOivnz56cZrkwVsqaHDBkyJK655pr4yU9+UuLpylNr1nTChAnRrVu3ePDBB0s8XXk62jW99tprY9CgQdG5c+dYtmxZbNiwId2QZebz1nTlypUxevTo2LFjxzHf/uJ48Xlr+vTTT8e1114bmzZtip49e7bBhG2rbH7s8kn79++Phx56KKZMmVLwzec4stas6cGDB2P37t3H5W+co9GaNX3ttddi7dq1MXLkyBJPV56Odk0XLVoUf//73+PWW29NOF15KuT79Oyzz46ampq45JJL4oUXXkg0Yfk5mjV94oknYtiwYXHXXXfFF77whRg8eHDMmjUr9u3bl3jatlE2P3b5pGXLlsXOnTtj8uTJbT1Kh9GaNb377rtj7969cfXVV5dusDJWyJr2798/Pvjggzhw4EDU19fH9ddfX/oBy9DRrOnGjRvjlltuiRdffDG6dCnLP+KSOpo1rampifvuuy/OOeecyOfz8eCDD8Yll1wSK1eujIsvvjjdsGXiaNZ006ZNsWbNmujatWs8/vjj8eGHH8aNN94YH3/88fHxvo+sDI0dOza7/PLLj/r4kSNHZj/4wQ9KN1AHUOiaLlmyJDvxxBOzFStWlHCq8lbImm7atCl7/fXXs/vuuy/r2bNntmTJkhJPV54+b00PHDiQDRs2LFu4cGHzvltvvTU788wzE0xXngr9vX/I5Zdfno0fP74EE5W/o1nTMWPGZF27ds127tzZvO/RRx/NKioqsn/961+lHrHNlV18bNmyJevUqVO2bNmyo36N+Phsha7pww8/nFVWVmZPPvlkiScrX635Pj3k9ttvzwYPHlyCqcrb0azpjh07sojIOnfu3LxVVFQ07/vjH/+YcOL271i+T++4447s9NNPL8FU5e1o1/S6667LBg4c2GLfX/7ylywisrfffruUI7YLZXdNctGiRdGnT5+47LLL2nqUDqOQNV26dGlMmTIlli5d6v/BZziW79MsyyKfz5dgqvJ2NGvavXv3eOONN1rsu/fee+P555+P3//+9zFgwIBSj1lWjuX79LXXXouampoSTFXejnZNL7jggvjd734Xe/bsiZNOOikiIt5+++3o1KlT9O/fP8Wobaqs4uPgwYOxaNGimDRp0mE/y509e3a8++67sXjx4uZ9h97dvmfPnvjggw9iw4YNccIJJ8RXvvKVlGO3a4Ws6dKlS+O6666L//u//4vhw4dHY2NjRERUVlZGjx49ks/eXhWypr/85S/j1FNPjdNPPz0i/vO5Hz//+c9j+vTpyeduz452TTt16hRDhw5t8XyfPn2ia9euh+0/3hXyfTp//vw47bTTYsiQIc1vpnz00Ufj0UcfbYvR261C1nTixIlx++23x3e+85247bbb4sMPP4wf/vCHMWXKlKisrGyL8ZMqq/h47rnnYuvWrTFlypTDntu2bVts3bq1xb5PfvbE+vXrY8mSJVFXVxdbtmwp9ahlo5A1/dWvfhUHDhyIadOmtfjgnEmTJsX999+fYtyyUMiaHjx4MGbPnh2bN2+OLl26xMCBA+POO++MqVOnphy53Sv09z6fr5A13b9/f8yaNSvefffdqKysjCFDhsTy5cvj61//esqR271C1vSkk06KFStWxPTp02PYsGHRq1evuPrqq+OOO+5IOXKbKdvP+QAAylNZfs4HAFC+xAcAkJT4AACSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBS/w8WEx66w9pWLAAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" } ], "source": [ - "report([mean_cluster_size(random_grid(200, 200)) for _ in range(N)])" + "mean_cluster_size(random_grids(100, 100, N=200))" ] }, { "cell_type": "code", "execution_count": 14, - "id": "b580bc66-9bc2-406c-8ce4-806b9c020dc7", + "id": "ac814364-c03d-4888-bbbf-b0d6ac6c6625", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{'mean': 7.47612464655258,\n", - " 'max': 7.681147051292993,\n", - " 'min': 7.279786459597185,\n", - " 'stdev': 0.07889928048753433,\n", - " 'N': 200}" + "7.400513340948056" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" } ], "source": [ - "report([mean_cluster_size(random_grid(300, 300)) for _ in range(N)])" + "mean_cluster_size(random_grids(200, 200, N=200))" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "8824b41d-6224-4156-94a8-6d0ab3e6a1a6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "7.468919989722078" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mean_cluster_size(random_grids(300, 300, N=200))" ] }, { @@ -534,7 +618,7 @@ "id": "6d20f32a-8c7a-4456-aa41-d2989e33fd57", "metadata": {}, "source": [ - "I think that what's happening is that the clusters that are near the edge of the grid get arbitrarily cut off, and since the edge is a smaller percentage of the larger grid, the larger grid has a larger mean cluster size, one that is a better representative of what would happen on an infinite grid. But I can't say exactly what the mean converges to." + "I think that what's happening is that the clusters that are near the edge of the grid get arbitrarily cut off, and since the edge is a smaller percentage of larger grids, a larger grid has a larger mean cluster size, one that is a better representative of what would happen on an infinite grid. But I can't say exactly what the mean converges to; probably somewhere around 7.5." ] }, { @@ -542,37 +626,9 @@ "id": "f3f21c52-8baf-4e71-a33c-e6f4cefd0877", "metadata": {}, "source": [ - "# My First Random Cluster\n", + "# Examining Random Clusters\n", "\n", - "The great thing about an *n* × 1 grid is that the clusters can't interfere with each other. Going left-to-right, once one cluster ends, the next one starts, and there's no looping back. But in an arbitrary 2D array there is looping back. So I want to get a feel for what the \"first\" cluster on an infinite grid looks like: start by placing a color at the origin square, then recursively place random colors in neighboring squares, extending the cluster, but stop when the cluster is completely surrounded by squares of the other color. " - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "ee2a29b8-7ec6-4558-b86a-5933ddc716f0", - "metadata": {}, - "outputs": [], - "source": [ - "def random_cluster(colors='@.', start=(0, 0)) -> Grid:\n", - " \"\"\"Create just enough of a grid to create a single random cluster.\"\"\"\n", - " cluster = {}\n", - " Q = [start] # A queue of the frontier squares of the cluster\n", - " while Q:\n", - " sq = Q.pop()\n", - " if sq not in cluster:\n", - " cluster[sq] = random.choice(colors)\n", - " if cluster[sq] == cluster[start]:\n", - " Q.extend(neighbors(sq))\n", - " return cluster" - ] - }, - { - "cell_type": "markdown", - "id": "58e3e7bc-2576-4332-921c-04e02da6490b", - "metadata": {}, - "source": [ - "I'll define `show` to print a picture of the cluster:" + "On an *n* × 1 grid a cluster is a straight line. But what do clusters look like on an arbitrarily large grid? I'll define `show` to print a grid (and return the number of colored squares in the grid), and `cluster_at(grid, square)` to return just the part of `grid` that contains `square` and all the other members of square's cluster." ] }, { @@ -582,100 +638,82 @@ "metadata": {}, "outputs": [], "source": [ - "def show(cluster: Grid) -> int:\n", - " \"\"\"Print a representation of the grid and return the size of the one cluster.\"\"\"\n", - " xs = sorted({x for (x, y) in cluster})\n", - " ys = sorted({y for (x, y) in cluster})\n", + "def show(grid: Grid) -> int:\n", + " \"\"\"Print a representation of the grid and return counts for the colors.\"\"\"\n", + " xs = sorted({x for (x, y) in grid})\n", + " ys = sorted({y for (x, y) in grid})\n", " for y in ys:\n", - " print(*[cluster.get((x, y), ' ') for x in xs])\n", - " return cluster_size(cluster)\n", + " print(*[grid.get((x, y), ' ') for x in xs])\n", + " return len(grid)\n", "\n", - "def cluster_size(cluster) -> int: \n", - " color = cluster[0, 0]\n", - " return Counter(cluster.values())[color]" + "def cluster_at(grid: Grid[Square, int], square: Square, color='#') -> Grid[Square, Color]:\n", + " \"\"\"The cluster that square belongs to in grid. Grid must already be clustered.\"\"\"\n", + " cluster_number = grid[square]\n", + " assert isinstance(cluster_number, int), \"grid must be already clustered by cluster(grid)\"\n", + " return {sq: color for sq in grid if grid[sq] == cluster_number}" + ] + }, + { + "cell_type": "markdown", + "id": "b866bc53-a9b0-45ca-8f85-d796b42fe987", + "metadata": {}, + "source": [ + "I'll make a 300 x 300 grid and then show the cluster at four different squares, chosen more-or-less randomly:" ] }, { "cell_type": "code", "execution_count": 17, - "id": "1827747e-76ac-496e-ad3b-b7cbf8b5201a", + "id": "35daf49d-9ac8-4cea-b85e-1f95cea2d9d3", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " . . \n", - ". @ @ .\n", - ". @ . \n", - " . \n" - ] - }, - { - "data": { - "text/plain": [ - "3" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "random.seed(37)\n", - "show(random_cluster())" + "random.seed(1234)\n", + "\n", + "grid300 = cluster(random_grid(300, 300))" ] }, { "cell_type": "code", "execution_count": 18, - "id": "fd429e22-b8bd-475e-9bb0-cbdac2d57c0e", + "id": "f23766c7-18b2-4c94-85bf-0346c70eaa21", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - " . \n", - " . @ . \n", - " . @ @ . \n", - " . . @ . \n", - " . @ . . @ . \n", - " . @ @ @ @ @ . \n", - " . . @ @ . @ . @ .\n", - " . . @ . @ @ @ @ @ @ @ .\n", - " . @ . @ @ @ @ @ @ . . . \n", - " . @ @ @ @ . . @ . \n", - " . . @ . @ . @ . \n", - " . . @ . . . @ @ @ @ . \n", - " . . @ . . @ @ @ @ @ . . . \n", - " . @ @ @ @ . . @ @ . @ @ @ . \n", - " . @ @ @ . . . @ @ . @ @ . . \n", - " . . @ @ @ @ @ @ . @ . @ . @ @ @ @ . \n", - " . @ @ @ . . @ . @ @ @ @ . @ @ . . . \n", - " . @ . @ . . . @ . @ @ @ @ @ . \n", - " . . @ . . @ @ @ . . . . . \n", - " . @ @ . @ @ . @ . \n", - " . @ . @ @ @ . . \n", - " . @ @ @ @ @ @ @ . \n", - " . @ @ @ . @ @ @ . \n", - " . . . . . @ @ @ . @ @ . \n", - " . @ @ @ . . @ @ @ @ @ @ . @ . \n", - " . @ @ @ . @ @ . . @ @ . . \n", - " . @ @ @ @ @ @ . @ . \n", - " . @ @ . . . . @ @ @ . \n", - " . . . @ . . @ . \n", - ". @ @ . @ @ . . \n", - " . @ @ @ . \n", - " . @ @ @ . \n", - " . . . \n" + " # # # # # \n", + " # # # # # # # \n", + " # # # # # # # # \n", + " # # # # # # \n", + " # # # # # # # # # # \n", + " # # # # # # # # # \n", + " # # # # # # # # # # # # \n", + " # # # # # # # # # # # #\n", + " # # # # # # # # # #\n", + " # # # # # # # # # # # # \n", + " # # # # # # # # # # # # # # # # # # \n", + " # # # # # # # # # # # # # # # # # # \n", + " # # # # # # # # # # # # # # # # # # # # \n", + " # # # # # # # # # # \n", + " # # # # # # # # # # # # \n", + "# # # # # # # # # # # # \n", + " # # # # # # # \n", + "# # # # # # # # # \n", + "# # # # # \n", + " # # # # # # # # # # # \n", + " # # # # # # # \n", + " # # # # # # \n", + " # # # # # # # # # \n", + " # # # # \n", + " # \n" ] }, { "data": { "text/plain": [ - "167" + "240" ] }, "execution_count": 18, @@ -684,31 +722,50 @@ } ], "source": [ - "show(random_cluster())" + "show(cluster_at(grid300, (150, 150)))" ] }, { "cell_type": "code", "execution_count": 19, - "id": "13ba443e-c5b9-4897-9075-0833f92081ae", + "id": "408ad7a8-9cd6-4d0a-a7e1-170581eb4c45", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - " . . \n", - ". @ @ . . \n", - " . @ @ @ .\n", - " . . @ .\n", - " . @ .\n", - " . \n" + " # # \n", + " # # # # \n", + " # \n", + " # \n", + " # # \n", + " # # \n", + " # \n", + " # # # # # # # # # # \n", + " # # # # # # # # # \n", + " # # # # # # # \n", + " # # # # # # # # \n", + " # # # # # # # # # # # # \n", + " # # # # # # # \n", + " # # # # # # # # # \n", + " # # # # # # # # # # \n", + " # # # # # # # # # # # # #\n", + " # # # # # # # # # # # # \n", + " # # # # # # # # # # # # # # # # \n", + "# # # # # # # # # # # \n", + "# # # # # # # # \n", + " # # # # # \n", + " # # # # # # # # \n", + " # # \n", + "# # # \n", + " # \n" ] }, { "data": { "text/plain": [ - "7" + "164" ] }, "execution_count": 19, @@ -717,7 +774,105 @@ } ], "source": [ - "show(random_cluster())" + "show(cluster_at(grid300, (150, 200)))" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "1827747e-76ac-496e-ad3b-b7cbf8b5201a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " # # # \n", + " # # # # # # \n", + " # # # # \n", + " # # # # # \n", + " # # # # # # # \n", + " # # # # # # # #\n", + " # # # # # # # # # \n", + " # # # # # # # # # # \n", + " # # # # # \n", + " # # \n", + " # # \n", + " # # # # # # \n", + " # # # # # \n", + " # # # # # # # # \n", + " # # # # # # # \n", + " # # # \n", + " # # # # \n", + " # # # # # # \n", + " # # # # # # # \n", + " # # # # # # # \n", + " # # # # # # # \n", + " # # # # # # # # # # \n", + " # # # # # # # # # # # # \n", + " # # # # # # \n", + " # # # # # # # \n", + " # # # # # # # # \n", + " # # # # # # # # # \n", + " # # # # # # # # \n", + " # # # # # # # \n", + " # # # # # # # # # # # \n", + " # # # # # # # # # # # # # # \n", + " # # # # # # # # # # # # # \n", + "# # # # # # # # # # # # \n", + " # # # # # \n", + " # # # # # \n", + " # # # # # # # # \n", + " # # # \n", + " # # # # # # # \n", + " # # # # # \n", + " # # # # # \n", + "# # # # # # # # # \n", + " # # # # # # # \n", + " # \n" + ] + }, + { + "data": { + "text/plain": [ + "293" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "show(cluster_at(grid300, (200, 150)))" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "fd429e22-b8bd-475e-9bb0-cbdac2d57c0e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "#\n" + ] + }, + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "show(cluster_at(grid300, (200, 200)))" ] }, { @@ -725,100 +880,83 @@ "id": "faf079b0-8534-4d92-aa8e-acd035dc5420", "metadata": {}, "source": [ - "We can see that there is a lot of variation in size and shape of the first clusters. We can also see that the shape will constrain other clusters: there are some small clusters of the second color that are trapped inside the cluster of the first color. Below I sample 10,000 first clusters and report on their sizes:" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "ab6ba895-aa51-41af-baf1-f09ee47e973f", - "metadata": {}, - "outputs": [], - "source": [ - "sizes = [cluster_size(random_cluster()) for _ in range(10_000)]" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "df7f6ae0-32aa-4002-8056-9de0dc2da07a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'mean': 59.6728, 'max': 917, 'min': 1, 'stdev': 75.3054688022268, 'N': 10000}" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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9cBdd92lGTNmaPLkyXH7WYtz5+mnn9bYsWP1rW99S7m5ubryyiu1ceNG93HW4ty5+uqr9ac//Umvv/66JOlvf/ubtm/frq9//euSWIu+MqA/sdcG//rXv3TixIluv5QyLy+v2y+vRO8wxmjx4sW6+uqrVVRUJEnun/Xp1uGtt95yZ1JTUzVs2LBuM6xVYmpra/XKK69oz5493R5jLc6df/7zn1q/fr0WL16se+65R7t379bChQvl9Xp12223sRbn0N13361IJKJLLrlESUlJOnHihO6//37dfPPNkvh70VeImF7i8XjivjbGdNuH3jF//nz9/e9/1/bt27s91pN1YK0S09LSokWLFmnbtm0aMmTIx86xFn3v5MmTGjt2rKqqqiRJV155pfbv36/169frtttuc+dYi7735JNPavPmzXriiSd02WWXKRQKKRgMyu/3a9asWe4ca9G7+Oeks5STk6OkpKRuldzW1tatuHH2FixYoKefflovvviihg8f7u73+XySdMZ18Pl86urqUnt7+8fO4JM1NTWpra1NxcXFSk5OVnJyshoaGvTTn/5UycnJ7p8la9H3LrzwQl166aVx+0aPHq23335bEn8vzqXvf//7Wr58uW666SaNGTNGgUBA3/ve91RdXS2JtegrRMxZSk1NVXFxserr6+P219fXa/z48f10VIOPMUbz58/XU089pRdeeEEFBQVxjxcUFMjn88WtQ1dXlxoaGtx1KC4uVkpKStxMa2ur9u3bx1olYNKkSdq7d69CoZC7jR07VrfccotCoZAuvvhi1uIc+drXvtbtowZef/1195fk8vfi3Hn//fd13nnxP1KTkpLcW6xZiz7ST28oHlQ+usX6kUceMQcOHDDBYNCkp6ebN998s78PbdC48847jeM45qWXXjKtra3u9v7777szq1evNo7jmKeeesrs3bvX3Hzzzae9fXH48OHm+eefN6+88oq57rrruH2xF/zv3UnGsBbnyu7du01ycrK5//77zRtvvGG2bNlihg4dajZv3uzOsBbnxqxZs8znP/959xbrp556yuTk5Jhly5a5M6xF7yNiesnPfvYzM3LkSJOammq+8pWvuLf+ondIOu326KOPujMnT540K1euND6fz3i9XnPNNdeYvXv3xr1OZ2enmT9/vsnKyjJpaWmmvLzcvP322+f4bAafUyOGtTh3fv/735uioiLj9XrNJZdcYh5++OG4x1mLcyMajZpFixaZESNGmCFDhpiLL77Y3HvvvSYWi7kzrEXv8xhjTH9eCQIAAOgJ3hMDAACsRMQAAAArETEAAMBKRAwAALASEQMAAKxExAAAACsRMQAAwEpEDAAAsBIRAwAArETEAAAAKxExAADASkQMAACw0v8DjbzBwXX5iLQAAAAASUVORK5CYII=\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "report(sizes)" - ] - }, - { - "cell_type": "markdown", - "id": "7d2c2f1a-5b51-4d02-a35c-05d6f90e5a09", - "metadata": {}, - "source": [ - "There's quite a spread, with the standard deviation being larger than the mean, and the maximum being about 10 times the mean. It might help to zoom in on the smaller sizes:" + "We see that there is a lot of variation in size and shape of the clusters. To get a better idea of the variation I introduce `cluster_counts(S)` to do the following: make an S x S grid; cluster it; define `sizes` such that `sizes[cluster_number]` gives the cluster's size; define the set of squares in the `perimeter`; define the set of clusters that touch the perimeter (and thus have a size that might be smaller than it should be); and finally return a Counter that for each cluster size gives the number of squares that are in a cluster of that size:" ] }, { "cell_type": "code", "execution_count": 22, - "id": "3dcbff13-ddb1-4ba0-9650-3b6b6122f76a", + "id": "77c0aba0-700d-486e-ba9e-d70dbe11097e", + "metadata": {}, + "outputs": [], + "source": [ + "def cluster_counts(S: int) -> Counter:\n", + " \"\"\"For a random SxS grid, return a counter of {cluster_size: squares_with_that_size},\n", + " for all squares that are not part of a cluster that touches the perimeter.\"\"\"\n", + " grid = cluster(random_grid(S, S)) # Grid of {square: cluster_number}\n", + " sizes = Counter(grid.values()) # Counter of {cluster_number: cluster_size}\n", + " perimeter = cross([0, S - 1], range(S)) | cross(range(S), [0, S - 1]) # Squares on perimeter\n", + " perimeter_clusters = {grid[p] for p in perimeter} # Cluster numbers on perimeter squares\n", + " return Counter(sizes[grid[sq]] for sq in grid if grid[sq] not in perimeter_clusters)\n", + "\n", + "def cross(xs, ys) -> Set[Square]: return {(x, y) for x in xs for y in ys}" + ] + }, + { + "cell_type": "markdown", + "id": "a314bce0-fe3c-490c-a859-3650266542c2", + "metadata": {}, + "source": [ + "I'll then define `counter_stats` to return a dict of statistics about a counter, and use it to explore our cluster counts:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "44f89808-d7a6-47ec-8945-d6f6c4affbb9", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{'mean': 7.851026539809714,\n", - " 'max': 20,\n", - " 'min': 1,\n", - " 'stdev': 5.816812011424741,\n", - " 'N': 3994}" + "{'mean': 58.88548665248869,\n", + " 'N': 974751,\n", + " 'range': range(1, 734),\n", + " 'common': [(1, 62777),\n", + " (2, 31428),\n", + " (3, 28983),\n", + " (4, 25780),\n", + " (5, 23480),\n", + " (6, 21900),\n", + " (7, 20412),\n", + " (8, 18168),\n", + " (9, 17316),\n", + " (10, 16280)]}" ] }, - "execution_count": 22, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" } ], "source": [ - "smaller = [s for s in sizes if s <= 20]\n", - "report(smaller, bins=range(22))" + "def counter_stats(counter: Counter, common=10) -> dict:\n", + " \"\"\"Return a dict of some statistics for the values in this counter.\"\"\"\n", + " return dict(mean=mean(counter.elements()), N=sum(counter.values()), \n", + " range=range(min(counter), max(counter) + 1), common=counter.most_common(common))\n", + "\n", + "counter_stats(cluster_counts(1000))" ] }, { "cell_type": "markdown", - "id": "e93bc126-d2c9-415d-97cf-ee07bd36502d", + "id": "b0005025-4a9e-4fe9-ac04-0d1ce43812f8", "metadata": {}, "source": [ - "We see that there are over 600 clusters of size 1. That makes sense; we start with one square, and about once every 24 times all 4 neighbors will be the other color, and 10000 / 16 = 625." + "Wait a minute ... earler we saw that the average cluster size on a 300 x 300 grid was about 7.5. But the `counter_stats` here give a `mean` cluster size of somewhere around 60. How can that be? It is because the two numbers are answering two different questions. **7.5** is the answer to \"if we randomly pick a **cluster**, what is its expected size\"? **60** is the answer to \"if we randomly pick a **square**, what is the expected size of the square's cluster?\"\n", + "\n", + "In the output from `counter_stats`, the `common` entry gives a list of the ten most common sizes (which happen to be the integers from 1 to 10), paired with the counts of how many squares have that size cluster. On average, 1/16 of the clusters have size 1, (when all 4 neighbors of the start square get the opposite color). Half as many have size 2. A cluster with hundreds of squares is far less common, but certainly possible.\n", + "\n" ] }, { @@ -833,12 +971,12 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "id": "da7a19ef-d47f-4c69-8fe5-cfaf79861dab", "metadata": {}, "outputs": [], "source": [ - "assert grids(1, 3) == [\n", + "assert all_grids(1, 3) == [\n", " {(0, 0): 'R', (0, 1): 'R', (0, 2): 'R'},\n", " {(0, 0): 'R', (0, 1): 'R', (0, 2): 'B'},\n", " {(0, 0): 'R', (0, 1): 'B', (0, 2): 'R'},\n", @@ -848,7 +986,7 @@ " {(0, 0): 'B', (0, 1): 'B', (0, 2): 'R'},\n", " {(0, 0): 'B', (0, 1): 'B', (0, 2): 'B'}]\n", "\n", - "assert grids(1, 2, 'RGB') == [\n", + "assert all_grids(1, 2, 'RGB') == [\n", " {(0, 0): 'R', (0, 1): 'R'},\n", " {(0, 0): 'R', (0, 1): 'G'},\n", " {(0, 0): 'R', (0, 1): 'B'},\n", @@ -862,7 +1000,7 @@ "grid6x1 = one_grid(6, 1, 'RRBBBR')\n", "assert grid6x1 == {(0, 0): 'R', (1, 0): 'R', (2, 0): 'B', (3, 0): 'B', (4, 0): 'B', (5, 0): 'R'}\n", "assert cluster(grid6x1) == {(0, 0): 1, (1, 0): 1, (2, 0): 2, (3, 0): 2, (4, 0): 2, (5, 0): 3}\n", - "assert mean_cluster_size(grid6x1) == 2\n", + "assert mean_cluster_size([grid6x1]) == 2\n", "\n", "grid5x3 = one_grid(5, 3, 'RR:RR'\n", " '.R:R.'\n", @@ -871,13 +1009,16 @@ " (0, 0): 1, (1, 0): 1, (2, 0): 2, (3, 0): 1, (4, 0): 1,\n", " (0, 1): 3, (1, 1): 1, (2, 1): 2, (3, 1): 1, (4, 1): 4,\n", " (0, 2): 3, (1, 2): 1, (2, 2): 1, (3, 2): 1, (4, 2): 4}\n", - "assert mean_cluster_size(grid5x3) == 3.75\n", + "assert mean_cluster_size([grid5x3]) == 3.75\n", "\n", "grid10x2 = one_grid(10, 2, 'RBRBRRRBBR' # Example from diagram at top of notebook\n", " 'RRRRBBRBRB') \n", - "assert mean_cluster_size(grid10x2) == 20/9\n", + "assert mean_cluster_size([grid10x2]) == 20/9\n", "\n", - "assert len(random_grid(3, 4)) == 12" + "grid4x3 = random_grid(3, 4)\n", + "assert len(grid4x3) == 12\n", + "\n", + "assert cross((10, 20, 30), (1, 2, 3)) == {(10, 1), (10, 2), (10, 3), (20, 1), (20, 2), (20, 3), (30, 1), (30, 2), (30, 3)}" ] } ],