From e9e1c4c43b98ee8a8b9ef3a2a02218f6a96b1c9d Mon Sep 17 00:00:00 2001 From: Peter Norvig Date: Thu, 24 Sep 2026 22:51:09 -0700 Subject: [PATCH] Add files via upload --- py/TruncatablePrimes.ipynb | 780 +++++++++++++++++++++++++++++++++++++ 1 file changed, 780 insertions(+) create mode 100644 py/TruncatablePrimes.ipynb diff --git a/py/TruncatablePrimes.ipynb b/py/TruncatablePrimes.ipynb new file mode 100644 index 0000000..826b2c8 --- /dev/null +++ b/py/TruncatablePrimes.ipynb @@ -0,0 +1,780 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8812d495-6014-4669-97ae-003be4f54605", + "metadata": {}, + "source": [ + "
Peter Norvig
Jan 2026
\n", + "\n", + "# Truncatable Primes\n", + "\n", + "What's special about [this pencil](https://mathsgear.co.uk/products/truncatable-prime-pencil)?\n", + "\n", + "[![](https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQju3c-tDhXMmfLbaaogGvcEChC4mAj2LWOCQTGhZA6SQ&s=10)](https://community.wolfram.com/groups/-/m/t/1569707)\n", + "\n", + "The 24-digit number printed on this pencil is a prime, and as you sharpen the pencil and remove digits one at a time from the left, the resulting numbers are all primes:\n", + "\n", + " 357686312646216567629137 is prime\n", + " 57686312646216567629137 is prime\n", + " 7686312646216567629137 is prime\n", + " ...\n", + " 137 is prime\n", + " 37 is prime\n", + " 7 is prime\n", + "\n", + "Numbers like this are called [**left-truncatable primes**](https://en.wikipedia.org/wiki/Truncatable_prime). \n", + "\n", + "My function `left_truncatable_primes` works as follows:\n", + "- Start with the one-digit primes, (2, 3, 5, 7).\n", + "- On each iteration create a list of new numbers that are one digit longer by placing each possible digit 1–9 to the left of each prime.\n", + "- Out of those new numbers, find just the new primes.\n", + "- If there are any new primes, the function recursively adds digits to them, giving us even longer primes.\n", + "- In the end, the function returns all the left-truncatable primes." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "8ab95f58-0d60-4516-b19c-5f55614abb54", + "metadata": {}, + "outputs": [], + "source": [ + "from sympy import isprime # isprime(n) returns True if n is a prime\n", + "\n", + "def left_truncatable_primes(primes=(2, 3, 5, 7)) -> tuple[int]:\n", + " \"\"\"All left-truncatable primes, in ascending order.\"\"\"\n", + " new_numbers = (int(d + str(p)) for d in \"123456789\" for p in primes)\n", + " new_primes = tuple(filter(isprime, new_numbers))\n", + " if new_primes:\n", + " return primes + left_truncatable_primes(new_primes)\n", + " else:\n", + " return primes" + ] + }, + { + "cell_type": "markdown", + "id": "90d8f036-da26-4975-a0da-ab637694a679", + "metadata": {}, + "source": [ + "Let's see how many truncatable primes there are:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "4c9a569c-af7f-4335-b10b-785e5709862b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4260" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "LTP = left_truncatable_primes()\n", + "\n", + "len(LTP)" + ] + }, + { + "cell_type": "markdown", + "id": "a32b56c4-587b-48d5-91ec-1abcef2e909f", + "metadata": {}, + "source": [ + "There are **4260**; here are the smallest and largest 16 of them:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e937655d-16eb-44ce-8698-0a0c09c9f6a9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(2, 3, 5, 7, 13, 17, 23, 37, 43, 47, 53, 67, 73, 83, 97, 113)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "LTP[:16]" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "502be81c-fb57-431a-855a-21184a1aeb0d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(66276812967623946997,\n", + " 67986315421273233617,\n", + " 86312646216567629137,\n", + " 315396334245663786197,\n", + " 367986315421273233617,\n", + " 666276812967623946997,\n", + " 686312646216567629137,\n", + " 918918997653319693967,\n", + " 5918918997653319693967,\n", + " 6686312646216567629137,\n", + " 7686312646216567629137,\n", + " 9918918997653319693967,\n", + " 57686312646216567629137,\n", + " 95918918997653319693967,\n", + " 96686312646216567629137,\n", + " 357686312646216567629137)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "LTP[-16:]" + ] + }, + { + "cell_type": "markdown", + "id": "e82700e8-8fb2-45cf-bcf0-7805e46174bb", + "metadata": {}, + "source": [ + "## Right-Truncatable Primes\n", + "\n", + "What if you sharpen the pencil from the other end? You get `right-trunctable primes`:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "3870bc47-5833-4879-b622-2094b4247239", + "metadata": {}, + "outputs": [], + "source": [ + "def right_truncatable_primes(primes=(2, 3, 5, 7)) -> tuple[int]:\n", + " \"\"\"All right-truncatable primes, in ascending order.\"\"\"\n", + " # Only consider (1, 3, 7, 9) as the digit on the right; placing any other digit forms a composite number\n", + " new_numbers = (10 * p + d for p in primes for d in (1, 3, 7, 9))\n", + " new_primes = tuple(filter(isprime, new_numbers))\n", + " if new_primes:\n", + " return primes + right_truncatable_primes(new_primes)\n", + " else:\n", + " return primes" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "7b39ffb5-ba52-42e6-a270-79bc10b26c2c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "83" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "RTP = right_truncatable_primes()\n", + "\n", + "len(RTP)" + ] + }, + { + "cell_type": "markdown", + "id": "5323ae60-285d-49d6-a133-5c476eb4c5e5", + "metadata": {}, + "source": [ + "There are only 83 right-truncatable primes. Here they are:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "e3780212-2b86-469f-9df0-81bd6fcc9c94", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(2, 3, 5, 7, 23, 29, 31, 37, 53, 59, 71, 73, 79, 233, 239, 293, 311, 313, 317, 373, 379, 593, 599, 719, 733, 739, 797, 2333, 2339, 2393, 2399, 2939, 3119, 3137, 3733, 3739, 3793, 3797, 5939, 7193, 7331, 7333, 7393, 23333, 23339, 23399, 23993, 29399, 31193, 31379, 37337, 37339, 37397, 59393, 59399, 71933, 73331, 73939, 233993, 239933, 293999, 373379, 373393, 593933, 593993, 719333, 739391, 739393, 739397, 739399, 2339933, 2399333, 2939999, 3733799, 5939333, 7393913, 7393931, 7393933, 23399339, 29399999, 37337999, 59393339, 73939133)\n" + ] + } + ], + "source": [ + "print(RTP)" + ] + }, + { + "cell_type": "markdown", + "id": "976c7f1f-79c5-48c3-8909-62b809993057", + "metadata": {}, + "source": [ + "We can also find the **two-sided truncatable primes**, which are both right- and left-truncatable. It turns out there are only 15 of them:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "b87ae26e-0dee-4c95-914f-c897fda17508", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{2, 3, 5, 7, 23, 37, 53, 73, 313, 317, 373, 797, 3137, 3797, 739397}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "set(LTP) & set(RTP)" + ] + }, + { + "cell_type": "markdown", + "id": "82fc3133-4a17-47ec-b742-7a37f78421a8", + "metadata": {}, + "source": [ + "There isn't an official name for a truncatable prime that remains prime for all possible choices of truncating either the rightmost or leftmost digit at each step. I'll call them **omni-truncatable primes**. I can compute them like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "28f12184-b247-4cea-86d4-6b0e07e2a63f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 3, 5, 7, 23, 37, 53, 73, 373]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def is_omni_truncatable_prime(n: int) -> bool:\n", + " \"\"\"Is n a truncatable prime for all possible choices of truncating digits from left or right?\"\"\"\n", + " return (isprime(n) and\n", + " (n < 10 or (is_omni_truncatable_prime(int(str(n)[1:])) and\n", + " is_omni_truncatable_prime(int(str(n)[:-1])))))\n", + "\n", + "[p for p in RTP if is_omni_truncatable_prime(p)]" + ] + }, + { + "cell_type": "markdown", + "id": "f2323985-2518-41d1-8a3b-6e21e40d27be", + "metadata": {}, + "source": [ + "There are only 9 of them: 4 one-digit, 4 two-digit, and 1 three-digit.\n", + "\n", + "## Summary \n", + "\n", + "|Kind|Count|Largest|Digits|\n", + "|---|---:|---:|---:|\n", + "|Left-truncatable primes|4260|357686312646216567629137|24|\n", + "|Right-truncatable primes|83|73939133 |8|\n", + "|Two-sided primes|15|739397 |6|\n", + "|Omni-truncatable primes|9|373 |3|\n", + "\n", + "# Primality Testing\n", + "\n", + "I was very impressed with the speed of the function [`sympy.isprime`](https://github.com/sympy/sympy/blob/master/sympy/ntheory/primetest.py), and I wanted to investigate the topic of primality testing. I'll start by defining `isprime_simple`, which follows the definition of a prime number almost verbatim:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "1d460931-3d70-485d-807f-8fb360ef4ef2", + "metadata": {}, + "outputs": [], + "source": [ + "def isprime_simple(n: int) -> bool:\n", + " \"\"\"Is n prime? A prime number is defined as an integer greater than 1 \n", + " that cannot be evenly divided by any whole number other than 1 and itself.\"\"\"\n", + " divisors = range(2, n)\n", + " return n > 1 and not any(n % d == 0 for d in divisors)" + ] + }, + { + "cell_type": "markdown", + "id": "1834f8f0-3712-4302-ac56-79972f85d0db", + "metadata": {}, + "source": [ + "To test a *d*-digit prime we have to iterate through nearly 10d divisors, which will be noticeably slow for anything larger than about 7-digit numbers.\n", + "\n", + "We can speed things up a bit with two ideas, one minor and one major. The minor idea is to cut the run time in half by only testing the odd divisors (after determining that 2 is a prime and all other even numbers are composite). The major idea is that we only need to test divisors up to √*n*, not up to *n*. If *n* is composite, then *n* is the product of two numbers, and one of them must be less than or equal to √*n*, so we can stop there." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "647fb66b-32ae-494a-8945-cf7358c2f958", + "metadata": {}, + "outputs": [], + "source": [ + "from math import sqrt\n", + "\n", + "def isprime_faster(n: int) -> bool:\n", + " \"\"\"More sophisticated primality tester: test only odd divisors up to √n.\"\"\"\n", + " if n <= 4: # Handle small numbers up to 4\n", + " return n in (2, 3)\n", + " elif n % 2 == 0: # Even numbers other than 2 are composite\n", + " return False\n", + " else: # Test odd divisors up to √n\n", + " divisors = range(3, int(sqrt(n)) + 1, 2) \n", + " return not any(n % d == 0 for d in divisors)" + ] + }, + { + "cell_type": "markdown", + "id": "05b8cbf7-ea10-4047-bcdb-a5ac7981384b", + "metadata": {}, + "source": [ + "For a 24-digit prime, `isprime_simple` would need to test septillions of divisors, while `isprime_faster` would \"only\" need to test trillions. That's the difference between never completing versus taking a few hours. But we would like to get the time down from hours to milliseconds. The major idea wasn't enough; we need a *massive* breakthrough. \n", + "\n", + "## Probabilistic Primality Testing\n", + "\n", + "**Fortunately**, [Pierre de Fermat](https://en.wikipedia.org/wiki/Pierre_de_Fermat) provided the massive breakthrough in 1640! \n", + "\n", + "[Fermat's little theorem](https://en.wikipedia.org/wiki/Fermat%27s_little_theorem) states that if *n* is prime and *a* is not divisible by *n*, then:\n", + "\n", + "        *a*(*n* - 1) ≡ 1 (mod *n*). \n", + "\n", + "We can use the theorem to create a [Fermat primality test](https://en.wikipedia.org/wiki/Fermat_primality_test): given an integer *n*, choose a random *a* and test if *a*(*n* - 1) ≡ 1 (mod *n*). If the test is false then *n* is definitely composite. If the test is true, then we're not sure: *n* might be prime, might not. But if we choose multiple values of *a* and they all give a remainder of 1 (mod *n*), then that is stronger evidence that *n* is probably prime. This is called a [Monte Carlo algorithm](https://en.wikipedia.org/wiki/Monte_Carlo_algorithm); an algorithm that uses [randomization](https://en.wikipedia.org/wiki/Randomized_algorithm), and can sometimes be wrong: it can incorrectly report a **false prime** for a composite number *n*. \n", + "\n", + "Some examples of testing *n* by choosing random values of *a*:\n", + "\n", + "\n", + "|*n*|*a*|*a*(*n* - 1) (mod *n*)|Conclusion|\n", + "|--:|--:|:--:|:--|\n", + "|12|5|5|*12 is definitely composite*|\n", + "|221|18|1|*221 could be prime or composite*|\n", + "|221|2|16|*try again with new value for **a**; 221 is definitely composite*|\n", + "|2137|2|1|*2137 could be prime or composite*|\n", + "|2137|3|1|*try again: 2137 could still be prime or composite*|\n", + "|2137|7|1|*try again: 2137 could still be prime or composite*|\n", + "|2137|99|1|*try again: 2137 could still be prime or composite, but a lot of evidence that it is prime*|\n", + "\n", + "\n", + "Here is a simple implementation:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "c8a59c5e-a01c-49be-a931-c1547c4ac712", + "metadata": {}, + "outputs": [], + "source": [ + "def isprime_fermat(n: int, k=20) -> bool:\n", + " \"\"\"If isprime_fermat(n) is True then n is probably prime; if False then n is definitely composite.\n", + " Increase `k` to decrease the probability of false primes.\"\"\"\n", + " if n <= 10:\n", + " return n in (2, 3, 5, 7)\n", + " else:\n", + " return all(pow(a, n - 1, n) == 1 \n", + " for a in sample(2, n, k))" + ] + }, + { + "cell_type": "markdown", + "id": "6746894c-00e7-41df-b4a3-d5c85862cc94", + "metadata": {}, + "source": [ + "(*Note* that `pow(a, n - 1, n)` computes *a*(*n* - 1) (mod *n*) very efficiently; see the last section of this notebook.)\n", + "\n", + "We need a way to sample *k* random integers from a range, one at a time:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "476282dd-a48a-4eb5-8fed-8495d4878ec2", + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "from typing import Iterable\n", + "\n", + "def sample(start: int, stop: int, k: int) -> Iterable[int]:\n", + " \"\"\"Randomly sample `k` integers from range(start, stop), one by one.\"\"\"\n", + " return (random.randrange(start, stop) for _ in range(k))" + ] + }, + { + "cell_type": "markdown", + "id": "15ef6884-253d-4a3a-b58f-397ac73f552d", + "metadata": {}, + "source": [ + "How common are false primes? I define one function to estimate the probability of a false prime by testing a sample of integers, and another function to print a table of false prime probabilities, over various values of *k*, and various ranges of numbers *n*:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "45a11a93-9ef3-48d1-80fa-e0113041346e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " label k = 1 k = 5 k = 10 k = 20 \n", + " 3 digits 1.431% 0.007% 0.000% 0.000%\n", + " 4 digits 0.471% 0.027% 0.001% 0.000%\n", + " 5 digits 0.120% 0.003% 0.003% 0.001%\n", + " 6 digits 0.031% 0.002% 0.002% 0.000%\n", + " 7 digits 0.011% 0.000% 0.000% 0.000%\n", + " 8 digits 0.003% 0.000% 0.000% 0.000%\n", + " 9 digits 0.001% 0.000% 0.000% 0.000%\n", + "10 digits 0.000% 0.000% 0.000% 0.000%\n", + "11 digits 0.000% 0.000% 0.000% 0.000%\n", + "12 digits 0.000% 0.000% 0.000% 0.000%\n" + ] + } + ], + "source": [ + "from statistics import mean\n", + "\n", + "def false_prime_probability(samples: Iterable[int], k: int) -> float:\n", + " \"\"\"The probability of false primes from isprime_fermat(n, k) for n across the samples.\"\"\"\n", + " composites = (n for n in samples if not isprime(n))\n", + " return mean(isprime_fermat(n, k) for n in composites)\n", + " \n", + "\n", + "def report_false_primes(label_start_stop_triples: Iterable, k_values=(1, 5, 10, 20), num_samples=100_000):\n", + " \"\"\"Report false prime probability for different digit ranges, and k values.\n", + " First arg is a list of triples such as (\"3 digits\", 100, 1000).\"\"\"\n", + " print(' label ', *[f'k = {k:<4}' for k in k_values])\n", + " for (label, start, stop) in label_start_stop_triples:\n", + " samples = list(sample(start, stop, num_samples))\n", + " print(f'{label:10} ', *[f'{false_prime_probability(samples, k):8.3%}' for k in k_values])\n", + "\n", + "report_false_primes([(f'{d:2} digits', 10 ** (d - 1), 10 ** d) \n", + " for d in range(3, 13)])" + ] + }, + { + "cell_type": "markdown", + "id": "f3fe25d6-5f0a-478b-90d0-6c89e092e92e", + "metadata": {}, + "source": [ + "We see that with *k* = 1, 3-digit composites (such as 123 or 747) are falsely called prime about 1.4% of the time, and 4-digit comnposites about 0.5%, but once we get to 10-digit numbers (such as 1234567891), or increase *k* to 10 or more repetitions, false primes are very rare indeed.\n", + "\n", + "The so-called [**Carmichael numbers**](https://en.wikipedia.org/wiki/Carmichael_number) are composite numbers with a very high false prime probability. [Some Carmichael numbers](https://oeis.org/A033502) are worse than others. \n", + "\n", + "We can test some of them:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "7871aaa7-af1c-462b-85bb-d46818ccabea", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " label k = 1 k = 10 k = 30 k = 100 k = 1000\n", + " 1105 69.560% 2.560% 0.000% 0.000% 0.000%\n", + " 1729 75.350% 5.450% 0.010% 0.000% 0.000%\n", + " 2465 72.460% 3.900% 0.020% 0.000% 0.000%\n", + " 2821 76.310% 6.910% 0.090% 0.000% 0.000%\n", + " 6601 80.270% 10.530% 0.130% 0.000% 0.000%\n", + " 8911 79.820% 10.910% 0.150% 0.000% 0.000%\n", + " 41041 70.160% 2.470% 0.000% 0.000% 0.000%\n", + " 101101 71.590% 3.590% 0.010% 0.000% 0.000%\n", + " 294409 95.080% 60.040% 22.570% 0.550% 0.000%\n", + " 340561 81.990% 14.420% 0.160% 0.000% 0.000%\n", + " 825265 60.780% 0.520% 0.000% 0.000% 0.000%\n", + " 56052361 99.110% 91.720% 77.240% 41.920% 0.010%\n", + " 118901521 99.350% 93.090% 81.470% 50.970% 0.150%\n", + " 172947529 99.350% 94.220% 83.660% 53.970% 0.200%\n", + " 216821881 99.470% 94.170% 84.640% 56.180% 0.430%\n", + "1299963601 99.700% 97.030% 91.020% 73.300% 4.720%\n", + "2301745249 99.790% 97.720% 92.210% 78.380% 8.150%\n", + "9624742921 99.870% 98.340% 95.090% 85.480% 20.660%\n" + ] + } + ], + "source": [ + "carmichael_numbers = (1105, 1729, 2465, 2821, 6601, 8911, 41041, 101101, 294409, 340561, 825265, \n", + " 56052361, 118901521, 172947529, 216821881, 1299963601, 2301745249, 9624742921)\n", + "\n", + "report_false_primes([(c, c, c + 1) \n", + " for c in carmichael_numbers], \n", + " k_values=(1, 10, 30, 100, 1000), num_samples=10_000)" + ] + }, + { + "cell_type": "markdown", + "id": "4ecd2289-66d5-4b0f-ac00-5db0ad92dde9", + "metadata": {}, + "source": [ + "This is bad; some Carmichael numbers have false prime probability above 99% with *k* = 1, and still over 50% with *k* = 100." + ] + }, + { + "cell_type": "markdown", + "id": "4c76de9d-4803-4db5-b6c5-811a6a1c6da1", + "metadata": {}, + "source": [ + "So our Fermat test is mostly reliable, but a few composite numbers will consistently be identified as false primes.\n", + "\n", + "**Fortunately**, there are variations on Fermat's idea that always give the right answer. `sympy.isprime` ([source code here](https://github.com/sympy/sympy/blob/master/sympy/ntheory/primetest.py)) uses the [Miller-Rabin test](https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test) (sometimes called Rabin-Miller, but I give Gary Miller precedence because he is my former colleague). The algorithm is guaranteed to give the right answer, but it does get slow for very large numbers. `sympy.isprime` gains efficiency by breaking the range of *n* values into subranges and cleverly precomputing a list of *a* values that work for every *n* in a subrange. For numbers greater than 264, `sympy.isprime` gives up on guaranteed correct answers and falls back on the [Baillie–PSW test](https://en.wikipedia.org/wiki/Baillie%E2%80%93PSW_primality_test), which is faster than Miller-Rabin, but is probabilistic. There are no known false positives for the Baillie-PSW test, but no proof that they don't exist.\n", + "\n", + "Below I test that all the primality test functions agree with `sympy.isprime` on the first 1,000 integers and a selection of larger primes and composites. (I don't use an `assert` because it is possible that `isprime_fermat` could fail, but a score of 100 indicates 100% success.)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "4f377075-e352-46f9-a716-f1de673a7086", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'isprime_simple': 100, 'isprime_faster': 100, 'isprime_fermat': 100}" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "integers = [*range(1000), 11939, 65537, 117223, 7629137, 11939*11939, 11939*65537]\n", + "prime_fns = (isprime_simple, isprime_faster, isprime_fermat, isprime)\n", + "\n", + "{fn.__name__: 100 * mean(fn(n) == isprime(n) for n in integers) \n", + " for fn in prime_fns if fn != isprime}" + ] + }, + { + "cell_type": "markdown", + "id": "bfd71dc7-7f6d-4c98-9886-5c242d9b297a", + "metadata": {}, + "source": [ + "## Speed of Primality testing\n", + "\n", + "**The great thing** about `isprime_fermat` and `sympy.isprime` is that they are **very fast**, handling even 32-digit primes in well under a millisecond. \n", + "\n", + "I'll make a chart of the speeds of all the primality checking functions running on primes with digit-lengths ranging from 5 to 32. For small *n* we repeat the test multiple time to get less noisy timing results; when a test takes over a second we skip the bigger primes." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "8109d9fc-bbae-4ef0-a75c-908d9746a557", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import timeit\n", + "import sympy\n", + "\n", + "big_primes = [sympy.nextprime(3 * 10 ** d) for d in range(4, 32)]\n", + "\n", + "def plot_run_times(primes, functions=prime_fns):\n", + " \"\"\"For each primality-testing function, plot its run time on primes of different digit-lengths.\"\"\"\n", + " plt.figure(figsize=(9, 6))\n", + " for function in functions:\n", + " plt.plot(*time_test(function, primes), 'o-', label=function.__name__)\n", + " plt.grid(True, axis='y', which=\"major\", ls=':', color='gray')\n", + " plt.xticks(range(len(str(min(primes))), len(str(max(primes))) + 1))\n", + " plt.yscale('log')\n", + " plt.xlabel('Number of digits in prime')\n", + " plt.ylabel('Run time in seconds (log scale)')\n", + " plt.legend()\n", + "\n", + "def time_test(function, primes, repeat=40) -> tuple[list[int], list[float]]:\n", + " \"\"\"Time the function on each of the primes, repeated `repeat` times, stopping \n", + " when one exceeds a second. Return two lists: digit sizes and corresponding run times.\"\"\"\n", + " D, T = [], [] # D is length of primes in digits; T is time in seconds\n", + " for p in primes:\n", + " time = timeit.timeit(lambda: function(p), number=repeat) / repeat\n", + " D.append(len(str(p)))\n", + " T.append(time)\n", + " if time > 1.0:\n", + " break # Stop testing a function once it takes over a second of run time\n", + " elif time > .01:\n", + " repeat = 1 # Reset `repeat` to 1 if it took .01 second\n", + " return D, T" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "b1ab0f3d-6ce6-4b8c-9d4d-bb78bb1ac432", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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D4jr168LvV4v5s+fLwDUuS5ZLBYxatSoSldg5aqrNcWuyNW66ii+EPWvq7XFlqOu2osvR121F1uuulpTfLnqqjN/U1fqqr34ctTXmmLLUVePHz8u/Pz8hFKpFIGBgeK7774TQshTT+3FrsiVeupMfCFc+011JyYMTUBhYaGIjIwUixcvliWeWq0Wffv2rTTvueeeE3fccYcs8cslJiaKe++9V9aYn3/+uWjVqpX4/PPPxfHjx8V//vMfERISIj755BNZ4l+4cEEMGjRIABBKpVL06dNHPProo+LWW291Ka69hCE9Pb3Sck888YQYPny4S7ErcnfCYDAYxJgxY8Sf/vQnkZeXJ1vswsJCcf78ebFv3z4xceJE0bZtW5GRkeFy/JSUFBEZGVnpR0XOhKGq9PR0oVarxVdffeVy/LS0NAFAPPLII5WWGzVqlHj44Yddil1V586dxZQpU+oUs7bYixYtEp06dRIbN24Ux44dE++8847w9/cX27ZtkyV+SkqKiIuLs9Xb4cOHixEjRogRI0bUKfazzz4rYmJixNWrV23z5KqrNcWuyNW66ii+K3W1tthy1NWa4stVVx19LuXqW1drii9XXXWm7K7UVXvx5aiv9mK7Wlf1er04f/68OHjwoHjppZdEWFiYOHnypCz11F7silypp87Ed/U31Z2YMDQRCQkJ4umnn5YlVps2bcSkSZMqzXv33XeFVquVJb4QQly+fFkoFAqxYcMG2WIKIUSrVq3E8uXLK8174403ROfOnWXdTmFhoe0fz4MPPihGjhzpUryqBzkXL14UAMThw4crLTd69Gjx+OOPuxS7IncmDAaDQYwdO1b06NGj3le/nDnoFkKIjh071utKUtX4b7/9tpAkSSiVStsDgFAoFCImJsal2PZ07NixUrva+sbX6/VCpVKJN954o9JyL7zwgujXr59LsSvatWuXACCOHj1a1yLXGLu4uFio1epqfYMmTZpU5+S4pvgV3bhxQ2RmZgohhLjtttvEs88+63TcKVOmiFatWonU1NRK8+Woq/ZiV+RKXXUU35W66kzZK6prXbUXX466Wp+y16Wu2osvR111puyu1FV78eWor86U3ZW6WtHQoUPFU089JetvatXYFcnZh6FqfDl+U92JfRiaAL1ej9OnTyM6OlqWeP3796821Nm5c+cQExMjS3wA+PjjjxEREYF7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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_run_times(big_primes)" + ] + }, + { + "cell_type": "markdown", + "id": "159833b0-afaf-486f-9c8e-1c8d2a11d9bf", + "metadata": {}, + "source": [ + "What have we learned about timing?\n", + "- `isprime_simple` has a run time of *O*(*n*)\n", + "- `isprime_faster` has a run time of *O*(√*n*); we see its slope is half as steep on the log-log graph\n", + "- `isprime_fermat` and `isprime` ([according to Wikipedia](https://en.wikipedia.org/wiki/Fermat_primality_test)) have a run time of *O*(*k* log2*n*), where *k* is the number of repeats \n", + "- `isprime_simple` takes more than a second for an 8-digit prime; `isprime_faster is 10,000 times faster for that prime\n", + "- `isprime_faster` takes more than a second for a 17-digit prime; `isprime_fermat` is over 10,000 times faster for that prime\n", + "- `isprime_fermat` and `isprime` are well under a millisecond run time all the way out to a 32-digit prime\n", + "- `isprime_fermat` is about ten times slower than `isprime`\n", + " - (But that's pretty good for `isprime_fermat` considering how much simpler it is: 8 lines versus about 800.)" + ] + }, + { + "cell_type": "markdown", + "id": "1347399a-24f2-4d5f-aa7f-822951126bf5", + "metadata": {}, + "source": [ + "This was fun for me! I remember being amazed by Fermat's Little Theorem when I learned about it in [Prof. Michael Rosen's](https://mathematics.brown.edu/people/michael-rosen) number theory class in college, but I never worked through the details of how often the test gives a false prime result until now. \n", + "\n", + "That number theory class was important for me in several ways. First, it was [hard fun](https://dailypapert.com/hard-fun/). Second, it was the first class that felt like actually *doing* math, rather than being taught some techniques to prepare you to do math some time in the future. And third, I learned a valuable lesson: there was a take-home final exam, handed out Friday and due Monday. I worked on it all day Saturday, and couldn't answer a single one of the ten problems. At that point, I thought to myself \"This never happens on my computer science assignments; I guess I'm more cut out for CS than math.\" On Sunday I came back to it and eventually got answers to most of the problems. (I remember that on one problem, Prof. Rosen said \"You had a very elegant solution for that one, better than the answer I expected. But then I realized it was so good, it could be extended to be a trivial solution to this known difficult problem, and so I looked more closely and noticed a flaw in your argument. Too bad.\")\n", + "\n", + "## Note on Modular Exponentiation\n", + "\n", + "Just one more thing: none of this would work unless we can efficiently compute *a*(*n* - 1) (mod *n*). How does the `pow` builtin function do it? When *a* and *n* are 24-digit numbers, if we naively tried to compute `a ** (n - 1)` and then apply (mod *n*), we'd have two problems: we'd need a billion petabytes to store the result, and we'd need centuries to compute it. The way around these problems is to use [modular exponentiation](https://en.wikipedia.org/wiki/Modular_exponentiation) where we apply the modulus to each intermediate result, and cut the exponent in half each iteration, so we need only do *O*(log *n*) multiplications, not *O*(*n*). That's a big difference: 1024 is a trillion trillion, and log2(1024) is only 80. \n", + "\n", + "There are two key ides that make this work:\n", + "1) *b*2*e* = (*b* × *b*)*e*\n", + "2) *b*2 (mod *m*) = (*b* (mod *m*))2 (mod *m*)\n", + " - Proof: let *r* = *b* (mod *m*); then *b* = (*km* + *r*) for some integer value of *k*, and:\n", + " - *b*2 (mod *m*) = (*km* + *r*)2 (mod *m*) = ((*km*)2 + 2*kmr* + *r*2) (mod *m*) = *r*2 (mod *m*)\n", + "\n", + "Here is an implementation:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "e71eb140-7e1a-4146-87a7-8c4eebf61486", + "metadata": {}, + "outputs": [], + "source": [ + "def modpow(b: int, e: int, m: int) -> int:\n", + " \"\"\"Compute b ** e mod m, efficiently.\"\"\"\n", + " return (1 if e == 0 else\n", + " b if e == 1 else\n", + " modpow((b * b) % m, e // 2, m) if e % 2 == 0 else\n", + " modpow((b * b) % m, e // 2, m) * b % m)" + ] + }, + { + "cell_type": "markdown", + "id": "6baf40c3-db36-40ba-b2d7-45afa21f811f", + "metadata": {}, + "source": [ + "We can see this function is fast and that it correctly computes the remainder of 1 for the following:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "01838139-52dd-48a0-8f62-bcdef351345e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 25 μs, sys: 1 μs, total: 26 μs\n", + "Wall time: 26.9 μs\n" + ] + }, + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 123456789012345678901234\n", + "n = 357686312646216567629137\n", + "\n", + "%time modpow(a, n - 1, n)" + ] + }, + { + "cell_type": "markdown", + "id": "4be033d1-93a0-4ef5-853e-81da0782ed20", + "metadata": {}, + "source": [ + "I hope you have enjoyed this excursion into the world of primes!" + ] + } + ], + "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 +}