diff --git a/_freeze/posts/number-number/1/index/execute-results/html.json b/_freeze/posts/number-number/1/index/execute-results/html.json index c88f4b7..e6ff56b 100644 --- a/_freeze/posts/number-number/1/index/execute-results/html.json +++ b/_freeze/posts/number-number/1/index/execute-results/html.json @@ -2,7 +2,7 @@ "hash": "749e06a7af6447084f45c7dd55a91231", "result": { "engine": "jupyter", - "markdown": "---\ntitle: \"Numbering Numbers: From 0 to ∞\"\ndescription: |\n How do we count an infinitude of numbers?\nformat:\n html:\n html-math-method: katex\ndate: \"2023-11-26\"\ndate-modified: \"2025-07-22\"\ncategories:\n - algebra\n - question-mark function\n - haskell\n---\n\n\n\nThe infinite is replete with paradoxes.\nSome of the best come from comparing sizes of infinite collections.\nFor example, every natural number can be mapped to a (nonnegative) even number and vice versa.\n\n$$\n\\begin{gather*}\n \\N \\rightarrow 2\\N\n \\\\\n n \\mapsto 2n\n \\\\ \\\\\n 0 \\mapsto 0,~ 1 \\mapsto2,~ 2 \\mapsto 4,~ 3 \\mapsto 6,~ 4 \\mapsto 8, ...\n\\end{gather*}\n$$\n\n(For the purposes of this post, $0 \\in \\N, ~ 0 \\notin \\N^+$)\n\nAll even numbers are \"hit\" by this map (by the definition of an even number),\n and no two natural numbers map to the same even number\n (again, more or less by definition, since $2m = 2n$ implies that $m = n$ over $\\N$).\nTherefore, the map is [one-to-one](https://en.wikipedia.org/wiki/Injective_function)\n and [onto](https://en.wikipedia.org/wiki/Surjective_function),\n so the map is a [bijection](https://en.wikipedia.org/wiki/Bijection).\nA consequence is that the map has an inverse, namely by reversing all of the arrows in the above block\n (i.e., the action of halving an even number).\n\nBijections with the natural numbers are easier to understand as a way to place things\n into a linear sequence.\nIn other words, they enumerate \"some sort of item\"; in this case, even numbers.\n\nIn the finite world, a bijection between two things implies that they have the same size.\nIt makes sense to extend the same logic to the infinite world, but there's a catch.\nThe nonnegative even numbers are clearly a strict subset of the natural numbers,\n but by this argument they have the same size.\n\n$$\n\\begin{matrix}\n 2\\N & \\longleftrightarrow & \\N & \\hookleftarrow & 2\\N\n \\\\\n 0 & \\mapsto & \\textcolor{red}0 & \\dashleftarrow & \\textcolor{red}0\n \\\\\n 2 & \\mapsto & 1 & &\n \\\\\n 4 & \\mapsto & \\textcolor{red}2 & \\dashleftarrow & \\textcolor{red}2\n \\\\\n 6 & \\mapsto & 3 & &\n \\\\\n 8 & \\mapsto & \\textcolor{red}4 & \\dashleftarrow & \\textcolor{red}4\n \\\\\n 10 & \\mapsto & 5 & &\n \\\\\n 12 & \\mapsto & \\textcolor{red}6 & \\dashleftarrow & \\textcolor{red}6\n \\\\\n 14 & \\mapsto & 7 & &\n \\\\\n 16 & \\mapsto & \\textcolor{red}8 & \\dashleftarrow & \\textcolor{red}8\n \\\\\n \\vdots & & \\vdots & & \\vdots\n\\end{matrix}\n$$\n\n\nAre we Positive?\n----------------\n\nThe confusion continues if we look at the integers and the naturals.\nIntegers are the natural numbers and their negatives, so it would be intuitive to assume that\n there are twice as many of them as there are naturals (more or less one to account for zero).\nBut since that logic fails for the naturals and the even numbers,\n it fails for the naturals and integers as well.\n\n$$\n\\begin{gather*}\n \\begin{align*}\n \\mathbb{N} &\\rightarrow \\mathbb{Z}\n \\\\\n n &\\mapsto \\left\\{ \\begin{matrix}\n n/2 & n \\text{ even}\n \\\\\n -(n+1)/2 & n \\text{ odd}\n \\end{matrix} \\right.\n \\end{align*}\n \\\\ \\\\\n 0 \\mapsto 0,\\quad 2 \\mapsto 1, \\quad 4 \\mapsto 2, \\quad 6 \\mapsto 3, \\quad 8 \\mapsto 4,~...\n \\\\\n 1 \\mapsto -1, \\quad 3 \\mapsto -2, \\quad 5 \\mapsto -3, \\quad 7 \\mapsto -4, \\quad 9 \\mapsto -5,~...\n\\end{gather*}\n$$\n\nOr, in Haskell[^1]:\n\n[^1]: That is, if you cover your eyes and pretend that `undefined` will never happen,\n and if you ignore that `Int` is bounded, unlike `Integer`.\n\n\n\nIn other words, this map sends even numbers to the naturals (the inverse of the doubling map)\n and the odds to the negatives.\nThe same arguments about the bijective nature of this map apply as before, and so the paradox persists,\n since naturals are also a strict subset of integers.\n\n\n### Rational Numbers\n\nRationals are a bit worse.\nTo make things a little easier, let's focus on the positive rationals (i.e., fractions excluding 0).\nUnlike the integers, there is no obvious \"next rational\" after (or even before) 1.\nIf there were, we could follow it with its reciprocal, like how an integer is followed\n by its negative in the map above.\n\nOn the other hand, the integers provide a sliver of hope that listing all rational numbers is possible.\nIntegers can be defined as pairs of natural numbers, along with a way of considering two pairs equal.\n\n$$\n\\begin{gather*}\n -1 = (0,1) \\sim_\\Z (1,2) \\sim_\\Z (2,3) \\sim_\\Z (3,4) \\sim_\\Z ...\n \\\\[10pt]\n (a,b) \\sim_\\mathbb{Z} (c,d) \\iff a+d = b+c \\quad a,b,c,d \\in \\mathbb{N}\n \\\\[10pt]\n \\mathbb{Z} := ( \\mathbb{N} \\times \\mathbb{N} ) / \\sim_\\mathbb{Z}\n\\end{gather*}\n$$\n\n\n\nThis relation is the same as saying $a - b = c - d$ (i.e., that -1 = 0 - 1, etc.),\n but has the benefit of not requiring subtraction to be defined.\nThis is all the better, since, as grade-schoolers are taught, subtracting a larger natural number\n from a smaller one is impossible.\n\nThe same equivalence definition exists for positive rationals.\nIt is perhaps more familiar, because of the emphasis placed on simplifying fractions when learning them.\nWe can [cross-multiply](https://en.wikipedia.org/wiki/Cross-multiplication) fractions to get\n a similar equality condition to the one for integers.\n\n$$\n\\begin{gather*}\n {1 \\over 2} = (1,2) \\sim_\\mathbb{Q} \\overset{2/4}{(2,4)} \\sim_\\mathbb{Q}\n \\overset{3/6}{(3,6)} \\sim_\\mathbb{Q} \\overset{4/8}{(4,8)} \\sim_\\mathbb{Q} ...\n \\\\ \\\\\n (a,b) \\sim_\\mathbb{Q} (c,d) \\iff ad = bc \\quad a,b,c,d \\in \\mathbb{N}^+\n \\\\ ~ \\\\\n \\mathbb{Q^+} := ( \\mathbb{N^+} \\times \\mathbb{N^+} ) / \\sim_\\mathbb{Q}\n\\end{gather*}\n$$\n\n\n\nWe specify that neither element of the pair can be zero, so this excludes divisions by zero\n (and the especially tricky case of 0/0, which would be equal to all fractions).\nEffectively, this just replaces where addition appears in the integer equivalence with multiplication.\n\n\n### Eliminating Repeats\n\nNaively, to tackle both of these cases, we might consider enumerating pairs of natural numbers.\nWe order them by sums and break ties by sorting on the first index.\n\n\n\n::: {#d55ab25e .cell .plain execution_count=7}\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \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 Index\n \n Pair (a, b)\n \n Sum (a + b)\n \n Integer (a - b)\n \n Rational (a+1 / b+1)\n
\n 0\n \n (0,0)\n \n 0\n \n 0\n \n 1/1\n
\n 1\n \n (0,1)\n \n 1\n \n -1\n \n 1/2\n
\n 2\n \n (1,0)\n \n 1\n \n 1\n \n 2/1\n
\n 3\n \n (0,2)\n \n 2\n \n -2\n \n 1/3\n
\n 4\n \n (1,1)\n \n 2\n \n 0\n \n 2/2\n
\n 5\n \n (2,0)\n \n 2\n \n 2\n \n 3/1\n
\n 6\n \n (0,3)\n \n 3\n \n -3\n \n 1/4\n
\n 7\n \n (1,2)\n \n 3\n \n -1\n \n 2/3\n
\n 8\n \n (2,1)\n \n 3\n \n 1\n \n 3/2\n
\n 9\n \n (3,0)\n \n 3\n \n 3\n \n 4/1\n
\n```\n:::\n:::\n\n\nThis certainly works to show that naturals and pairs of naturals can be put into bijection,\n but it when interpreting the results as integers or rationals, we double-count several of them.\nThis is easy to see in the case of the integers, but it will also happen in the rationals.\nFor example, the pair (3, 5) would correspond to 4/6 = 2/3, which has already been counted.\n\nIncidentally, Haskell comes with a function called `nubBy`.\nThis function eliminates duplicates according to another function of our choosing.\nWe can also just implement it ourselves and use it to create a naive enumeration of integers and rationals,\n based on the equalities defined earlier:\n\n\n\nFor completeness's sake, the resulting pairs of each map are as follows\n\n::: {#3e52ea03 .cell .plain execution_count=9}\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \n \n \n
\n n\n \n \n allIntegersMap n\n \n \n \n allRationalsMap n\n \n
\n 0\n \n (0,0) = 0\n \n (1,1) = 1/1\n
\n 1\n \n (0,1) = -1\n \n (1,2) = 1/2\n
\n 2\n \n (1,0) = 1\n \n (2,1) = 2/1\n
\n 3\n \n (0,2) = -2\n \n (1,3) = 1/3\n
\n 4\n \n (2,0) = 2\n \n (3,1) = 3/1\n
\n 5\n \n (0,3) = -3\n \n (1,4) = 1/4\n
\n 6\n \n (3,0) = 3\n \n (2,3) = 2/3\n
\n 7\n \n (0,4) = -4\n \n (3,2) = 3/2\n
\n 8\n \n (4,0) = 4\n \n (4,1) = 4/1\n
\n 9\n \n (0,5) = -5\n \n (1,5) = 1/5\n
\n```\n:::\n:::\n\n\nNote that the tuples produced by `allIntegers`, when interpreted as integers, happen to coincide\n with the earlier enumeration given by `listIntegers`.\n\n\nTree of Fractions\n-----------------\n\nThere's an entirely separate structure which contains all rationals in least terms.\nIt relies on an operation between two fractions called the *mediant*.\nFor two rational numbers in least terms *p* and *q*, such that *p* < *q*, the mediant is designated *p* ⊕ *q* and will:\n\n1. also be in least terms (with some exceptions, see below),\n2. be larger than *p*, and\n3. be smaller than *q*\n\n$$\n\\begin{gather*}\n p = {a \\over b} < {c \\over d} = q, \\quad \\gcd(a,b) = \\gcd(c,d) = 1\n \\\\ \\\\\n p < p \\oplus q < q \\quad \\phantom{\\gcd(a+c, b+d) = 1}\n \\\\ \\\\\n {a \\over b} < {a+c \\over b+d} < {c \\over d}, \\quad \\gcd(a+c, b+d) = 1\n\\end{gather*}\n$$\n\nWe know our sequence of rationals starts with 1/1, 1/2, and 2/1.\nIf we start as before with 1/1 and want to get the other quantities,\n then we can take its mediants with 0/1 and 1/0, respectively\n (handwaving the fact that the latter isn't a legitimate fraction).\n\n$$\n\\begin{align*}\n && && \\large{1 \\over 1} && &&\n \\\\\n { \\oplus {0 \\over 1} } && \\large{/} && && \\large{\\backslash} ~ && \\oplus {1 \\over 0}\n \\\\\n && \\large{1 \\over 2} && && \\large{2 \\over 1} &&\n\\end{align*}\n$$\n\nWe might try continuing this pattern by doing the same thing to 1/2.\nWe can take its mediant with 0/1 to get 1/3.\nUnfortunately, the mediant of 1/2 and 1/0 is 2/2 (as is the mediant of 2/1 with 0/1),\n which isn't in least terms, and has already appeared as 1/1.\n\nWe could try another fraction that's appeared in the tree.\nUnfortunately, 2/1 suffers from the same issue as 1/0 -- 1/2 ⊕ 2/1 = 3/3, which is\n the same quantity as before, despite both fractions being in least terms.\nOn the other hand, 1/2 ⊕ 1/1 = 2/3, which is in least terms.\nSimilarly, 2/1 ⊕ 1/1 is 3/2, its reciprocal.\n\n$$\n\\begin{align*}\n && && \\large{1 \\over 2} && &&\n \\\\\n { \\oplus {0 \\over 1} } && \\large{/} && && \\large{\\backslash} ~ && \\oplus {1 \\over 1}\n \\\\\n && \\large{1 \\over 3} && && \\large{2 \\over 3} &&\n\\end{align*}\n\\qquad \\qquad\n\\begin{align*}\n && && \\large{2 \\over 1} && &&\n \\\\\n { \\oplus {1 \\over 1} } && \\large{/} && && \\large{\\backslash} ~ && \\oplus {1 \\over 0}\n \\\\\n && \\large{3 \\over 2} && && \\large{3 \\over 1} &&\n\\end{align*}\n$$\n\nThe trick is to notice that a step to the left \"updates\" what the next step to the right looks like.\nSteps to the right behave symmetrically.\nFor example, in the row we just generated, the left child of 2/3 is its mediant with 1/2,\n its right child is its mediant with 1/1.\n\nContinuing this iteration ad infinitum forms the\n [Stern-Brocot tree](https://en.wikipedia.org/wiki/Stern%E2%80%93Brocot_tree).\nA notable feature of this is that it is a\n [binary search tree](https://en.wikipedia.org/wiki/Binary_search_tree) (of infinite height).\nThis means that for any node, the value at the node is greater than all values in the left subtree\n and less than all values in the right subtree.\n\n![](./stern-brocot_tree.png)\n\nThere's a bit of a lie in presenting the tree like this.\nAs a binary tree, it's most convenient to show the nodes spaced evenly, but the distance between\n 1/1 and 2/1 is not typically seen as the same as the distance between 1/1 and 1/2.\n\nWe can implement this in Haskell using `Data.Tree`.\nThis package actually lets you describe trees with any number of child nodes,\n but we only need two for the sake of the Stern-Brocot tree.\n\n\n\n### Cutting the Tree Down\n\nWe're halfway there. All that remains is to read off every value in the tree as a sequence.\nPerhaps the most naive way would be to read off by always following the left or right child.\nUnfortunately, these give some fairly dull sequences.\n\n::: {#8c4f3756 .cell layout-ncol='2' execution_count=11}\n\n::: {.cell-output .cell-output-display}\n```\n(1,1)\n(1,2)\n(1,3)\n(1,4)\n(1,5)\n(1,6)\n(1,7)\n(1,8)\n(1,9)\n(1,10)\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\n(1,1)\n(2,1)\n(3,1)\n(4,1)\n(5,1)\n(6,1)\n(7,1)\n(8,1)\n(9,1)\n(10,1)\n```\n:::\n:::\n\n\nRather than by following paths in the tree, we can instead do a breadth-first search.\nIn other words, we read off each row individually, in order.\nThis gives us our sequence of rational numbers with no repeats.\n\n$$\n\\begin{gather*}\n \\begin{align*}\n \\mathbb{N^+}& ~\\rightarrow~ \\mathbb{Q}\n \\\\\n n & ~\\mapsto~ \\text{bfs}[n]\n \\end{align*}\n \\\\ \\\\\n 1 \\mapsto 1/1,~ \\\\\n 2 \\mapsto 1/2,\\quad 3 \\mapsto 2/1,~ \\\\\n 4 \\mapsto 1/3,\\quad 5 \\mapsto 2/3, \\quad 6 \\mapsto 3/2, \\quad 7 \\mapsto 3/1,~ ...\n\\end{gather*}\n$$\n\nFor convenience, this enumeration is given starting from 1 rather than from 0.\nThis numbering makes it clearer that each row starts with a power of 2,\n since the structure is a binary tree, and the complexity doubles with each row.\nThe enumeration could just as easily start from 0 by starting with $\\N$,\n then getting to $\\N^+$ with $n \\mapsto n+1$.\n\nWe can also write a breadth-first search in Haskell, for posterity:\n\n::: {#e6151cfb .cell execution_count=12}\n\n::: {.cell-output .cell-output-display}\n```\n(1,1) = 1/1\n(1,2) = 1/2\n(2,1) = 2/1\n(1,3) = 1/3\n(2,3) = 2/3\n(3,2) = 3/2\n(3,1) = 3/1\n(1,4) = 1/4\n(2,5) = 2/5\n(3,5) = 3/5\n```\n:::\n:::\n\n\nThe entries in this enumeration have already been given.\n\n\n### Another Tree\n\nAnother tree of fractions to consider is the tree of binary fractions.\nThese fractions simply consist of odd numbers divided by powers of two.\nThe most convenient way to organize these into a tree is to keep denominators equal\n if the nodes have the same depth from the root.\nWe also stipulate that we arrange the nodes as a binary search tree, like the Stern-Brocot tree.\n\nThe tree starts from 1/1 as before.\nIts children have denominator 2, so we have 1/2 to the left and 3/2 to the right.\nThis is equivalent to subtracting 1/2 for the left step and adding 1/2 for the right step.\nAt the next layer, we want fractions with denominator 1/4, and do similarly.\nIn terms of adding and subtracting, we just use 1/4 instead of 1/2.\n\n![](./dyadic_fraction_tree.png)\n\nWe can describe this easily in Haskell:\n\n\n\nThe entries of this tree have an additional interpretation when converted to their binary expansions.\nThese fractions always terminate in a \"1\" in binary, but ignoring this final entry, starting from the root\n and following \"left\" for 0 and \"right\" for 1 places us at that fraction in the tree.\nIn other words, the binary expansions encode the path from the root to the node.\n\n![](./binary_expansion_tree.png)\n\n\nWhy Bother?\n-----------\n\nThe tree of binary fractions and the Stern-Brocot tree are both infinite binary search trees,\n so we might imagine overlaying one tree over the other, pairing up the individual entries.\n\n![](./question_mark_tree.png)\n\nIn Haskell, we can pair up entries recursively:\n\n\n\nConveniently, both left subtrees of the root fall in the interval (0, 1).\nIt also pairs up 1 and 1/2 with themselves.\nDoing so establishes a bijection between the rationals and the binary rationals in that interval.\nRationals are more continuous than integers, so it might be of some curiosity to plot this function.\nWe only have to look at a square over the unit interval. Doing so reveals a curious shape:\n\n::: {#4d3b6159 .cell layout-ncol='2' execution_count=15}\n\n::: {.cell-output .cell-output-display}\n![Binary rationals on the x-axis, rationals on the y-axis](index_files/figure-html/cell-15-output-1.svg){}\n:::\n\n::: {.cell-output .cell-output-display}\n![Rationals on the x-axis, binary rationals on the y-axis](index_files/figure-html/cell-15-output-2.svg){}\n:::\n:::\n\n\nThe plot on the right which maps the rationals to the binary rationals is known as\n [Minkowski's question mark function](https://en.wikipedia.org/wiki/Minkowski%27s_question-mark_function).\nNotice that this function is nearly 1/2 for values near 1/2\n (nearly 1/4 for values near 1/3, nearly 1/8 for values near 1/4, etc.).\n\n\n### I'm Repeating Myself\n\nThe inverse question mark map (which I'll call ¿ for short), besides mapping binary rationals to rationals,\n has an interesting relationship with other rational numbers.\nRecall that we only defined the function in terms of fractions\n which happen to have finite binary expansions.\nThose with infinite binary expansions, such as 1/3 (and indeed, any fraction whose denominator\n isn't a power of 2) aren't defined.\n\n$$\n\\begin{gather*}\n {1 \\over 2} = 0.1_2\n \\\\\n {1 \\over 3} = 0.\\overline{01} = 0.\\textcolor{red}{01}\\textcolor{green}{01}\\textcolor{blue}{01}...\n \\\\\n {1 \\over 4} = 0.01_2\n \\\\\n {1 \\over 5} = 0.\\overline{0011} = 0.\\textcolor{red}{0011}\\textcolor{green}{0011}\\textcolor{blue}{0011}...\n \\\\\n \\vdots\n\\end{gather*}\n$$\n\nWe can persevere if we continue to interpret the binary strings as a path in the tree.\nThis means that for 1/3, we go left initially, then alternate between going left and right.\nAs we do so, let's take note of the values we pass along the way:\n\n\n\n::: {#696d9918 .cell .plain execution_count=17}\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \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 Binary fraction\n \n Binary fraction (decimal)\n \n Stern-Brocot rational\n \n Stern-Brocot rational (decimal)\n
\n 0\n \n 1/1\n \n 1.0\n \n 1/1\n \n 1.0\n
\n 1\n \n 1/2\n \n 0.5\n \n 1/2\n \n 0.5\n
\n 2\n \n 1/4\n \n 0.25\n \n 1/3\n \n 0.3333333333333333\n
\n 3\n \n 3/8\n \n 0.375\n \n 2/5\n \n 0.4\n
\n 4\n \n 5/16\n \n 0.3125\n \n 3/8\n \n 0.375\n
\n 5\n \n 11/32\n \n 0.34375\n \n 5/13\n \n 0.38461538461538464\n
\n 6\n \n 21/64\n \n 0.328125\n \n 8/21\n \n 0.38095238095238093\n
\n 7\n \n 43/128\n \n 0.3359375\n \n 13/34\n \n 0.38235294117647056\n
\n 8\n \n 85/256\n \n 0.33203125\n \n 21/55\n \n 0.38181818181818183\n
\n ...\n \n ...\n \n ...\n \n ...\n \n ...\n
\n 100\n \n (too big to show)\n \n 0.3333333333333333\n \n (too big to show)\n \n 0.3819660112501052\n
\n ...\n \n ...\n \n ...\n \n ...\n \n ...\n
\n```\n:::\n:::\n\n\n::: {#230d6b64 .cell layout-ncol='2' execution_count=18}\n\n::: {.cell-output .cell-output-display}\n![Binary convergents of 1/3](index_files/figure-html/cell-18-output-1.svg){}\n:::\n\n::: {.cell-output .cell-output-display}\n![¿ applied to binary convergents of 1/3, which also appear to converge](index_files/figure-html/cell-18-output-2.svg){}\n:::\n:::\n\n\nBoth sequences appear to converge to a number, with the binary fractions obviously converging to 1/3.\nThe rationals from the Stern-Brocot don't appear to be converging to a repeating decimal.\nLooking closer, the numerators and denominators of the fractions appear to come from the Fibonacci numbers.\nIn fact, the quantity that the fractions approach is $2 - \\varphi$, where φ is the golden ratio.\nThis number is the root of the polynomial $x^2 - 3x + 1$.\n\nIn fact, all degree 2 polynomials have roots that are encoded by a repeating path in the Stern-Brocot tree.\nPut another way, ¿ can be extended to map rationals other than binary fractions to quadratic roots\n (and ? maps quadratic roots to rational numbers).\nThis is easier to understand when writing the quantity as its\n [continued fraction expansion](https://en.wikipedia.org/wiki/Continued_fraction),\n but that's an entirely separate discussion.\n\nEither way, it tells us something interesting: not only can all rational numbers be enumerated,\n but so can quadratic *irrationals*.\n\n\n### The Other Side\n\nI'd like to briefly digress from talking about enumerations and mention the right subtree.\nThe question mark function, as defined here, is only defined on numbers between 0 and 1\n (and even then, technically only rational numbers).\nAccording to Wikipedia's definition, the question mark function is quasi-periodic --\n $?(x + 1) = ?(x) + 1$.\nOn the other hand, according to the definition by pairing up the two trees,\n rationals greater than 1 get mapped to binary fractions between 1 and 2.\n\n::: {#fig-question-mark-linlog .cell layout-ncol='2' execution_count=19}\n\n::: {.cell-output .cell-output-display}\n![linear x-axis](index_files/figure-html/fig-question-mark-linlog-output-1.svg){#fig-question-mark-linlog-1}\n:::\n\n::: {.cell-output .cell-output-display}\n![(base 2)-logarithmic x-axis](index_files/figure-html/fig-question-mark-linlog-output-2.svg){#fig-question-mark-linlog-2}\n:::\n\nQuestion mark function including right subtree\n:::\n\n\nHere are graphs describing *our* question mark function, on linear and logarithmic plots.\nInstead of repeating, the function continues its self-similar behavior\n as it proceeds onward to infinity (logarithmically).\nThe right graph stretches from -∞, where its value would be 0, to ∞, where its value would be 2.\n\nPersonally, I like this definition a bit better, if only because it matches other ways\n of thinking about the interval (0, 1).\nFor example,\n\n- In topology, it's common to show that this interval is homeomorphic to the entire real line\n- It's similar to the [rational functions which appear in stereography](/posts/stereo/1/),\n which continue to infinity instead of being periodic\n- It showcases how the Stern-Brocot tree sorts rational numbers by complexity better\n\nHowever, it's also true that different definitions are good for different things.\nFor example, periodicity matches the intuition that numbers can be decomposed\n into a fractional and integral part.\nIntegral parts grow without bound, while fractional parts are periodic,\n just like the function would be.\n\n\nClosing\n-------\n\nI'd like to draw this discussion of enumerating numbers to a close for now.\nI wrote this article to establish some preliminaries regarding *another* post that I have planned.\nOn the other hand, since I was describing the Stern-Brocot tree, I felt it also pertinent\n to show the question mark function, since it's a very interesting self-similar curve.\nEven then, I have shown them as a curiosity instead of giving them their time in the spotlight.\n\nI have omitted some things I would like to have discussed, such as\n [order type](https://en.wikipedia.org/wiki/Order_type),\n and enumerating things beyond just the quadratic irrationals.\nI may return to some of these topics in the future, such as to show a way to order integer polynomials.\n\nDiagrams created with GeoGebra (because trying to render them in LaTeX would have taken too long)\n and Matplotlib.\n\n", + "markdown": "---\ntitle: \"Numbering Numbers: From 0 to ∞\"\ndescription: |\n How do we count an infinitude of numbers?\nformat:\n html:\n html-math-method: katex\ndate: \"2023-11-26\"\ndate-modified: \"2025-07-22\"\ncategories:\n - algebra\n - question-mark function\n - haskell\n---\n\n\n\nThe infinite is replete with paradoxes.\nSome of the best come from comparing sizes of infinite collections.\nFor example, every natural number can be mapped to a (nonnegative) even number and vice versa.\n\n$$\n\\begin{gather*}\n \\N \\rightarrow 2\\N\n \\\\\n n \\mapsto 2n\n \\\\ \\\\\n 0 \\mapsto 0,~ 1 \\mapsto2,~ 2 \\mapsto 4,~ 3 \\mapsto 6,~ 4 \\mapsto 8, ...\n\\end{gather*}\n$$\n\n(For the purposes of this post, $0 \\in \\N, ~ 0 \\notin \\N^+$)\n\nAll even numbers are \"hit\" by this map (by the definition of an even number),\n and no two natural numbers map to the same even number\n (again, more or less by definition, since $2m = 2n$ implies that $m = n$ over $\\N$).\nTherefore, the map is [one-to-one](https://en.wikipedia.org/wiki/Injective_function)\n and [onto](https://en.wikipedia.org/wiki/Surjective_function),\n so the map is a [bijection](https://en.wikipedia.org/wiki/Bijection).\nA consequence is that the map has an inverse, namely by reversing all of the arrows in the above block\n (i.e., the action of halving an even number).\n\nBijections with the natural numbers are easier to understand as a way to place things\n into a linear sequence.\nIn other words, they enumerate \"some sort of item\"; in this case, even numbers.\n\nIn the finite world, a bijection between two things implies that they have the same size.\nIt makes sense to extend the same logic to the infinite world, but there's a catch.\nThe nonnegative even numbers are clearly a strict subset of the natural numbers,\n but by this argument they have the same size.\n\n$$\n\\begin{matrix}\n 2\\N & \\longleftrightarrow & \\N & \\hookleftarrow & 2\\N\n \\\\\n 0 & \\mapsto & \\textcolor{red}0 & \\dashleftarrow & \\textcolor{red}0\n \\\\\n 2 & \\mapsto & 1 & &\n \\\\\n 4 & \\mapsto & \\textcolor{red}2 & \\dashleftarrow & \\textcolor{red}2\n \\\\\n 6 & \\mapsto & 3 & &\n \\\\\n 8 & \\mapsto & \\textcolor{red}4 & \\dashleftarrow & \\textcolor{red}4\n \\\\\n 10 & \\mapsto & 5 & &\n \\\\\n 12 & \\mapsto & \\textcolor{red}6 & \\dashleftarrow & \\textcolor{red}6\n \\\\\n 14 & \\mapsto & 7 & &\n \\\\\n 16 & \\mapsto & \\textcolor{red}8 & \\dashleftarrow & \\textcolor{red}8\n \\\\\n \\vdots & & \\vdots & & \\vdots\n\\end{matrix}\n$$\n\n\nAre we Positive?\n----------------\n\nThe confusion continues if we look at the integers and the naturals.\nIntegers are the natural numbers and their negatives, so it would be intuitive to assume that\n there are twice as many of them as there are naturals (more or less one to account for zero).\nBut since that logic fails for the naturals and the even numbers,\n it fails for the naturals and integers as well.\n\n$$\n\\begin{gather*}\n \\begin{align*}\n \\mathbb{N} &\\rightarrow \\mathbb{Z}\n \\\\\n n &\\mapsto \\left\\{ \\begin{matrix}\n n/2 & n \\text{ even}\n \\\\\n -(n+1)/2 & n \\text{ odd}\n \\end{matrix} \\right.\n \\end{align*}\n \\\\ \\\\\n 0 \\mapsto 0,\\quad 2 \\mapsto 1, \\quad 4 \\mapsto 2, \\quad 6 \\mapsto 3, \\quad 8 \\mapsto 4,~...\n \\\\\n 1 \\mapsto -1, \\quad 3 \\mapsto -2, \\quad 5 \\mapsto -3, \\quad 7 \\mapsto -4, \\quad 9 \\mapsto -5,~...\n\\end{gather*}\n$$\n\nOr, in Haskell[^1]:\n\n[^1]: That is, if you cover your eyes and pretend that `undefined` will never happen,\n and if you ignore that `Int` is bounded, unlike `Integer`.\n\n::: {#775b525e .cell execution_count=2}\n``` {.haskell .cell-code}\ntype Nat = Int\n\nlistIntegers :: Nat -> Int\nlistIntegers n\n | n < 0 = undefined\n | even n = n `div` 2\n | otherwise = -(n + 1) `div` 2\n```\n:::\n\n\nIn other words, this map sends even numbers to the naturals (the inverse of the doubling map)\n and the odds to the negatives.\nThe same arguments about the bijective nature of this map apply as before, and so the paradox persists,\n since naturals are also a strict subset of integers.\n\n\n### Rational Numbers\n\nRationals are a bit worse.\nTo make things a little easier, let's focus on the positive rationals (i.e., fractions excluding 0).\nUnlike the integers, there is no obvious \"next rational\" after (or even before) 1.\nIf there were, we could follow it with its reciprocal, like how an integer is followed\n by its negative in the map above.\n\nOn the other hand, the integers provide a sliver of hope that listing all rational numbers is possible.\nIntegers can be defined as pairs of natural numbers, along with a way of considering two pairs equal.\n\n$$\n\\begin{gather*}\n -1 = (0,1) \\sim_\\Z (1,2) \\sim_\\Z (2,3) \\sim_\\Z (3,4) \\sim_\\Z ...\n \\\\[10pt]\n (a,b) \\sim_\\mathbb{Z} (c,d) \\iff a+d = b+c \\quad a,b,c,d \\in \\mathbb{N}\n \\\\[10pt]\n \\mathbb{Z} := ( \\mathbb{N} \\times \\mathbb{N} ) / \\sim_\\mathbb{Z}\n\\end{gather*}\n$$\n\n::: {#97b6db11 .cell execution_count=3}\n``` {.haskell .cell-code}\nintEqual :: (Nat, Nat) -> (Nat, Nat) -> Bool\nintEqual (a, b) (c, d) = a + d == b + c\n```\n:::\n\n\nThis relation is the same as saying $a - b = c - d$ (i.e., that -1 = 0 - 1, etc.),\n but has the benefit of not requiring subtraction to be defined.\nThis is all the better, since, as grade-schoolers are taught, subtracting a larger natural number\n from a smaller one is impossible.\n\nThe same equivalence definition exists for positive rationals.\nIt is perhaps more familiar, because of the emphasis placed on simplifying fractions when learning them.\nWe can [cross-multiply](https://en.wikipedia.org/wiki/Cross-multiplication) fractions to get\n a similar equality condition to the one for integers.\n\n$$\n\\begin{gather*}\n {1 \\over 2} = (1,2) \\sim_\\mathbb{Q} \\overset{2/4}{(2,4)} \\sim_\\mathbb{Q}\n \\overset{3/6}{(3,6)} \\sim_\\mathbb{Q} \\overset{4/8}{(4,8)} \\sim_\\mathbb{Q} ...\n \\\\ \\\\\n (a,b) \\sim_\\mathbb{Q} (c,d) \\iff ad = bc \\quad a,b,c,d \\in \\mathbb{N}^+\n \\\\ ~ \\\\\n \\mathbb{Q^+} := ( \\mathbb{N^+} \\times \\mathbb{N^+} ) / \\sim_\\mathbb{Q}\n\\end{gather*}\n$$\n\n::: {#4c4d2c94 .cell execution_count=4}\n``` {.haskell .cell-code}\nratEqual :: (Nat, Nat) -> (Nat, Nat) -> Bool\nratEqual (a, b) (c, d) = a * d == b * c\n```\n:::\n\n\nWe specify that neither element of the pair can be zero, so this excludes divisions by zero\n (and the especially tricky case of 0/0, which would be equal to all fractions).\nEffectively, this just replaces where addition appears in the integer equivalence with multiplication.\n\n\n### Eliminating Repeats\n\nNaively, to tackle both of these cases, we might consider enumerating pairs of natural numbers.\nWe order them by sums and break ties by sorting on the first index.\n\n::: {#aa044caa .cell execution_count=5}\n``` {.haskell .cell-code}\n-- All pairs of natural numbers that sum to n\nlistPairs :: Nat -> [(Nat, Nat)]\nlistPairs n = [ (k, n - k) | k <- [0..n] ]\n\n-- \"Triangular\" enumeration of all pairs of positive integers\nallPairs :: [(Nat, Nat)]\nallPairs = concatMap listPairs [0..]\n\n-- Use a natural number to index the enumeration of all pairs\nallPairsMap :: Nat -> (Nat, Nat)\nallPairsMap n = allPairs !! n\n```\n:::\n\n\n::: {#6143624a .cell .plain execution_count=6}\n``` {.haskell .cell-code code-fold=\"true\"}\npairEnumeration = columns (\\(_, f) v -> f v) (\\(l, _) -> Headed l) [\n (\"Index\", show . fst),\n (\"Pair (a, b)\", show . snd),\n (\"Sum (a + b)\", show . uncurry (+) . snd),\n (\"Integer (a - b)\", show . uncurry (-) . snd),\n (\"Rational (a+1 / b+1)\", (\\(a, b) -> show (a + 1) ++ \"/\" ++ show (b + 1)) . snd)\n ]\n\nrenderTable (rmap stringCell pairEnumeration) $ take 10 $ zip [0..] allPairs\n```\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \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 Index\n \n Pair (a, b)\n \n Sum (a + b)\n \n Integer (a - b)\n \n Rational (a+1 / b+1)\n
\n 0\n \n (0,0)\n \n 0\n \n 0\n \n 1/1\n
\n 1\n \n (0,1)\n \n 1\n \n -1\n \n 1/2\n
\n 2\n \n (1,0)\n \n 1\n \n 1\n \n 2/1\n
\n 3\n \n (0,2)\n \n 2\n \n -2\n \n 1/3\n
\n 4\n \n (1,1)\n \n 2\n \n 0\n \n 2/2\n
\n 5\n \n (2,0)\n \n 2\n \n 2\n \n 3/1\n
\n 6\n \n (0,3)\n \n 3\n \n -3\n \n 1/4\n
\n 7\n \n (1,2)\n \n 3\n \n -1\n \n 2/3\n
\n 8\n \n (2,1)\n \n 3\n \n 1\n \n 3/2\n
\n 9\n \n (3,0)\n \n 3\n \n 3\n \n 4/1\n
\n```\n:::\n:::\n\n\nThis certainly works to show that naturals and pairs of naturals can be put into bijection,\n but it when interpreting the results as integers or rationals, we double-count several of them.\nThis is easy to see in the case of the integers, but it will also happen in the rationals.\nFor example, the pair (3, 5) would correspond to 4/6 = 2/3, which has already been counted.\n\nIncidentally, Haskell comes with a function called `nubBy`.\nThis function eliminates duplicates according to another function of our choosing.\nWe can also just implement it ourselves and use it to create a naive enumeration of integers and rationals,\n based on the equalities defined earlier:\n\n::: {#7017b14d .cell execution_count=7}\n``` {.haskell .cell-code}\nnubBy :: (a -> a -> Bool) -> [a] -> [a]\nnubBy f = nubBy' [] where\n nubBy' ys [] = []\n nubBy' ys (z:zs)\n -- Ignore this element, something equivalent is in ys\n | any (f z) ys = nubBy' ys zs\n -- Append this element to the result and our internal list\n | otherwise = z:nubBy' (z:ys) zs\n\nallIntegers :: [(Nat, Nat)]\n-- Remove duplicates under integer equality\nallIntegers = nubBy intEqual allPairs\n\nallIntegersMap :: Nat -> (Nat, Nat)\nallIntegersMap n = allIntegers !! n\n\nallRationals :: [(Nat, Nat)]\n-- Add 1 to the numerator and denominator to get rid of 0,\n-- then remove duplicates under fraction equality\nallRationals = nubBy ratEqual $ map (\\(a,b) -> (a+1, b+1)) allPairs\n\nallRationalsMap :: Nat -> (Nat, Nat)\nallRationalsMap n = allRationals !! n\n```\n:::\n\n\nFor completeness's sake, the resulting pairs of each map are as follows\n\n::: {#95e58f2c .cell .plain execution_count=8}\n``` {.haskell .cell-code code-fold=\"true\"}\ncodeCell = htmlCell . Html.code . Html.string\n\nshowAsInteger p@(a,b) = show p ++ \" = \" ++ show (a - b)\nshowAsRational' p@(a,b) = show a ++ \"/\" ++ show b\nshowAsRational p@(a,b) = show p ++ \" = \" ++ showAsRational' p\n\nmapEnumeration = columns (\\(_, f) v -> f v) (\\(l, _) -> Headed l) [\n (stringCell \"n\", stringCell . show),\n (codeCell \"allIntegersMap n\",\n stringCell . showAsInteger . allIntegersMap),\n (codeCell \"allRationalsMap n\",\n stringCell . showAsRational . allRationalsMap)\n ]\n\nrenderTable mapEnumeration [0..9]\n```\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \n \n \n
\n n\n \n \n allIntegersMap n\n \n \n \n allRationalsMap n\n \n
\n 0\n \n (0,0) = 0\n \n (1,1) = 1/1\n
\n 1\n \n (0,1) = -1\n \n (1,2) = 1/2\n
\n 2\n \n (1,0) = 1\n \n (2,1) = 2/1\n
\n 3\n \n (0,2) = -2\n \n (1,3) = 1/3\n
\n 4\n \n (2,0) = 2\n \n (3,1) = 3/1\n
\n 5\n \n (0,3) = -3\n \n (1,4) = 1/4\n
\n 6\n \n (3,0) = 3\n \n (2,3) = 2/3\n
\n 7\n \n (0,4) = -4\n \n (3,2) = 3/2\n
\n 8\n \n (4,0) = 4\n \n (4,1) = 4/1\n
\n 9\n \n (0,5) = -5\n \n (1,5) = 1/5\n
\n```\n:::\n:::\n\n\nNote that the tuples produced by `allIntegers`, when interpreted as integers, happen to coincide\n with the earlier enumeration given by `listIntegers`.\n\n\nTree of Fractions\n-----------------\n\nThere's an entirely separate structure which contains all rationals in least terms.\nIt relies on an operation between two fractions called the *mediant*.\nFor two rational numbers in least terms *p* and *q*, such that *p* < *q*, the mediant is designated *p* ⊕ *q* and will:\n\n1. also be in least terms (with some exceptions, see below),\n2. be larger than *p*, and\n3. be smaller than *q*\n\n$$\n\\begin{gather*}\n p = {a \\over b} < {c \\over d} = q, \\quad \\gcd(a,b) = \\gcd(c,d) = 1\n \\\\ \\\\\n p < p \\oplus q < q \\quad \\phantom{\\gcd(a+c, b+d) = 1}\n \\\\ \\\\\n {a \\over b} < {a+c \\over b+d} < {c \\over d}, \\quad \\gcd(a+c, b+d) = 1\n\\end{gather*}\n$$\n\nWe know our sequence of rationals starts with 1/1, 1/2, and 2/1.\nIf we start as before with 1/1 and want to get the other quantities,\n then we can take its mediants with 0/1 and 1/0, respectively\n (handwaving the fact that the latter isn't a legitimate fraction).\n\n$$\n\\begin{align*}\n && && \\large{1 \\over 1} && &&\n \\\\\n { \\oplus {0 \\over 1} } && \\large{/} && && \\large{\\backslash} ~ && \\oplus {1 \\over 0}\n \\\\\n && \\large{1 \\over 2} && && \\large{2 \\over 1} &&\n\\end{align*}\n$$\n\nWe might try continuing this pattern by doing the same thing to 1/2.\nWe can take its mediant with 0/1 to get 1/3.\nUnfortunately, the mediant of 1/2 and 1/0 is 2/2 (as is the mediant of 2/1 with 0/1),\n which isn't in least terms, and has already appeared as 1/1.\n\nWe could try another fraction that's appeared in the tree.\nUnfortunately, 2/1 suffers from the same issue as 1/0 -- 1/2 ⊕ 2/1 = 3/3, which is\n the same quantity as before, despite both fractions being in least terms.\nOn the other hand, 1/2 ⊕ 1/1 = 2/3, which is in least terms.\nSimilarly, 2/1 ⊕ 1/1 is 3/2, its reciprocal.\n\n$$\n\\begin{align*}\n && && \\large{1 \\over 2} && &&\n \\\\\n { \\oplus {0 \\over 1} } && \\large{/} && && \\large{\\backslash} ~ && \\oplus {1 \\over 1}\n \\\\\n && \\large{1 \\over 3} && && \\large{2 \\over 3} &&\n\\end{align*}\n\\qquad \\qquad\n\\begin{align*}\n && && \\large{2 \\over 1} && &&\n \\\\\n { \\oplus {1 \\over 1} } && \\large{/} && && \\large{\\backslash} ~ && \\oplus {1 \\over 0}\n \\\\\n && \\large{3 \\over 2} && && \\large{3 \\over 1} &&\n\\end{align*}\n$$\n\nThe trick is to notice that a step to the left \"updates\" what the next step to the right looks like.\nSteps to the right behave symmetrically.\nFor example, in the row we just generated, the left child of 2/3 is its mediant with 1/2,\n its right child is its mediant with 1/1.\n\nContinuing this iteration ad infinitum forms the\n [Stern-Brocot tree](https://en.wikipedia.org/wiki/Stern%E2%80%93Brocot_tree).\nA notable feature of this is that it is a\n [binary search tree](https://en.wikipedia.org/wiki/Binary_search_tree) (of infinite height).\nThis means that for any node, the value at the node is greater than all values in the left subtree\n and less than all values in the right subtree.\n\n![](./stern-brocot_tree.png)\n\nThere's a bit of a lie in presenting the tree like this.\nAs a binary tree, it's most convenient to show the nodes spaced evenly, but the distance between\n 1/1 and 2/1 is not typically seen as the same as the distance between 1/1 and 1/2.\n\nWe can implement this in Haskell using `Data.Tree`.\nThis package actually lets you describe trees with any number of child nodes,\n but we only need two for the sake of the Stern-Brocot tree.\n\n::: {#688b980d .cell execution_count=9}\n``` {.haskell .cell-code}\nimport Data.Tree\n\n-- Make a tree by applying the function `make` to each node\n-- Start with the root value (1, 1), along with\n-- its left and right steps, (0, 1) and (1, 0)\nsternBrocot = unfoldTree make ((1,1), (0,1), (1,0)) where\n -- Place the first value in the tree, then describe the next\n -- values for `make` in a list:\n make (v@(vn, vd), l@(ln, ld), r@(rn, rd))\n = (v, [\n -- the left value, and its left (unchanged) and right steps...\n ((ln + vn, ld + vd), l, v),\n -- and the right value, and its left and right (unchanged) steps\n ((vn + rn, vd + rd), v, r)\n ])\n```\n:::\n\n\n### Cutting the Tree Down\n\nWe're halfway there. All that remains is to read off every value in the tree as a sequence.\nPerhaps the most naive way would be to read off by always following the left or right child.\nUnfortunately, these give some fairly dull sequences.\n\n::: {#5911816b .cell layout-ncol='2' execution_count=10}\n``` {.haskell .cell-code}\ntreePath :: [Int] -> Tree a -> [a]\ntreePath xs (Node y ys)\n -- If we don't have any directions (xs), or the node\n -- has no children (ys), then there's nowhere to go\n | null xs || null ys = [y]\n -- Otherwise, go down subtree \"x\", then recurse with that tree\n -- and the rest of the directions (xs)\n | otherwise = y:treePath (tail xs) (ys !! head xs)\n\n-- Always go left (child 0)\n-- i.e., numbers with numerator 1\nmapM_ print $ take 10 $ treePath (repeat 0) sternBrocot\n\n-- Always go right (child 1)\n-- i.e., numbers with denominator 1\nmapM_ print $ take 10 $ treePath (repeat 1) sternBrocot\n```\n\n::: {.cell-output .cell-output-display}\n```\n(1,1)\n(1,2)\n(1,3)\n(1,4)\n(1,5)\n(1,6)\n(1,7)\n(1,8)\n(1,9)\n(1,10)\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\n(1,1)\n(2,1)\n(3,1)\n(4,1)\n(5,1)\n(6,1)\n(7,1)\n(8,1)\n(9,1)\n(10,1)\n```\n:::\n:::\n\n\nRather than by following paths in the tree, we can instead do a breadth-first search.\nIn other words, we read off each row individually, in order.\nThis gives us our sequence of rational numbers with no repeats.\n\n$$\n\\begin{gather*}\n \\begin{align*}\n \\mathbb{N^+}& ~\\rightarrow~ \\mathbb{Q}\n \\\\\n n & ~\\mapsto~ \\text{bfs}[n]\n \\end{align*}\n \\\\ \\\\\n 1 \\mapsto 1/1,~ \\\\\n 2 \\mapsto 1/2,\\quad 3 \\mapsto 2/1,~ \\\\\n 4 \\mapsto 1/3,\\quad 5 \\mapsto 2/3, \\quad 6 \\mapsto 3/2, \\quad 7 \\mapsto 3/1,~ ...\n\\end{gather*}\n$$\n\nFor convenience, this enumeration is given starting from 1 rather than from 0.\nThis numbering makes it clearer that each row starts with a power of 2,\n since the structure is a binary tree, and the complexity doubles with each row.\nThe enumeration could just as easily start from 0 by starting with $\\N$,\n then getting to $\\N^+$ with $n \\mapsto n+1$.\n\nWe can also write a breadth-first search in Haskell, for posterity:\n\n::: {#91920684 .cell execution_count=11}\n``` {.haskell .cell-code}\nbfs :: Tree a -> [a]\nbfs (Node root children) = bfs' root children where\n -- Place the current node in the list\n bfs' v [] = [v]\n -- Pluck one node off our list of trees, then recurse with\n -- the rest, along with that node's children\n bfs' v ((Node y ys):xs) = v:bfs' y (xs ++ ys)\n\nsternBrocotRationals = bfs sternBrocot\n\nmapM_ putStrLn $ take 10 $ map showAsRational sternBrocotRationals\n```\n\n::: {.cell-output .cell-output-display}\n```\n(1,1) = 1/1\n(1,2) = 1/2\n(2,1) = 2/1\n(1,3) = 1/3\n(2,3) = 2/3\n(3,2) = 3/2\n(3,1) = 3/1\n(1,4) = 1/4\n(2,5) = 2/5\n(3,5) = 3/5\n```\n:::\n:::\n\n\nThe entries in this enumeration have already been given.\n\n\n### Another Tree\n\nAnother tree of fractions to consider is the tree of binary fractions.\nThese fractions simply consist of odd numbers divided by powers of two.\nThe most convenient way to organize these into a tree is to keep denominators equal\n if the nodes have the same depth from the root.\nWe also stipulate that we arrange the nodes as a binary search tree, like the Stern-Brocot tree.\n\nThe tree starts from 1/1 as before.\nIts children have denominator 2, so we have 1/2 to the left and 3/2 to the right.\nThis is equivalent to subtracting 1/2 for the left step and adding 1/2 for the right step.\nAt the next layer, we want fractions with denominator 1/4, and do similarly.\nIn terms of adding and subtracting, we just use 1/4 instead of 1/2.\n\n![](./dyadic_fraction_tree.png)\n\nWe can describe this easily in Haskell:\n\n::: {#555c1dba .cell execution_count=12}\n``` {.haskell .cell-code}\n-- Start with 1/1 (i.e., (1, 1))\nbinFracTree = unfoldTree make (1,1) where\n -- Place the first value in the tree, then describe the next\n -- values for `make` in a list:\n make v@(vn, vd)\n = (v, [\n -- double the numerator and denominator, then subtract 1 from the numerator\n (2*vn - 1, 2*vd),\n -- same, but add 1 to the numerator instead\n (2*vn + 1, 2*vd)\n ])\n```\n:::\n\n\nThe entries of this tree have an additional interpretation when converted to their binary expansions.\nThese fractions always terminate in a \"1\" in binary, but ignoring this final entry, starting from the root\n and following \"left\" for 0 and \"right\" for 1 places us at that fraction in the tree.\nIn other words, the binary expansions encode the path from the root to the node.\n\n![](./binary_expansion_tree.png)\n\n\nWhy Bother?\n-----------\n\nThe tree of binary fractions and the Stern-Brocot tree are both infinite binary search trees,\n so we might imagine overlaying one tree over the other, pairing up the individual entries.\n\n![](./question_mark_tree.png)\n\nIn Haskell, we can pair up entries recursively:\n\n::: {#82577de7 .cell execution_count=13}\n``` {.haskell .cell-code}\nzipTree :: Tree a -> Tree b -> Tree (a,b)\n-- Pair the values in the nodes together, then recurse with the child trees\nzipTree (Node x xs) (Node y ys) = Node (x,y) $ zipWith zipTree xs ys\n\nbinarySBTree = zipTree sternBrocot binFracTree\n```\n:::\n\n\nConveniently, both left subtrees of the root fall in the interval (0, 1).\nIt also pairs up 1 and 1/2 with themselves.\nDoing so establishes a bijection between the rationals and the binary rationals in that interval.\nRationals are more continuous than integers, so it might be of some curiosity to plot this function.\nWe only have to look at a square over the unit interval. Doing so reveals a curious shape:\n\n::: {#66c88259 .cell layout-ncol='2' execution_count=14}\n``` {.haskell .cell-code code-fold=\"true\"}\nimport Data.Tuple (swap)\nimport Data.List (sort)\nimport Data.Bifunctor (bimap, first)\n\nleftSubtree (Node _ (x:_)) = x\n\n-- Divide entries of the (zipped) trees\n() (a,b) = fromIntegral a / fromIntegral b :: Double\nbinarySBDoubles n = take n $ map (bimap () ()) $ bfs $ leftSubtree binarySBTree\n\n(MPL.tightLayout <>) $ uncurry MPL.plot $ unzip $ sort $ map swap $ binarySBDoubles 250\n(MPL.tightLayout <>) $ uncurry MPL.plot $ unzip $ sort $ binarySBDoubles 250\n```\n\n::: {.cell-output .cell-output-display}\n![Binary rationals on the x-axis, rationals on the y-axis](index_files/figure-html/cell-15-output-1.svg){}\n:::\n\n::: {.cell-output .cell-output-display}\n![Rationals on the x-axis, binary rationals on the y-axis](index_files/figure-html/cell-15-output-2.svg){}\n:::\n:::\n\n\nThe plot on the right which maps the rationals to the binary rationals is known as\n [Minkowski's question mark function](https://en.wikipedia.org/wiki/Minkowski%27s_question-mark_function).\nNotice that this function is nearly 1/2 for values near 1/2\n (nearly 1/4 for values near 1/3, nearly 1/8 for values near 1/4, etc.).\n\n\n### I'm Repeating Myself\n\nThe inverse question mark map (which I'll call ¿ for short), besides mapping binary rationals to rationals,\n has an interesting relationship with other rational numbers.\nRecall that we only defined the function in terms of fractions\n which happen to have finite binary expansions.\nThose with infinite binary expansions, such as 1/3 (and indeed, any fraction whose denominator\n isn't a power of 2) aren't defined.\n\n$$\n\\begin{gather*}\n {1 \\over 2} = 0.1_2\n \\\\\n {1 \\over 3} = 0.\\overline{01} = 0.\\textcolor{red}{01}\\textcolor{green}{01}\\textcolor{blue}{01}...\n \\\\\n {1 \\over 4} = 0.01_2\n \\\\\n {1 \\over 5} = 0.\\overline{0011} = 0.\\textcolor{red}{0011}\\textcolor{green}{0011}\\textcolor{blue}{0011}...\n \\\\\n \\vdots\n\\end{gather*}\n$$\n\nWe can persevere if we continue to interpret the binary strings as a path in the tree.\nThis means that for 1/3, we go left initially, then alternate between going left and right.\nAs we do so, let's take note of the values we pass along the way:\n\n::: {#a0b1bf2f .cell execution_count=15}\n``` {.haskell .cell-code}\n-- Follow the path described by the binary expansion of 1/3\noneThirdPath = treePath (0:cycle [0,1]) $ zipTree sternBrocot binFracTree\n```\n:::\n\n\n::: {#f54ff78f .cell .plain execution_count=16}\n``` {.haskell .cell-code code-fold=\"true\"}\ntrimTo n x = if length x > n then \"(too big to show)\" else x\n\ntreePathColumns = columns (\\(_, f) v -> f v) (\\(l, _) -> Headed l) [\n (stringCell \"n\",\n stringCell . fromEither . fmap show),\n (stringCell \"Binary fraction\",\n stringCell . fromEither . fmap (trimTo 10 . showAsRational' . snd . (oneThirdPath !!))),\n (stringCell \"Binary fraction (decimal)\",\n stringCell . fromEither . fmap (show . () . snd . (oneThirdPath !!))),\n (stringCell \"Stern-Brocot rational\",\n stringCell . fromEither . fmap (trimTo 10 . showAsRational' . fst . (oneThirdPath !!))),\n (stringCell \"Stern-Brocot rational (decimal)\",\n stringCell . fromEither . fmap (show . () . fst . (oneThirdPath !!)))\n ] where\n fromEither = either id id\n\nrenderTable treePathColumns (map Right [0..8] ++ [Left \"...\", Right 100, Left \"...\"])\n```\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \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 Binary fraction\n \n Binary fraction (decimal)\n \n Stern-Brocot rational\n \n Stern-Brocot rational (decimal)\n
\n 0\n \n 1/1\n \n 1.0\n \n 1/1\n \n 1.0\n
\n 1\n \n 1/2\n \n 0.5\n \n 1/2\n \n 0.5\n
\n 2\n \n 1/4\n \n 0.25\n \n 1/3\n \n 0.3333333333333333\n
\n 3\n \n 3/8\n \n 0.375\n \n 2/5\n \n 0.4\n
\n 4\n \n 5/16\n \n 0.3125\n \n 3/8\n \n 0.375\n
\n 5\n \n 11/32\n \n 0.34375\n \n 5/13\n \n 0.38461538461538464\n
\n 6\n \n 21/64\n \n 0.328125\n \n 8/21\n \n 0.38095238095238093\n
\n 7\n \n 43/128\n \n 0.3359375\n \n 13/34\n \n 0.38235294117647056\n
\n 8\n \n 85/256\n \n 0.33203125\n \n 21/55\n \n 0.38181818181818183\n
\n ...\n \n ...\n \n ...\n \n ...\n \n ...\n
\n 100\n \n (too big to show)\n \n 0.3333333333333333\n \n (too big to show)\n \n 0.3819660112501052\n
\n ...\n \n ...\n \n ...\n \n ...\n \n ...\n
\n```\n:::\n:::\n\n\n::: {#9f7b9346 .cell layout-ncol='2' execution_count=17}\n``` {.haskell .cell-code code-fold=\"true\"}\nconvergentsOneThird = map (() . snd) oneThirdPath\nconvergentsSBNumber = map (() . fst) oneThirdPath\n\nplotSequence n = uncurry MPL.plot . unzip . take n . zip ([0..] :: [Int])\n\n(MPL.tightLayout <>) $ plotSequence 20 convergentsOneThird\n(MPL.tightLayout <>) $ plotSequence 20 convergentsSBNumber\n```\n\n::: {.cell-output .cell-output-display}\n![Binary convergents of 1/3](index_files/figure-html/cell-18-output-1.svg){}\n:::\n\n::: {.cell-output .cell-output-display}\n![¿ applied to binary convergents of 1/3, which also appear to converge](index_files/figure-html/cell-18-output-2.svg){}\n:::\n:::\n\n\nBoth sequences appear to converge to a number, with the binary fractions obviously converging to 1/3.\nThe rationals from the Stern-Brocot don't appear to be converging to a repeating decimal.\nLooking closer, the numerators and denominators of the fractions appear to come from the Fibonacci numbers.\nIn fact, the quantity that the fractions approach is $2 - \\varphi$, where φ is the golden ratio.\nThis number is the root of the polynomial $x^2 - 3x + 1$.\n\nIn fact, all degree 2 polynomials have roots that are encoded by a repeating path in the Stern-Brocot tree.\nPut another way, ¿ can be extended to map rationals other than binary fractions to quadratic roots\n (and ? maps quadratic roots to rational numbers).\nThis is easier to understand when writing the quantity as its\n [continued fraction expansion](https://en.wikipedia.org/wiki/Continued_fraction),\n but that's an entirely separate discussion.\n\nEither way, it tells us something interesting: not only can all rational numbers be enumerated,\n but so can quadratic *irrationals*.\n\n\n### The Other Side\n\nI'd like to briefly digress from talking about enumerations and mention the right subtree.\nThe question mark function, as defined here, is only defined on numbers between 0 and 1\n (and even then, technically only rational numbers).\nAccording to Wikipedia's definition, the question mark function is quasi-periodic --\n $?(x + 1) = ?(x) + 1$.\nOn the other hand, according to the definition by pairing up the two trees,\n rationals greater than 1 get mapped to binary fractions between 1 and 2.\n\n::: {#fig-question-mark-linlog .cell layout-ncol='2' execution_count=18}\n``` {.haskell .cell-code code-fold=\"true\"}\nbinarySBDoublesAll n = take n $ map (bimap () ()) $ bfs binarySBTree\n\n(MPL.tightLayout <>) $ uncurry MPL.plot $\n unzip $ sort $ binarySBDoublesAll 250\n(MPL.tightLayout <>) $ uncurry MPL.plot $\n unzip $ map (first log) $ sort $ binarySBDoublesAll 250\n```\n\n::: {.cell-output .cell-output-display}\n![linear x-axis](index_files/figure-html/fig-question-mark-linlog-output-1.svg){#fig-question-mark-linlog-1}\n:::\n\n::: {.cell-output .cell-output-display}\n![(base 2)-logarithmic x-axis](index_files/figure-html/fig-question-mark-linlog-output-2.svg){#fig-question-mark-linlog-2}\n:::\n\nQuestion mark function including right subtree\n:::\n\n\nHere are graphs describing *our* question mark function, on linear and logarithmic plots.\nInstead of repeating, the function continues its self-similar behavior\n as it proceeds onward to infinity (logarithmically).\nThe right graph stretches from -∞, where its value would be 0, to ∞, where its value would be 2.\n\nPersonally, I like this definition a bit better, if only because it matches other ways\n of thinking about the interval (0, 1).\nFor example,\n\n- In topology, it's common to show that this interval is homeomorphic to the entire real line\n- It's similar to the [rational functions which appear in stereography](/posts/stereo/1/),\n which continue to infinity instead of being periodic\n- It showcases how the Stern-Brocot tree sorts rational numbers by complexity better\n\nHowever, it's also true that different definitions are good for different things.\nFor example, periodicity matches the intuition that numbers can be decomposed\n into a fractional and integral part.\nIntegral parts grow without bound, while fractional parts are periodic,\n just like the function would be.\n\n\nClosing\n-------\n\nI'd like to draw this discussion of enumerating numbers to a close for now.\nI wrote this article to establish some preliminaries regarding *another* post that I have planned.\nOn the other hand, since I was describing the Stern-Brocot tree, I felt it also pertinent\n to show the question mark function, since it's a very interesting self-similar curve.\nEven then, I have shown them as a curiosity instead of giving them their time in the spotlight.\n\nI have omitted some things I would like to have discussed, such as\n [order type](https://en.wikipedia.org/wiki/Order_type),\n and enumerating things beyond just the quadratic irrationals.\nI may return to some of these topics in the future, such as to show a way to order integer polynomials.\n\nDiagrams created with GeoGebra (because trying to render them in LaTeX would have taken too long)\n and Matplotlib.\n\n", "supporting": [ "index_files" ], diff --git a/_freeze/posts/number-number/1/index/figure-html/cell-15-output-1.svg b/_freeze/posts/number-number/1/index/figure-html/cell-15-output-1.svg index cc205e6..f7799ee 100644 --- a/_freeze/posts/number-number/1/index/figure-html/cell-15-output-1.svg +++ b/_freeze/posts/number-number/1/index/figure-html/cell-15-output-1.svg @@ -6,7 +6,7 @@ - 2025-07-30T03:24:44.195833 + 2025-07-30T17:57:28.746680 image/svg+xml @@ -41,12 +41,12 @@ z - - + @@ -91,7 +91,7 @@ z - + @@ -132,7 +132,7 @@ z - + @@ -168,7 +168,7 @@ z - + @@ -215,7 +215,7 @@ z - + @@ -271,7 +271,7 @@ z - + @@ -304,12 +304,12 @@ z - - + @@ -324,7 +324,7 @@ L -3.5 0 - + @@ -339,7 +339,7 @@ L -3.5 0 - + @@ -388,7 +388,7 @@ z - + @@ -403,7 +403,7 @@ z - + @@ -445,7 +445,7 @@ z - + @@ -460,7 +460,7 @@ z - + @@ -487,7 +487,7 @@ z - + @@ -502,7 +502,7 @@ z - + @@ -711,7 +711,7 @@ L 417.607123 38.931316 L 420.541402 34.969256 L 423.475682 28.530909 L 423.475682 28.530909 -" clip-path="url(#pcdb8677c09)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/> +" clip-path="url(#p6dddd11e9f)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/> + diff --git a/_freeze/posts/number-number/1/index/figure-html/cell-15-output-2.svg b/_freeze/posts/number-number/1/index/figure-html/cell-15-output-2.svg index d1c0066..651efb3 100644 --- a/_freeze/posts/number-number/1/index/figure-html/cell-15-output-2.svg +++ b/_freeze/posts/number-number/1/index/figure-html/cell-15-output-2.svg @@ -6,7 +6,7 @@ - 2025-07-30T03:24:45.626240 + 2025-07-30T17:57:30.086732 image/svg+xml @@ -41,12 +41,12 @@ z - - + @@ -105,7 +105,7 @@ z - + @@ -146,7 +146,7 @@ z - + @@ -195,7 +195,7 @@ z - + @@ -231,7 +231,7 @@ z - + @@ -273,7 +273,7 @@ z - + @@ -320,7 +320,7 @@ z - + @@ -347,7 +347,7 @@ z - + @@ -403,7 +403,7 @@ z - + @@ -452,12 +452,12 @@ z - - + @@ -472,7 +472,7 @@ L -3.5 0 - + @@ -487,7 +487,7 @@ L -3.5 0 - + @@ -502,7 +502,7 @@ L -3.5 0 - + @@ -517,7 +517,7 @@ L -3.5 0 - + @@ -532,7 +532,7 @@ L -3.5 0 - + @@ -718,7 +718,7 @@ L 409.458854 32.885347 L 414.798598 30.708128 L 423.475682 28.530909 L 423.475682 28.530909 -" clip-path="url(#p5e155994b3)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/> +" clip-path="url(#p34cae5d2d5)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/> + diff --git a/_freeze/posts/number-number/1/index/figure-html/cell-18-output-1.svg b/_freeze/posts/number-number/1/index/figure-html/cell-18-output-1.svg index ed4ea11..4b9060e 100644 --- a/_freeze/posts/number-number/1/index/figure-html/cell-18-output-1.svg +++ b/_freeze/posts/number-number/1/index/figure-html/cell-18-output-1.svg @@ -6,7 +6,7 @@ - 2025-07-30T03:24:47.581890 + 2025-07-30T17:57:32.094511 image/svg+xml @@ -41,12 +41,12 @@ z - - + @@ -91,7 +91,7 @@ z - + @@ -157,7 +157,7 @@ z - + @@ -172,7 +172,7 @@ z - + @@ -199,7 +199,7 @@ z - + @@ -231,7 +231,7 @@ z - + @@ -247,7 +247,7 @@ z - + @@ -263,7 +263,7 @@ z - + @@ -281,12 +281,12 @@ z - - + @@ -335,7 +335,7 @@ z - + @@ -371,7 +371,7 @@ z - + @@ -386,7 +386,7 @@ z - + @@ -433,7 +433,7 @@ z - + @@ -448,7 +448,7 @@ z - + @@ -504,7 +504,7 @@ z - + @@ -551,7 +551,7 @@ z - + @@ -585,7 +585,7 @@ L 364.867309 273.348939 L 384.403433 273.346137 L 403.939557 273.347538 L 423.475682 273.346837 -" clip-path="url(#pa627a7ffc5)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/> +" clip-path="url(#pe6fd9f8a67)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/> + diff --git a/_freeze/posts/number-number/1/index/figure-html/cell-18-output-2.svg b/_freeze/posts/number-number/1/index/figure-html/cell-18-output-2.svg index e7513b5..f77ed42 100644 --- a/_freeze/posts/number-number/1/index/figure-html/cell-18-output-2.svg +++ b/_freeze/posts/number-number/1/index/figure-html/cell-18-output-2.svg @@ -6,7 +6,7 @@ - 2025-07-30T03:24:48.966696 + 2025-07-30T17:57:33.434057 image/svg+xml @@ -41,12 +41,12 @@ z - - + @@ -91,7 +91,7 @@ z - + @@ -157,7 +157,7 @@ z - + @@ -172,7 +172,7 @@ z - + @@ -199,7 +199,7 @@ z - + @@ -231,7 +231,7 @@ z - + @@ -247,7 +247,7 @@ z - + @@ -263,7 +263,7 @@ z - + @@ -281,12 +281,12 @@ z - - + @@ -335,7 +335,7 @@ z - + @@ -371,7 +371,7 @@ z - + @@ -386,7 +386,7 @@ z - + @@ -433,7 +433,7 @@ z - + @@ -448,7 +448,7 @@ z - + @@ -504,7 +504,7 @@ z - + @@ -551,7 +551,7 @@ z - + @@ -585,7 +585,7 @@ L 364.867309 283.857633 L 384.403433 283.857595 L 403.939557 283.857609 L 423.475682 283.857604 -" clip-path="url(#pd6aa75d2de)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/> +" clip-path="url(#p415d5a15a1)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/> + diff --git a/_freeze/posts/number-number/1/index/figure-html/fig-question-mark-linlog-output-1.svg b/_freeze/posts/number-number/1/index/figure-html/fig-question-mark-linlog-output-1.svg index eaaf67d..14390d8 100644 --- a/_freeze/posts/number-number/1/index/figure-html/fig-question-mark-linlog-output-1.svg +++ b/_freeze/posts/number-number/1/index/figure-html/fig-question-mark-linlog-output-1.svg @@ -6,7 +6,7 @@ - 2025-07-30T03:24:50.499195 + 2025-07-30T17:57:34.918996 image/svg+xml @@ -41,12 +41,12 @@ z - - + @@ -82,7 +82,7 @@ z - + @@ -111,7 +111,7 @@ z - + @@ -150,7 +150,7 @@ z - + @@ -197,7 +197,7 @@ z - + @@ -231,7 +231,7 @@ z - + @@ -271,7 +271,7 @@ z - + @@ -316,7 +316,7 @@ z - + @@ -343,12 +343,12 @@ z - - + @@ -373,7 +373,7 @@ z - + @@ -389,7 +389,7 @@ z - + @@ -405,7 +405,7 @@ z - + @@ -421,7 +421,7 @@ z - + @@ -437,7 +437,7 @@ z - + @@ -453,7 +453,7 @@ z - + @@ -469,7 +469,7 @@ z - + @@ -485,7 +485,7 @@ z - + @@ -635,7 +635,7 @@ L 315.494194 35.062566 L 369.484938 30.708128 L 423.475682 28.530909 L 423.475682 28.530909 -" clip-path="url(#p73b8bb224c)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/> +" clip-path="url(#p3e78a3e766)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/> + diff --git a/_freeze/posts/number-number/1/index/figure-html/fig-question-mark-linlog-output-2.svg b/_freeze/posts/number-number/1/index/figure-html/fig-question-mark-linlog-output-2.svg index 8eeb40a..db25bb9 100644 --- a/_freeze/posts/number-number/1/index/figure-html/fig-question-mark-linlog-output-2.svg +++ b/_freeze/posts/number-number/1/index/figure-html/fig-question-mark-linlog-output-2.svg @@ -6,7 +6,7 @@ - 2025-07-30T03:24:51.868847 + 2025-07-30T17:57:36.284432 image/svg+xml @@ -41,12 +41,12 @@ z - - + @@ -123,7 +123,7 @@ z - + @@ -180,7 +180,7 @@ z - + @@ -196,7 +196,7 @@ z - + @@ -212,7 +212,7 @@ z - + @@ -227,7 +227,7 @@ z - + @@ -242,7 +242,7 @@ z - + @@ -257,7 +257,7 @@ z - + @@ -272,7 +272,7 @@ z - + @@ -289,12 +289,12 @@ z - - + @@ -310,7 +310,7 @@ L -3.5 0 - + @@ -326,7 +326,7 @@ L -3.5 0 - + @@ -342,7 +342,7 @@ L -3.5 0 - + @@ -370,7 +370,7 @@ z - + @@ -386,7 +386,7 @@ z - + @@ -402,7 +402,7 @@ z - + @@ -418,7 +418,7 @@ z - + @@ -434,7 +434,7 @@ z - + @@ -614,7 +614,7 @@ L 401.237608 32.885347 L 409.261115 30.708128 L 423.475682 28.530909 L 423.475682 28.530909 -" clip-path="url(#p1ecf935b40)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/> +" clip-path="url(#pde11ae1218)" style="fill: none; stroke: #1f77b4; stroke-width: 1.5; stroke-linecap: square"/> + diff --git a/_freeze/posts/number-number/2/index/execute-results/html.json b/_freeze/posts/number-number/2/index/execute-results/html.json index d4693b3..64438b7 100644 --- a/_freeze/posts/number-number/2/index/execute-results/html.json +++ b/_freeze/posts/number-number/2/index/execute-results/html.json @@ -2,7 +2,7 @@ "hash": "f1d1f6321eb69b507eacbbdbfb621264", "result": { "engine": "jupyter", - "markdown": "---\ntitle: \"Numbering Numbers, Part 2: Ordering Obliquely\"\ndescription: |\n How do we construct an irrational number from rational ones?\nformat:\n html:\n html-math-method: katex\ndate: \"2023-12-31\"\ndate-modified: \"2025-07-29\"\ncategories:\n - haskell\n - diagonal argument\n---\n\n\n\nThis post assumes you have read the [previous one](./1), which discusses the creation of sequences\n containing every integer, every binary fraction, and in fact, every fraction.\nFrom the fractions, we also could enumerate quadratic irrational numbers using the *question-mark function*.\n\n\nOther Irrationals\n-----------------\n\nBecause rationals -- and even some irrationals -- can be enumerated, we might imagine\n that it would be nice to enumerate *all* irrational numbers.\nUnfortunately, we're not very lucky this time.\n\nLet's start by making what we mean by \"number\" more concrete\nWe've already been exposed to infinite expansions, like 1/3 in binary (and conveniently, also decimal).\nIn discussing the question mark function, I mentioned the utility of repeating expansions as a means of\n accessing quadratic rationals in the Stern-Brocot tree.\nA general sequence need not repeat, so we can choose to treat these extra \"infinite expansions\" as numbers.\nProving that a rational number must have a repeating expansion is difficult, but if we accept this premise,\n then the new non-repeating expansions are our irrationals.\nFor example, $2 - \\varphi \\approx 0.381966...$, which we encountered last time, is such a number.\n\nDoing this introduces a number of headaches, the least of which is that of\n the definition of arithmetic on such quantities.\nHowever, we need only be concerned with the contents of these sequences to show why\n we can't list out all irrationals.\n\n\n### Diagonalization\n\nWe can narrow our focus to the interval between 0 and 1, since the numbers outside this interval\n are their reciprocals.\nNow (if we agree to use base ten), \"all numbers between 0 and 1\" as we've defined them begin with \"0.\",\n followed by an infinite sequence of digits 0-9.\nSuppose that we have an enumeration of every infinite sequence on this list -- no sequence is left out.\n[Cantor's diagonal argument](https://en.wikipedia.org/wiki/Cantor%27s_diagonal_argument) shows that\n a new sequence can be found by taking the sequence on the diagonal\n and changing each individual digit to another one.\nThe new sequence differs in at least one place from every element on the list,\n so the new sequence cannot be on the list.\nTherefore, such an enumeration cannot exist.\n\nThis is illustrated for binary sequences in this diagram:\n\n\n\n\nIt's fairly common to show this argument without much further elaboration,\n but there are a few problems with doing so:\n\n- We're using infinite sequences of digits, not numbers.\n - Equality between sequences is defined by having all elements coincide.\n- We assume we have an enumeration of sequences to which the argument applies.\n The contents of the enumeration are a mystery.\n- We have no idea which sequences are rational numbers, or if we'd construct one\n by applying the diagonal argument.\n\n\n### Equality\n\nThe purpose of the diagonal argument is to produce a new sequence which was not previously enumerated.\nThe sequence is different in all positions, but what we actually want is equality with respect to the base.\nIn base ten, we have the peculiar identity that\n [$0.\\overline{9} = 1$](https://en.wikipedia.org/wiki/0.999...).\nThis means that if the diagonal argument (applied to base ten sequences) constructs a new sequence\n with the digit 9 repeating forever, it might be equivalent to a sequence which was already in the list:\n\n::: {#5805c2a5 .cell .plain execution_count=3}\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \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 Sequence\n \n 0\n \n 1\n \n 2\n \n 3\n \n 4\n \n 5\n \n ...\n
\n 0\n \n \n 1\n \n \n 2\n \n 3\n \n 4\n \n 5\n \n 6\n \n ...\n
\n 1\n \n 9\n \n \n 2\n \n \n 4\n \n 8\n \n 3\n \n 7\n \n ...\n
\n 2\n \n 2\n \n 2\n \n \n 8\n \n \n 2\n \n 2\n \n 2\n \n ...\n
\n \n 3\n \n \n \n 2\n \n \n \n 3\n \n \n \n 9\n \n \n \n 3\n \n \n \n 9\n \n \n \n 9\n \n \n \n ...\n \n
\n 4\n \n 9\n \n 4\n \n 9\n \n 4\n \n \n 9\n \n \n 4\n \n ...\n
\n 5\n \n 8\n \n 1\n \n 2\n \n 5\n \n 7\n \n \n 9\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 2\n \n \n \n 3\n \n \n \n 9\n \n \n \n 4\n \n \n \n 0\n \n \n \n 0\n \n \n \n ...\n \n
\n```\n\nA case in which the diagonal argument could construct a number already on the list.\n:::\n:::\n\n\nIn the above table, sequence 3 is assumed to continue with 9's forever.\nThe new sequence ω comes from taking the red digits along the diagonal and mapping each digit\n to the next one (mod ten).\nIn the enumeration given, the diagonal continues with 9's forever, so we end up with 0's forever in ω.\n\n\n### Picking a Sequence and Ensuring Rationals\n\n\"No problem, just pick another enumeration,\" you might say.\nIndeed, the example given relies on a simple function and an enumeration\n to which it is particularly ill-suited.\n\nInstead, let's focus on something we *can* do.\nInstead of assuming we have all irrational numbers listed out already, let's start smaller.\nAs of last post, we already have several ways to enumerate all rational numbers between 0 and 1.\nWe can take this enumeration and convert each rational number to positional expansions in a base.\nThen after applying the diagonal argument, the resulting quantity *should* be an irrational number.\n\nFor convenience, we'll use binary expansions of rationals as our sequences.\nThat way, to get a unique sequence on the diagonal, we only have to worry\n about changing \"0\"s to \"1\"s (and vice versa).\nSince we have less flexibility in our digits, it also relieves us from some of the responsibility\n of finding a \"good\" function, like in the decimal case.\nHowever, it's still possible the argument constructs a number already equal to something on the list.\n\n\nInto Silicon\n------------\n\nBefore going any further, let's write a function for applying the diagonal argument to\n a list of binary sequences.\nThis can be implemented in Haskell fairly easily:\n\n\n\nNothing about this function is specific to \"binary sequences\", since `Int` contains values\n other than `1` and `0`.\nIt's just more intuitive to work with them instead of \"`True`\" and \"`False`\"\n (since `Bool` actually does have 2 values).\nYou can replace `(1 -)` with `not` to get a similar function for the type `Bool`.\n\nWe also need a function to get a rational number's binary expansion.\nThis is simple if you recall how to do long division.\nWe try to divide the numerator by the denominator, \"emit\" the quotient as part of the result list,\n then continue with the remainder (and the denominator is unchanged).\nIt's not *quite* that simple, since we also need to \"bring down\" more zeroes.\nIn binary, we can add more zeroes to an expansion by just multiplying by 2.\n\n::: {#852dbb85 .cell layout-ncol='2' execution_count=5}\n\n::: {.cell-output .cell-output-display}\n```\nx = 1 / 3 = 0.010101010...\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\ny = 1 / 5 = 0.001100110...\n```\n:::\n:::\n\n\nThis function gives us the leading 0 (actually the integer part of the quotient of `n` by `d`),\n but we can peel it off by applying `tail`.\n\nSince we intend to interpret these sequences as binary numbers, we might as well also\n convert this into a form we recognize as a number.\nAll we need to do is take a weighted sum of each sequence by its binary place value.\n\n::: {#65218a76 .cell execution_count=6}\n\n::: {.cell-output .cell-output-display}\n```\n0.2\n```\n:::\n:::\n\n\nThe precision `p` here is mostly useless, since we intend to take this to as far as `Double` will go.\n`p` = 100 will do for most sequences, since it's rare that we'll encounter more than a few zeroes\n at the beginning of a sequence.\n\n\nSome Enumerations\n-----------------\n\nNow, for the sake of argument, let's look at an enumeration that fails.\nUsing the tree of binary fractions from the last post, we use a breadth-first search to create\n a list of terminating binary expansions.\n\n![\n Tree of binary fractions, as the tree of terminating binary expansions.
\n We perform a BFS on the left subtree to obtain the values between 0 and 1.\n](../1/binary_expansion_tree.png)\n\n\n\n::: {#d68623fb .cell .plain execution_count=8}\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \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 \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 Sequence\n \n Fraction\n \n 0\n \n 1\n \n 2\n \n 3\n \n 4\n \n 5\n \n 6\n \n 7\n \n ...\n
\n 0\n \n 1/2\n \n \n 1\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 1\n \n 1/4\n \n 0\n \n \n 1\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 2\n \n 3/4\n \n 1\n \n 1\n \n \n 0\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 3\n \n 1/8\n \n 0\n \n 0\n \n 1\n \n \n 0\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 4\n \n 3/8\n \n 0\n \n 1\n \n 1\n \n 0\n \n \n 0\n \n \n 0\n \n 0\n \n 0\n \n ...\n
\n 5\n \n 5/8\n \n 1\n \n 0\n \n 1\n \n 0\n \n 0\n \n \n 0\n \n \n 0\n \n 0\n \n ...\n
\n 6\n \n 7/8\n \n 1\n \n 1\n \n 1\n \n 0\n \n 0\n \n 0\n \n \n 0\n \n \n 0\n \n ...\n
\n 7\n \n 1/16\n \n 0\n \n 0\n \n 0\n \n 1\n \n 0\n \n 0\n \n 0\n \n \n 0\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 0\n \n 0\n \n 1\n \n 1\n \n 1\n \n 1\n \n 1\n \n 1\n \n ...\n
\n```\n:::\n:::\n\n\nComputing the diagonal sequence, it quickly becomes apparent that we're going\n to keep getting \"0\"s along the diagonal.\nThis is because we're effectively just counting in binary some number of digits to the right.\nThe length of a binary expansion grows logarithmically, but going down the diagonal is a linear process.\nIn other words, we can't count fast enough by just adding 1 (or adding any number, really).\n\nEven worse than this, we get a sequence which is equal to sequence 1 as a binary expansion.\nWe can't even rely on the diagonal argument to give us a new number that *isn't* equal\n to a binary fraction.\n\n\n### Stern-Brocot Diagonal\n\nThe Stern-Brocot tree contains more than just binary fractions, so we're bound to encounter more than\n \"0\" forever when running along the diagonal.\nAgain, looking at the left subtree, we can read off fractions between 0 and 1.\n\n![\n Stern-Brocot tree\n](../1/stern-brocot_tree.png)\n\nWe end up with the following enumeration:\n\n::: {#b59f03fa .cell .plain execution_count=9}\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \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 \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 Sequence\n \n Fraction\n \n 0\n \n 1\n \n 2\n \n 3\n \n 4\n \n 5\n \n 6\n \n 7\n \n ...\n
\n 0\n \n 1/2\n \n \n 1\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 1\n \n 1/3\n \n 0\n \n \n 1\n \n \n 0\n \n 1\n \n 0\n \n 1\n \n 0\n \n 1\n \n ...\n
\n 2\n \n 2/3\n \n 1\n \n 0\n \n \n 1\n \n \n 0\n \n 1\n \n 0\n \n 1\n \n 0\n \n ...\n
\n 3\n \n 1/4\n \n 0\n \n 1\n \n 0\n \n \n 0\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 4\n \n 2/5\n \n 0\n \n 1\n \n 1\n \n 0\n \n \n 0\n \n \n 1\n \n 1\n \n 0\n \n ...\n
\n 5\n \n 3/5\n \n 1\n \n 0\n \n 0\n \n 1\n \n 1\n \n \n 0\n \n \n 0\n \n 1\n \n ...\n
\n 6\n \n 3/4\n \n 1\n \n 1\n \n 0\n \n 0\n \n 0\n \n 0\n \n \n 0\n \n \n 0\n \n ...\n
\n 7\n \n 1/5\n \n 0\n \n 0\n \n 1\n \n 1\n \n 0\n \n 0\n \n 1\n \n \n 1\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 0\n \n 0\n \n 0\n \n 1\n \n 1\n \n 1\n \n 1\n \n 0\n \n ...\n
\n```\n:::\n:::\n\n\nWhen expressed as a decimal, the new sequence corresponds to the value 0.12059395276... .\nDually, its continued fraction expansion begins \\[0; 8,3,2,2,1,2,12, ...\\].\nWhile the number is (almost) certainly irrational, I have no idea whether it is algebraic or transcendental.\n\n\n### Pairs, without repeats\n\nWe have a second, similar enumeration given by `allRationalsMap` in the previous post.\nWe'll need to filter out the numbers greater than 1 from this sequence, but that's not too difficult\n since we're already filtering out repeats.\n\n::: {#ab2a268b .cell .plain execution_count=10}\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \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 \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 Sequence\n \n Fraction\n \n 0\n \n 1\n \n 2\n \n 3\n \n 4\n \n 5\n \n 6\n \n 7\n \n ...\n
\n 0\n \n 1/2\n \n \n 1\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 1\n \n 1/3\n \n 0\n \n \n 1\n \n \n 0\n \n 1\n \n 0\n \n 1\n \n 0\n \n 1\n \n ...\n
\n 2\n \n 1/4\n \n 0\n \n 1\n \n \n 0\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 3\n \n 2/3\n \n 1\n \n 0\n \n 1\n \n \n 0\n \n \n 1\n \n 0\n \n 1\n \n 0\n \n ...\n
\n 4\n \n 1/5\n \n 0\n \n 0\n \n 1\n \n 1\n \n \n 0\n \n \n 0\n \n 1\n \n 1\n \n ...\n
\n 5\n \n 1/6\n \n 0\n \n 0\n \n 1\n \n 0\n \n 1\n \n \n 0\n \n \n 1\n \n 0\n \n ...\n
\n 6\n \n 2/5\n \n 0\n \n 1\n \n 1\n \n 0\n \n 0\n \n 1\n \n \n 1\n \n \n 0\n \n ...\n
\n 7\n \n 3/4\n \n 1\n \n 1\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n \n 0\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 0\n \n 0\n \n 1\n \n 1\n \n 1\n \n 1\n \n 0\n \n 1\n \n ...\n
\n```\n:::\n:::\n\n\nThis new sequence has a decimal expansion equivalent to 0.24005574958...\n (continued fraction expansion begins \\[0; 4, 6, 28, 1, 1, 5, 1, ...\\]).\nAgain, this is probably irrational, since WolframAlpha has no idea on whether a closed form exists.\n\n\nThe Diagonal Transform\n----------------------\n\nWhy stop with just one?\nThis new number can just be tacked onto the beginning of the list.\nThen, we re-apply the diagonal argument to obtain a new number.\nAnd so on ad infinitum.\n\n\n\n::: {#f6421e15 .cell .plain execution_count=12}\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \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 \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 \n
\n Sequence\n \n Fraction\n \n 0\n \n 1\n \n 2\n \n 3\n \n 4\n \n 5\n \n 6\n \n 7\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 -2\n \n \n \n \n \n \n \n 1\n \n \n \n 1\n \n \n \n 1\n \n \n \n 1\n \n \n \n 1\n \n \n \n 0\n \n \n \n 1\n \n \n \n 1\n \n \n \n ...\n \n
\n \n -1\n \n \n \n \n \n \n \n 0\n \n \n \n 0\n \n \n \n 0\n \n \n \n 1\n \n \n \n 1\n \n \n \n 1\n \n \n \n 1\n \n \n \n 0\n \n \n \n ...\n \n
\n 0\n \n 1/2\n \n \n 1\n \n \n \n 0\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 1\n \n 1/3\n \n 0\n \n \n 1\n \n \n \n 0\n \n \n 1\n \n 0\n \n 1\n \n 0\n \n 1\n \n ...\n
\n 2\n \n 2/3\n \n 1\n \n 0\n \n \n 1\n \n \n \n 0\n \n \n 1\n \n 0\n \n 1\n \n 0\n \n ...\n
\n 3\n \n 1/4\n \n 0\n \n 1\n \n 0\n \n \n 0\n \n \n \n 0\n \n \n 0\n \n 0\n \n 0\n \n ...\n
\n 4\n \n 2/5\n \n 0\n \n 1\n \n 1\n \n 0\n \n \n 0\n \n \n \n 1\n \n \n 1\n \n 0\n \n ...\n
\n 5\n \n 3/5\n \n 1\n \n 0\n \n 0\n \n 1\n \n 1\n \n \n 0\n \n \n \n 0\n \n \n 1\n \n ...\n
\n 6\n \n 3/4\n \n 1\n \n 1\n \n 0\n \n 0\n \n 0\n \n 0\n \n \n 0\n \n \n \n 0\n \n \n ...\n
\n 7\n \n 1/5\n \n 0\n \n 0\n \n 1\n \n 1\n \n 0\n \n 0\n \n 1\n \n \n 1\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\nUsing the Stern-Brocot enumeration ~~because I like it better~~\n:::\n:::\n\n\nFor completeness's sake, the decimal expansions of the first few numbers which pop are as follows:\n\n::: {#a360b5ab .cell execution_count=13}\n\n::: {.cell-output .cell-output-display}\n```\n0.12059395276295479\n0.9839683315741587\n0.39401828609177836\n0.7474914867558182\n3.876798422930419e-2\n0.7802097209903278\n0.3215249242021624\n0.6795283379777878\n0.1938245109955674\n0.947203605609322\n```\n:::\n:::\n\n\nDoes this list out \"all\" irrational numbers? Not even close.\nIn fact, this just gives us a bijection between the original enumeration\n and a new one containing our irrationals.\n\nThe new numbers we get also depend heavily on the order of the original sequence.\nThis is obvious just by looking at the first entry produced by our two good enumerations.\nPerhaps if we permuted the enumeration of rationals in all possible ways, we would end up listing\n all irrational numbers, but we'd also run through\n [all ways of ordering the natural numbers](https://en.wikipedia.org/wiki/Baire_space_%28set_theory%29).\n\nThe fact that \"bad\" enumerations exist tells us that it's not even guaranteed that we don't collide\n with any rationals.\nI conjecture that the good enumerations won't do so, since we shouldn't ever encounter\n an infinite sequence of \"0\"s or \"1\"s, and a sequence should eventually differ\n in at least one position from one already listed.\n\n\n### Almost Involution\n\nSince the function has the same input and output type, you may wonder what happens when\n it's applied multiple times.\nPerhaps it is possible that since `\\x -> 1 - x` is an involution, so too is this new function.\nAlas, experimentation proves us wrong.\n\n::: {#7f43a316 .cell layout-ncol='2' execution_count=14}\n\n::: {.cell-output .cell-output-display}\n```\n0.5\n0.3333333333333333\n0.9166666666666666\n0.125\n0.7124999999999999\n0.2875\n0.859375\n7.5e-2\n0.6841517857142857\n0.486328125\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\n1/2\n1/3\n11/12\n1/8\n57/80\n23/80\n55/64\n3/40\n???, possibly 613/896\n249/512\n```\n:::\n:::\n\n\nThe fraction forms of the above numbers are my best guesses.\nEither way, it only matches for the first two terms before going off the rails.\n\nEven stranger than this distorted reflection is the fact that going through the mirror again\n doesn't take us anywhere new:\n\n::: {#30df5ac9 .cell execution_count=15}\n\n::: {.cell-output .cell-output-display}\n```\n0.12059395276295479\n0.9839683315741587\n0.39401828609177836\n0.7474914867558182\n3.876798422930419e-2\n0.7802097209903278\n0.3215249242021624\n0.6795283379777878\n0.1938245109955674\n0.947203605609322\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\nTrue\n```\n:::\n:::\n\n\nIn other words, applying the transform thrice is the same as applying the transform once.\nNot that this has been shown rigorously in the least -- it's only been tested in a 100x100 square.\n\nRegardless, this semi-involutory behavior is strange and nontrivial.\nAnd it's not limited to this enumeration.\nFor example, the filtered `allRationalsMap` iterations shows:\n\n::: {#3232aac9 .cell layout='[[1,1],[1]]' execution_count=16}\n\n::: {.cell-output .cell-output-display}\n```\n0.5\n0.3333333333333333\n0.75\n0.16666666666666666\n0.7\n0.41666666666666663\n0.8687499999999999\n0.1015625\n0.5295758928571428\n0.49453125\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\n1/2\n1/3\n3/4\n1/6\n7/10\n5/12\n139/160\n13/128\n???, possibly 949/1792\n633/1280\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\nTrue\n```\n:::\n:::\n\n\nMeanwhile, the bad enumeration just gives more binary fractions, but it still doesn't mean\n double-transforming returns the original diagonal.\n\n::: {#e67a04cf .cell layout='[[1,1],[1,1],[1]]' execution_count=17}\n\n::: {.cell-output .cell-output-display}\n```\nFirst diagonal transform: \n0.25\n1.0\n0.5\n0.625\n6.25e-2\n0.78125\n0.390625\n0.6328125\n0.19140625\n0.814453125\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\nGuesses\n1/4\n1/1\n1/2\n5/8\n1/16\n25/32\n25/64\n81/128\n49/256\n417/512\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\nSecond diagonal transform: \n0.5\n0.25\n0.75\n0.125\n0.6875\n0.46875\n0.859375\n0.1171875\n0.55859375\n0.435546875\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\nGuesses\n1/2\n1/4\n3/4\n1/8\n11/16\n15/32\n55/64\n15/128\n143/256\n223/512\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\nTrue\n```\n:::\n:::\n\n\nWhat does it mean that the initial enumeration comes back as a completely different one?\nSince the new one is \"stable\" with respect to applying the diagonal transform twice,\n are the results somehow preferable or significant?\n\n\nClosing\n-------\n\nThese questions, and others I have about the products of this process, I unfortunately have no answer for.\nI was mostly troubled by how rare it is to find people applying the diagonal argument\n to something to anything other than random sequences.\n\nRandom sequences, either by choice or by algorithm (if you can even call them random at that point),\n are really only good to show *how* to apply the diagonal argument.\nWithout knowing *what* the argument is applied to, it's useless for constructive arguments.\nIt also still remains to be proven that your new sequences are sufficient for the purpose\n (since I've only shown a case in which it fails).\n\n(Recycled) diagrams made in Geogebra.\n\n\n", + "markdown": "---\ntitle: \"Numbering Numbers, Part 2: Ordering Obliquely\"\ndescription: |\n How do we construct an irrational number from rational ones?\nformat:\n html:\n html-math-method: katex\ndate: \"2023-12-31\"\ndate-modified: \"2025-07-29\"\ncategories:\n - haskell\n - diagonal argument\n---\n\n\n\nThis post assumes you have read the [previous one](./1), which discusses the creation of sequences\n containing every integer, every binary fraction, and in fact, every fraction.\nFrom the fractions, we also could enumerate quadratic irrational numbers using the *question-mark function*.\n\n\nOther Irrationals\n-----------------\n\nBecause rationals -- and even some irrationals -- can be enumerated, we might imagine\n that it would be nice to enumerate *all* irrational numbers.\nUnfortunately, we're not very lucky this time.\n\nLet's start by making what we mean by \"number\" more concrete\nWe've already been exposed to infinite expansions, like 1/3 in binary (and conveniently, also decimal).\nIn discussing the question mark function, I mentioned the utility of repeating expansions as a means of\n accessing quadratic rationals in the Stern-Brocot tree.\nA general sequence need not repeat, so we can choose to treat these extra \"infinite expansions\" as numbers.\nProving that a rational number must have a repeating expansion is difficult, but if we accept this premise,\n then the new non-repeating expansions are our irrationals.\nFor example, $2 - \\varphi \\approx 0.381966...$, which we encountered last time, is such a number.\n\nDoing this introduces a number of headaches, the least of which is that of\n the definition of arithmetic on such quantities.\nHowever, we need only be concerned with the contents of these sequences to show why\n we can't list out all irrationals.\n\n\n### Diagonalization\n\nWe can narrow our focus to the interval between 0 and 1, since the numbers outside this interval\n are their reciprocals.\nNow (if we agree to use base ten), \"all numbers between 0 and 1\" as we've defined them begin with \"0.\",\n followed by an infinite sequence of digits 0-9.\nSuppose that we have an enumeration of every infinite sequence on this list -- no sequence is left out.\n[Cantor's diagonal argument](https://en.wikipedia.org/wiki/Cantor%27s_diagonal_argument) shows that\n a new sequence can be found by taking the sequence on the diagonal\n and changing each individual digit to another one.\nThe new sequence differs in at least one place from every element on the list,\n so the new sequence cannot be on the list.\nTherefore, such an enumeration cannot exist.\n\nThis is illustrated for binary sequences in this diagram:\n\n\n\n\nIt's fairly common to show this argument without much further elaboration,\n but there are a few problems with doing so:\n\n- We're using infinite sequences of digits, not numbers.\n - Equality between sequences is defined by having all elements coincide.\n- We assume we have an enumeration of sequences to which the argument applies.\n The contents of the enumeration are a mystery.\n- We have no idea which sequences are rational numbers, or if we'd construct one\n by applying the diagonal argument.\n\n\n### Equality\n\nThe purpose of the diagonal argument is to produce a new sequence which was not previously enumerated.\nThe sequence is different in all positions, but what we actually want is equality with respect to the base.\nIn base ten, we have the peculiar identity that\n [$0.\\overline{9} = 1$](https://en.wikipedia.org/wiki/0.999...).\nThis means that if the diagonal argument (applied to base ten sequences) constructs a new sequence\n with the digit 9 repeating forever, it might be equivalent to a sequence which was already in the list:\n\n::: {#cd4d8b5b .cell .plain execution_count=2}\n``` {.haskell .cell-code code-fold=\"true\"}\ndiagData = rowsOmega [\n [1, 2, 3, 4, 5, 6],\n [9, 2, 4, 8, 3, 7],\n [2, 2, 8, 2, 2, 2],\n [2, 3, 9, 3, 9, 9],\n [9, 4, 9, 4, 9, 4],\n [8, 1, 2, 5, 7, 9]\n ]\n [2, 3, 9, 4, 0, 0]\n\nrenderTable (displayDiagRows [Omega, RN 3]\n (numberColumn <> diagBox' 5 <> ellipsisColumn)) diagData\n```\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \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 Sequence\n \n 0\n \n 1\n \n 2\n \n 3\n \n 4\n \n 5\n \n ...\n
\n 0\n \n \n 1\n \n \n 2\n \n 3\n \n 4\n \n 5\n \n 6\n \n ...\n
\n 1\n \n 9\n \n \n 2\n \n \n 4\n \n 8\n \n 3\n \n 7\n \n ...\n
\n 2\n \n 2\n \n 2\n \n \n 8\n \n \n 2\n \n 2\n \n 2\n \n ...\n
\n \n 3\n \n \n \n 2\n \n \n \n 3\n \n \n \n 9\n \n \n \n 3\n \n \n \n 9\n \n \n \n 9\n \n \n \n ...\n \n
\n 4\n \n 9\n \n 4\n \n 9\n \n 4\n \n \n 9\n \n \n 4\n \n ...\n
\n 5\n \n 8\n \n 1\n \n 2\n \n 5\n \n 7\n \n \n 9\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 2\n \n \n \n 3\n \n \n \n 9\n \n \n \n 4\n \n \n \n 0\n \n \n \n 0\n \n \n \n ...\n \n
\n```\n\nA case in which the diagonal argument could construct a number already on the list.\n:::\n:::\n\n\nIn the above table, sequence 3 is assumed to continue with 9's forever.\nThe new sequence ω comes from taking the red digits along the diagonal and mapping each digit\n to the next one (mod ten).\nIn the enumeration given, the diagonal continues with 9's forever, so we end up with 0's forever in ω.\n\n\n### Picking a Sequence and Ensuring Rationals\n\n\"No problem, just pick another enumeration,\" you might say.\nIndeed, the example given relies on a simple function and an enumeration\n to which it is particularly ill-suited.\n\nInstead, let's focus on something we *can* do.\nInstead of assuming we have all irrational numbers listed out already, let's start smaller.\nAs of last post, we already have several ways to enumerate all rational numbers between 0 and 1.\nWe can take this enumeration and convert each rational number to positional expansions in a base.\nThen after applying the diagonal argument, the resulting quantity *should* be an irrational number.\n\nFor convenience, we'll use binary expansions of rationals as our sequences.\nThat way, to get a unique sequence on the diagonal, we only have to worry\n about changing \"0\"s to \"1\"s (and vice versa).\nSince we have less flexibility in our digits, it also relieves us from some of the responsibility\n of finding a \"good\" function, like in the decimal case.\nHowever, it's still possible the argument constructs a number already equal to something on the list.\n\n\nInto Silicon\n------------\n\nBefore going any further, let's write a function for applying the diagonal argument to\n a list of binary sequences.\nThis can be implemented in Haskell fairly easily:\n\n::: {#9ef6e535 .cell execution_count=3}\n``` {.haskell .cell-code}\ndiagonalize :: [[Int]] -> [Int]\n -- pair each sequence with its index\ndiagonalize xs = let sequences = zip [0..] xs in\n -- read out the red diagonal\n let diagonal = map (\\(n, s_n) -> s_n !! n) sequences in\n -- map 0 to 1 and vice versa\n map (1 -) diagonal\n-- or in point-free form\n-- diagonalize = map (\\(x,y) -> 1 - (y !! x)) . zip [0..]\n```\n:::\n\n\nNothing about this function is specific to \"binary sequences\", since `Int` contains values\n other than `1` and `0`.\nIt's just more intuitive to work with them instead of \"`True`\" and \"`False`\"\n (since `Bool` actually does have 2 values).\nYou can replace `(1 -)` with `not` to get a similar function for the type `Bool`.\n\nWe also need a function to get a rational number's binary expansion.\nThis is simple if you recall how to do long division.\nWe try to divide the numerator by the denominator, \"emit\" the quotient as part of the result list,\n then continue with the remainder (and the denominator is unchanged).\nIt's not *quite* that simple, since we also need to \"bring down\" more zeroes.\nIn binary, we can add more zeroes to an expansion by just multiplying by 2.\n\n::: {#b118a7a3 .cell layout-ncol='2' execution_count=4}\n``` {.haskell .cell-code}\nbinDiv :: Int -> Int -> [Int]\n-- Divide n by d; q becomes part of the result list\n-- The rest of the list comes from doubling the remainder and keeping d fixed\nbinDiv n d = let (q,r) = n `divMod` d in q:binDiv (2*r) d\n\nx = take 10 $ binDiv 1 3\ny = take 10 $ binDiv 1 5\n\nputStrLn $ \"x = 1 / 3 = 0.\" ++ (tail x >>= show) ++ \"...\"\nputStrLn $ \"y = 1 / 5 = 0.\" ++ (tail y >>= show) ++ \"...\"\n```\n\n::: {.cell-output .cell-output-display}\n```\nx = 1 / 3 = 0.010101010...\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\ny = 1 / 5 = 0.001100110...\n```\n:::\n:::\n\n\nThis function gives us the leading 0 (actually the integer part of the quotient of `n` by `d`),\n but we can peel it off by applying `tail`.\n\nSince we intend to interpret these sequences as binary numbers, we might as well also\n convert this into a form we recognize as a number.\nAll we need to do is take a weighted sum of each sequence by its binary place value.\n\n::: {#ac159fb9 .cell execution_count=5}\n``` {.haskell .cell-code}\nfromBinSeq :: Int -> [Int] -> Double\n -- Construct a list of place values starting with 1/2\nfromBinSeq p = let placeValues = (tail $ iterate (/2) 1) in\n -- Convert the sequence to a type that can multiply with\n -- the place values, then weight, truncate, and sum.\n sum . take p . zipWith (*) placeValues . map fromIntegral\n\noneFifth = fromBinSeq 64 $ tail $ binDiv 1 5\n\nprint oneFifth\n```\n\n::: {.cell-output .cell-output-display}\n```\n0.2\n```\n:::\n:::\n\n\nThe precision `p` here is mostly useless, since we intend to take this to as far as `Double` will go.\n`p` = 100 will do for most sequences, since it's rare that we'll encounter more than a few zeroes\n at the beginning of a sequence.\n\n\nSome Enumerations\n-----------------\n\nNow, for the sake of argument, let's look at an enumeration that fails.\nUsing the tree of binary fractions from the last post, we use a breadth-first search to create\n a list of terminating binary expansions.\n\n![\n Tree of binary fractions, as the tree of terminating binary expansions.
\n We perform a BFS on the left subtree to obtain the values between 0 and 1.\n](../1/binary_expansion_tree.png)\n\n::: {#593d4d8d .cell execution_count=6}\n``` {.haskell .cell-code}\n-- Data class for a labelled sequence\ndata LabelledSeq a b = LS a [b] deriving Functor\n-- Create a labelled binary sequence by dividing n into d and tagging it with \"n/d\"\nbinSeqLabelled (n, d) = LS (show n ++ \"/\" ++ show d) (tail $ binDiv n d)\n-- Build a new diagonalize function to work over labelled sequences\nlsify diagonalize = diagonalize . map (\\(LS _ y) -> y)\n\n-- Extract the left subtree (i.e., the first child subtree)\nbadDiag diagonalizeLS = let (Node _ (tree:__)) = binFracTree in\n diagonalizeLS $ map binSeqLabelled $ bfs tree\n```\n:::\n\n\n::: {#4cc08e74 .cell .plain execution_count=7}\n``` {.haskell .cell-code code-fold=\"true\"}\n-- Helper functions for drawing tables\nbuildDiagTable m = (rowsOmegaLabelled \"Fraction\" . take m . map (\\(LS s y) -> (s, y))) <*> lsify diagonalize\nrenderDiagTable diagf n = renderTable\n (displayDiagRows []\n (numberColumn <> labelColumn \"Fraction\" <> diagBox' (n - 1) <> ellipsisColumn)) $\n diagf (buildDiagTable n)\n\nrenderDiagTable badDiag 8\n```\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \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 \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 Sequence\n \n Fraction\n \n 0\n \n 1\n \n 2\n \n 3\n \n 4\n \n 5\n \n 6\n \n 7\n \n ...\n
\n 0\n \n 1/2\n \n \n 1\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 1\n \n 1/4\n \n 0\n \n \n 1\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 2\n \n 3/4\n \n 1\n \n 1\n \n \n 0\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 3\n \n 1/8\n \n 0\n \n 0\n \n 1\n \n \n 0\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 4\n \n 3/8\n \n 0\n \n 1\n \n 1\n \n 0\n \n \n 0\n \n \n 0\n \n 0\n \n 0\n \n ...\n
\n 5\n \n 5/8\n \n 1\n \n 0\n \n 1\n \n 0\n \n 0\n \n \n 0\n \n \n 0\n \n 0\n \n ...\n
\n 6\n \n 7/8\n \n 1\n \n 1\n \n 1\n \n 0\n \n 0\n \n 0\n \n \n 0\n \n \n 0\n \n ...\n
\n 7\n \n 1/16\n \n 0\n \n 0\n \n 0\n \n 1\n \n 0\n \n 0\n \n 0\n \n \n 0\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 0\n \n 0\n \n 1\n \n 1\n \n 1\n \n 1\n \n 1\n \n 1\n \n ...\n
\n```\n:::\n:::\n\n\nComputing the diagonal sequence, it quickly becomes apparent that we're going\n to keep getting \"0\"s along the diagonal.\nThis is because we're effectively just counting in binary some number of digits to the right.\nThe length of a binary expansion grows logarithmically, but going down the diagonal is a linear process.\nIn other words, we can't count fast enough by just adding 1 (or adding any number, really).\n\nEven worse than this, we get a sequence which is equal to sequence 1 as a binary expansion.\nWe can't even rely on the diagonal argument to give us a new number that *isn't* equal\n to a binary fraction.\n\n\n### Stern-Brocot Diagonal\n\nThe Stern-Brocot tree contains more than just binary fractions, so we're bound to encounter more than\n \"0\" forever when running along the diagonal.\nAgain, looking at the left subtree, we can read off fractions between 0 and 1.\n\n![\n Stern-Brocot tree\n](../1/stern-brocot_tree.png)\n\nWe end up with the following enumeration:\n\n::: {#f3be84d7 .cell .plain execution_count=8}\n``` {.haskell .cell-code}\n-- Extract the left subtree (i.e., the first child subtree)\nsbDiag diagonalizeLS = let (Node _ (tree:__)) = sternBrocot in\n diagonalizeLS $ map binSeqLabelled $ bfs tree\n\nrenderDiagTable sbDiag 8\n```\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \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 \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 Sequence\n \n Fraction\n \n 0\n \n 1\n \n 2\n \n 3\n \n 4\n \n 5\n \n 6\n \n 7\n \n ...\n
\n 0\n \n 1/2\n \n \n 1\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 1\n \n 1/3\n \n 0\n \n \n 1\n \n \n 0\n \n 1\n \n 0\n \n 1\n \n 0\n \n 1\n \n ...\n
\n 2\n \n 2/3\n \n 1\n \n 0\n \n \n 1\n \n \n 0\n \n 1\n \n 0\n \n 1\n \n 0\n \n ...\n
\n 3\n \n 1/4\n \n 0\n \n 1\n \n 0\n \n \n 0\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 4\n \n 2/5\n \n 0\n \n 1\n \n 1\n \n 0\n \n \n 0\n \n \n 1\n \n 1\n \n 0\n \n ...\n
\n 5\n \n 3/5\n \n 1\n \n 0\n \n 0\n \n 1\n \n 1\n \n \n 0\n \n \n 0\n \n 1\n \n ...\n
\n 6\n \n 3/4\n \n 1\n \n 1\n \n 0\n \n 0\n \n 0\n \n 0\n \n \n 0\n \n \n 0\n \n ...\n
\n 7\n \n 1/5\n \n 0\n \n 0\n \n 1\n \n 1\n \n 0\n \n 0\n \n 1\n \n \n 1\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 0\n \n 0\n \n 0\n \n 1\n \n 1\n \n 1\n \n 1\n \n 0\n \n ...\n
\n```\n:::\n:::\n\n\nWhen expressed as a decimal, the new sequence corresponds to the value 0.12059395276... .\nDually, its continued fraction expansion begins \\[0; 8,3,2,2,1,2,12, ...\\].\nWhile the number is (almost) certainly irrational, I have no idea whether it is algebraic or transcendental.\n\n\n### Pairs, without repeats\n\nWe have a second, similar enumeration given by `allRationalsMap` in the previous post.\nWe'll need to filter out the numbers greater than 1 from this sequence, but that's not too difficult\n since we're already filtering out repeats.\n\n::: {#ab797911 .cell .plain execution_count=9}\n``` {.haskell .cell-code}\n-- Only focus on the rationals whose denominator is bigger\narDiag diagonalizeLS = let rationals01 = filter (uncurry (<)) allRationals in\n diagonalizeLS $ map binSeqLabelled rationals01\n\nrenderDiagTable arDiag 8\n```\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \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 \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 Sequence\n \n Fraction\n \n 0\n \n 1\n \n 2\n \n 3\n \n 4\n \n 5\n \n 6\n \n 7\n \n ...\n
\n 0\n \n 1/2\n \n \n 1\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 1\n \n 1/3\n \n 0\n \n \n 1\n \n \n 0\n \n 1\n \n 0\n \n 1\n \n 0\n \n 1\n \n ...\n
\n 2\n \n 1/4\n \n 0\n \n 1\n \n \n 0\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 3\n \n 2/3\n \n 1\n \n 0\n \n 1\n \n \n 0\n \n \n 1\n \n 0\n \n 1\n \n 0\n \n ...\n
\n 4\n \n 1/5\n \n 0\n \n 0\n \n 1\n \n 1\n \n \n 0\n \n \n 0\n \n 1\n \n 1\n \n ...\n
\n 5\n \n 1/6\n \n 0\n \n 0\n \n 1\n \n 0\n \n 1\n \n \n 0\n \n \n 1\n \n 0\n \n ...\n
\n 6\n \n 2/5\n \n 0\n \n 1\n \n 1\n \n 0\n \n 0\n \n 1\n \n \n 1\n \n \n 0\n \n ...\n
\n 7\n \n 3/4\n \n 1\n \n 1\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n \n 0\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 0\n \n 0\n \n 1\n \n 1\n \n 1\n \n 1\n \n 0\n \n 1\n \n ...\n
\n```\n:::\n:::\n\n\nThis new sequence has a decimal expansion equivalent to 0.24005574958...\n (continued fraction expansion begins \\[0; 4, 6, 28, 1, 1, 5, 1, ...\\]).\nAgain, this is probably irrational, since WolframAlpha has no idea on whether a closed form exists.\n\n\nThe Diagonal Transform\n----------------------\n\nWhy stop with just one?\nThis new number can just be tacked onto the beginning of the list.\nThen, we re-apply the diagonal argument to obtain a new number.\nAnd so on ad infinitum.\n\n::: {#5ab3d034 .cell execution_count=10}\n``` {.haskell .cell-code}\ntransform :: [[Int]] -> [[Int]]\n-- Emit the new diagonal sequence, then recurse with the new sequence\n-- prepended to the original enumeration\ntransform xs = let ds = diagonalize xs in ds:transform (ds:xs)\n```\n:::\n\n\n::: {#4dfd6c42 .cell .plain execution_count=11}\n``` {.haskell .cell-code code-fold=\"true\"}\ntransformLS :: [LabelledSeq String Int] -> [LabelledSeq String Int]\n-- Emit the new diagonal sequence, then recurse with the new sequence\n-- prepended to the original enumeration\ntransformLS xs = let ds = lsify diagonalize xs in LS \"\" ds:transformLS (LS \"\" ds:xs)\n\n-- Prepend and append ellipses, then join the reversed transform sequence to the original\nbuildTransformTable labelName m xs = DR Ellipsis [] []:table where\n table = concat [reverse prepended, mainTable, [DR Ellipsis [] []]]\n xs' = transformLS xs\n prepended = take m $ zipWith (\\(LS l x) n -> DR (RN $ -n) [] x) xs' [1..]\n mainTable = zipWith (\\(LS l x) n -> DR (RN n) [(labelName, l)] x) xs [0..]\n\n-- Render certain diagonals and rows in the same way\ndisplayTransformRows ns = renderCells (stringCell . showCell) $ ns >>= formatDiag where\n formatDiag (n, f) = [\n markDiagonal n (f . show),\n markRows [RN $ n-1] (f . showCell)\n ]\n\n-- Render diagonal 0 (row -1) as red and diagonal -1 (row -2) as green\nrenderTransformTable n = renderTable\n (displayTransformRows [(0, redCell), (-1, greenCell)]\n (numberColumn <> labelColumn \"Fraction\" <> diagBox' (n - 1) <> ellipsisColumn)) .\n buildTransformTable \"Fraction\" 2\n\nrenderTransformTable 8 $ take 8 $ sbDiag id\n```\n\n::: {.cell-output .cell-output-display}\n```{=html}\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 \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 \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 \n
\n Sequence\n \n Fraction\n \n 0\n \n 1\n \n 2\n \n 3\n \n 4\n \n 5\n \n 6\n \n 7\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 -2\n \n \n \n \n \n \n \n 1\n \n \n \n 1\n \n \n \n 1\n \n \n \n 1\n \n \n \n 1\n \n \n \n 0\n \n \n \n 1\n \n \n \n 1\n \n \n \n ...\n \n
\n \n -1\n \n \n \n \n \n \n \n 0\n \n \n \n 0\n \n \n \n 0\n \n \n \n 1\n \n \n \n 1\n \n \n \n 1\n \n \n \n 1\n \n \n \n 0\n \n \n \n ...\n \n
\n 0\n \n 1/2\n \n \n 1\n \n \n \n 0\n \n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n 0\n \n ...\n
\n 1\n \n 1/3\n \n 0\n \n \n 1\n \n \n \n 0\n \n \n 1\n \n 0\n \n 1\n \n 0\n \n 1\n \n ...\n
\n 2\n \n 2/3\n \n 1\n \n 0\n \n \n 1\n \n \n \n 0\n \n \n 1\n \n 0\n \n 1\n \n 0\n \n ...\n
\n 3\n \n 1/4\n \n 0\n \n 1\n \n 0\n \n \n 0\n \n \n \n 0\n \n \n 0\n \n 0\n \n 0\n \n ...\n
\n 4\n \n 2/5\n \n 0\n \n 1\n \n 1\n \n 0\n \n \n 0\n \n \n \n 1\n \n \n 1\n \n 0\n \n ...\n
\n 5\n \n 3/5\n \n 1\n \n 0\n \n 0\n \n 1\n \n 1\n \n \n 0\n \n \n \n 0\n \n \n 1\n \n ...\n
\n 6\n \n 3/4\n \n 1\n \n 1\n \n 0\n \n 0\n \n 0\n \n 0\n \n \n 0\n \n \n \n 0\n \n \n ...\n
\n 7\n \n 1/5\n \n 0\n \n 0\n \n 1\n \n 1\n \n 0\n \n 0\n \n 1\n \n \n 1\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\nUsing the Stern-Brocot enumeration ~~because I like it better~~\n:::\n:::\n\n\nFor completeness's sake, the decimal expansions of the first few numbers which pop are as follows:\n\n::: {#29cbe414 .cell execution_count=12}\n``` {.haskell .cell-code}\nsbDiagSeq = let (Node _ (tree:__)) = sternBrocot in\n transform $ map (tail . uncurry binDiv) $ bfs tree\n\nmapM_ print $ take 10 $ map (fromBinSeq 100) sbDiagSeq\n```\n\n::: {.cell-output .cell-output-display}\n```\n0.12059395276295479\n0.9839683315741587\n0.39401828609177836\n0.7474914867558182\n3.876798422930419e-2\n0.7802097209903278\n0.3215249242021624\n0.6795283379777878\n0.1938245109955674\n0.947203605609322\n```\n:::\n:::\n\n\nDoes this list out \"all\" irrational numbers? Not even close.\nIn fact, this just gives us a bijection between the original enumeration\n and a new one containing our irrationals.\n\nThe new numbers we get also depend heavily on the order of the original sequence.\nThis is obvious just by looking at the first entry produced by our two good enumerations.\nPerhaps if we permuted the enumeration of rationals in all possible ways, we would end up listing\n all irrational numbers, but we'd also run through\n [all ways of ordering the natural numbers](https://en.wikipedia.org/wiki/Baire_space_%28set_theory%29).\n\nThe fact that \"bad\" enumerations exist tells us that it's not even guaranteed that we don't collide\n with any rationals.\nI conjecture that the good enumerations won't do so, since we shouldn't ever encounter\n an infinite sequence of \"0\"s or \"1\"s, and a sequence should eventually differ\n in at least one position from one already listed.\n\n\n### Almost Involution\n\nSince the function has the same input and output type, you may wonder what happens when\n it's applied multiple times.\nPerhaps it is possible that since `\\x -> 1 - x` is an involution, so too is this new function.\nAlas, experimentation proves us wrong.\n\n::: {#aacbae40 .cell layout-ncol='2' execution_count=13}\n``` {.haskell .cell-code}\nsbDiagSeq2 = transform sbDiagSeq\n\nmapM_ print $ take 10 $ map (fromBinSeq 100) sbDiagSeq2\n-- Guesses\nmapM_ putStrLn [ \"1/2\", \"1/3\", \"11/12\", \"1/8\", \"57/80\", \"23/80\",\n \"55/64\", \"3/40\", \"???, possibly 613/896\", \"249/512\" ]\n```\n\n::: {.cell-output .cell-output-display}\n```\n0.5\n0.3333333333333333\n0.9166666666666666\n0.125\n0.7124999999999999\n0.2875\n0.859375\n7.5e-2\n0.6841517857142857\n0.486328125\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\n1/2\n1/3\n11/12\n1/8\n57/80\n23/80\n55/64\n3/40\n???, possibly 613/896\n249/512\n```\n:::\n:::\n\n\nThe fraction forms of the above numbers are my best guesses.\nEither way, it only matches for the first two terms before going off the rails.\n\nEven stranger than this distorted reflection is the fact that going through the mirror again\n doesn't take us anywhere new:\n\n::: {#bd14098b .cell execution_count=14}\n``` {.haskell .cell-code}\nsbDiagSeq3 = transform sbDiagSeq2\n\nsquaresAgree n xs ys = let takeSquare zs = take n $ map (take n) zs in\n takeSquare xs == takeSquare ys\n\nmapM_ print $ take 10 $ map (fromBinSeq 100) sbDiagSeq3\n-- First and third iterates agree\nprint $ squaresAgree 100 sbDiagSeq sbDiagSeq3\n```\n\n::: {.cell-output .cell-output-display}\n```\n0.12059395276295479\n0.9839683315741587\n0.39401828609177836\n0.7474914867558182\n3.876798422930419e-2\n0.7802097209903278\n0.3215249242021624\n0.6795283379777878\n0.1938245109955674\n0.947203605609322\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\nTrue\n```\n:::\n:::\n\n\nIn other words, applying the transform thrice is the same as applying the transform once.\nNot that this has been shown rigorously in the least -- it's only been tested in a 100x100 square.\n\nRegardless, this semi-involutory behavior is strange and nontrivial.\nAnd it's not limited to this enumeration.\nFor example, the filtered `allRationalsMap` iterations shows:\n\n::: {#68ed7f9a .cell layout='[[1,1],[1]]' execution_count=15}\n``` {.haskell .cell-code}\narDiagSeq = let rationals01 = filter (uncurry (<)) allRationals in\n transform $ map (tail . uncurry binDiv) rationals01\n\n\n-- What goes in isn't what comes out\nmapM_ print $ take 10 $ map (fromBinSeq 100) $ transform arDiagSeq\n-- Guesses\nmapM_ putStrLn [\"1/2\", \"1/3\", \"3/4\", \"1/6\", \"7/10\", \"5/12\", \"139/160\", \"13/128\", \"???, possibly 949/1792\", \"633/1280\"]\n-- First and third iterates agree\nprint $ squaresAgree 100 arDiagSeq (transform $ transform arDiagSeq)\n```\n\n::: {.cell-output .cell-output-display}\n```\n0.5\n0.3333333333333333\n0.75\n0.16666666666666666\n0.7\n0.41666666666666663\n0.8687499999999999\n0.1015625\n0.5295758928571428\n0.49453125\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\n1/2\n1/3\n3/4\n1/6\n7/10\n5/12\n139/160\n13/128\n???, possibly 949/1792\n633/1280\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\nTrue\n```\n:::\n:::\n\n\nMeanwhile, the bad enumeration just gives more binary fractions, but it still doesn't mean\n double-transforming returns the original diagonal.\n\n::: {#45b09da2 .cell layout='[[1,1],[1,1],[1]]' execution_count=16}\n``` {.haskell .cell-code}\nbfDiagSeq = let (Node _ (tree:__)) = binFracTree in\n transform $ map (tail . uncurry binDiv) $ bfs tree\n\n(putStrLn \"First diagonal transform: \" >>) $\n mapM_ print $ take 10 $ map (fromBinSeq 100) bfDiagSeq\n-- Guesses\nmapM_ putStrLn [\"Guesses\", \"1/4\", \"1/1\", \"1/2\", \"5/8\", \"1/16\", \"25/32\", \"25/64\", \"81/128\", \"49/256\", \"417/512\"]\n\n-- What goes in isn't what comes out\n(putStrLn \"Second diagonal transform: \" >>) $\n mapM_ print $ take 10 $ map (fromBinSeq 100) $ transform bfDiagSeq\n-- Guesses\nmapM_ putStrLn [\"Guesses\", \"1/2\", \"1/4\", \"3/4\", \"1/8\", \"11/16\", \"15/32\", \"55/64\", \"15/128\", \"143/256\", \"223/512\"]\n\n-- First and third iterates agree\nprint $ squaresAgree 100 bfDiagSeq (transform $ transform bfDiagSeq)\n```\n\n::: {.cell-output .cell-output-display}\n```\nFirst diagonal transform: \n0.25\n1.0\n0.5\n0.625\n6.25e-2\n0.78125\n0.390625\n0.6328125\n0.19140625\n0.814453125\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\nGuesses\n1/4\n1/1\n1/2\n5/8\n1/16\n25/32\n25/64\n81/128\n49/256\n417/512\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\nSecond diagonal transform: \n0.5\n0.25\n0.75\n0.125\n0.6875\n0.46875\n0.859375\n0.1171875\n0.55859375\n0.435546875\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\nGuesses\n1/2\n1/4\n3/4\n1/8\n11/16\n15/32\n55/64\n15/128\n143/256\n223/512\n```\n:::\n\n::: {.cell-output .cell-output-display}\n```\nTrue\n```\n:::\n:::\n\n\nWhat does it mean that the initial enumeration comes back as a completely different one?\nSince the new one is \"stable\" with respect to applying the diagonal transform twice,\n are the results somehow preferable or significant?\n\n\nClosing\n-------\n\nThese questions, and others I have about the products of this process, I unfortunately have no answer for.\nI was mostly troubled by how rare it is to find people applying the diagonal argument\n to something to anything other than random sequences.\n\nRandom sequences, either by choice or by algorithm (if you can even call them random at that point),\n are really only good to show *how* to apply the diagonal argument.\nWithout knowing *what* the argument is applied to, it's useless for constructive arguments.\nIt also still remains to be proven that your new sequences are sufficient for the purpose\n (since I've only shown a case in which it fails).\n\n(Recycled) diagrams made in Geogebra.\n\n\n", "supporting": [ "index_files" ], diff --git a/posts/number-number/_metadata.yml b/posts/number-number/_metadata.yml index 8c5faf1..1cede7f 100644 --- a/posts/number-number/_metadata.yml +++ b/posts/number-number/_metadata.yml @@ -4,9 +4,6 @@ freeze: auto # Enable banner style title blocks title-block-banner: true -execute: - echo: false - # Get rid of the figure label crossref: fig-title: ""