diff --git a/ipynb/RationalPi.ipynb b/ipynb/RationalPi.ipynb
new file mode 100644
index 0000000..cf0087f
--- /dev/null
+++ b/ipynb/RationalPi.ipynb
@@ -0,0 +1,259 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "5b025dd9-9345-402d-9be0-dd0c17842915",
+ "metadata": {},
+ "source": [
+ "
Peter Norvig
May 2026
\n",
+ "\n",
+ "# Fractions approximating π\n",
+ "\n",
+ "The number π is irrational, which means that it cannot be represented exactly by a fraction. But some fractions, such as 22/7, are known to come close to π. In this notebook, we look for fractions that approximate π even more closely than 22/7.\n",
+ "\n",
+ "If we want to know *what's the best fraction approximation to π with a denominator no more than a given number of digits?* we can try every possible denominator up to the given number of digits, compute the best numerator (and hence the best fraction) for each denominator, and choose the resulting fraction that minimizes the distance to the target, π (or if we want, any target):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "00c9ebd9-ee55-4dcb-917a-9c7a098d3816",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from fractions import Fraction\n",
+ "from math import pi\n",
+ "\n",
+ "def approximation(target=pi, digits=6) -> Fraction:\n",
+ " \"\"\"The fraction best approximating `target` whose denominator has no more than `digits` digits.\"\"\"\n",
+ " denominators = range(1, 10 ** digits)\n",
+ " fractions = (Fraction(round(target * d), d) for d in denominators)\n",
+ " return min(fractions, key=lambda x: abs(target - x))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "49db7449-1f81-4a7f-842a-84348dc5f16a",
+ "metadata": {},
+ "source": [
+ "For example, here are the best approximations with one- and three-digit denominators:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "de87500e-62cf-48fe-adee-3625f4cf6742",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Fraction(22, 7)"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "approximation(pi, 1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "3e9e799a-437a-45ff-8182-3d8d3342fe3a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Fraction(355, 113)"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "approximation(pi, 3)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4408e472-e7e6-4cc7-8a6b-790a55860bd3",
+ "metadata": {},
+ "source": [
+ "We can make a pretty report showing the best approximations to π with 1, 2, 3, 4, and 5-digit denominators:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "59faedd9-4899-4ccd-858b-fb5e4032a0cd",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def report(approximator=approximation, target=pi, d_range=range(1, 6)) -> str:\n",
+ " \"\"\"For each `d` in `d_range`, print a description of how close \n",
+ " the fraction `approximator(target, d)` is to `target`.\"\"\"\n",
+ " for d in d_range:\n",
+ " r = approximator(target, d)\n",
+ " print(f'{r:^22} = {r:.25f} (error {r-target:+6.0e})')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "24447e10-a58f-43f2-93e5-3b058c098096",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " 22/7 = 3.1428571428571428571428571 (error +1e-03)\n",
+ " 311/99 = 3.1414141414141414141414141 (error -2e-04)\n",
+ " 355/113 = 3.1415929203539823008849558 (error +3e-07)\n",
+ " 355/113 = 3.1415929203539823008849558 (error +3e-07)\n",
+ " 312689/99532 = 3.1415926536189366233975003 (error +3e-11)\n"
+ ]
+ }
+ ],
+ "source": [
+ "report()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5f21ad7f-193b-412e-b175-4cf9975cffd2",
+ "metadata": {},
+ "source": [
+ "(The approximation 355/113 is so good that no 4-digit-denominator is better. You can think of 3550/1130 as the best 4-digit approximation.)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9c035f0d-5cb8-4092-8ed1-883f9a9dd30d",
+ "metadata": {},
+ "source": [
+ "# Faster, Better Approximations\n",
+ "\n",
+ "A 5-digit denominator is the most that `approximation` can handle in under a second of run time. \n",
+ "\n",
+ "We can get better approximations much faster by iteratively improving a guess:\n",
+ "- The zeroth approximation to π is formed by rounding to the nearest integer, **3**.\n",
+ "- The first approximation to π is formed by adding 3 to an approximation of the remainder after rounding, 0.14159...\n",
+ " - Rounding the remainder would give us 0, so that doesn't help.\n",
+ " - Instead of rounding the remainder, use (1 / approximation(1 / remainder)).\n",
+ " - 1 / 0.14159... rounds to 7 (with remainder 0.06251...) so the first approximation is **3 + (1/7) = 22/7**.\n",
+ "- The second approximation to π is formed by adding in an approximation of 0.06251...\n",
+ " - 1 / 0.06251... rounds to 16, so the second approximation is **3 + (1/(7 + 1/16)) = 355/113**.\n",
+ " - You can repeat the approximation process to any depth.\n",
+ " \n",
+ "We can implement it like this:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "f81761ad-1b21-4dce-9fba-9621447617d1",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "PI = Fraction(\"3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803\")\n",
+ "\n",
+ "def fast_approximation(target: Fraction, depth: int) -> Fraction:\n",
+ " \"\"\"Approximate `target` by taking the whole part plus an approximation to the remainder.\n",
+ " Repeat `depth` times (or stop when there is no remainder).\n",
+ " Use (1 / fast_approximation(1 / remainder)), because remainder < 1 and (1 / remainder) > 1.\"\"\"\n",
+ " whole = round(target)\n",
+ " remainder = target - whole\n",
+ " if depth == 0 or remainder == 0:\n",
+ " return Fraction(whole)\n",
+ " else:\n",
+ " return whole + (1 / fast_approximation(1 / remainder, depth - 1))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7e3c32d2-66f7-450f-9dc3-e5034dfd09c5",
+ "metadata": {},
+ "source": [
+ "This gives an approximation accurate to 22 digits after 14 iterations, and takes just milliseconds to run:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "ba7e0c79-515e-48c8-9f7b-a4a99734b9be",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " 3 = 3.0000000000000000000000000 (error -1e-01)\n",
+ " 22/7 = 3.1428571428571428571428571 (error +1e-03)\n",
+ " 355/113 = 3.1415929203539823008849558 (error +3e-07)\n",
+ " 104348/33215 = 3.1415926539214210447087159 (error +3e-10)\n",
+ " 312689/99532 = 3.1415926536189366233975003 (error +3e-11)\n",
+ " 1146408/364913 = 3.1415926535914039784825424 (error +2e-12)\n",
+ " 5419351/1725033 = 3.1415926535898153832419438 (error +2e-14)\n",
+ " 80143857/25510582 = 3.1415926535897926593756269 (error -6e-16)\n",
+ " 245850922/78256779 = 3.1415926535897931602832772 (error -8e-17)\n",
+ " 411557987/131002976 = 3.1415926535897932578264482 (error +2e-17)\n",
+ " 1068966896/340262731 = 3.1415926535897932353925649 (error -3e-18)\n",
+ " 2549491779/811528438 = 3.1415926535897932390140098 (error +6e-19)\n",
+ "6167950454/1963319607 = 3.1415926535897932383863775 (error -8e-20)\n",
+ "21053343141/6701487259 = 3.1415926535897932384623817 (error -3e-22)\n"
+ ]
+ }
+ ],
+ "source": [
+ "report(fast_approximation, PI, range(14))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c4f1404b-a9b2-4aaa-be83-d34bab26d734",
+ "metadata": {},
+ "source": [
+ "It turns out there is a name for this approach: it is the [continued fraction approximation to π](https://en.wikipedia.org/wiki/Pi#Continued_fractions). \n",
+ "\n",
+ "Note: it is more traditional to use `whole = int(target)` rather than `whole = round(target)` to generate the continued fractions. Using `round` gives better fractions in fewer itera, but using `int` has the nice property that all the bits we add are positive."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4ea1db23-b668-4854-86ea-337462a18a8b",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python [conda env:base] *",
+ "language": "python",
+ "name": "conda-base-py"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.13.9"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}