Add files via upload

This commit is contained in:
Peter Norvig authored and GitHub committed 2026-01-12 08:22:23 -08:00
1 parent 9333518be1
commit 6986eda3cb
1 file changed
+87 -71
+87 -71
View File
@@ -3,9 +3,7 @@
{
"cell_type": "markdown",
"id": "8812d495-6014-4669-97ae-003be4f54605",
"metadata": {
"jp-MarkdownHeadingCollapsed": true
},
"metadata": {},
"source": [
"<div style=\"text-align:right\"><i>Peter Norvig<br>Jan 2026</i></div>\n",
"\n",
@@ -295,7 +293,7 @@
{
"cell_type": "code",
"execution_count": 10,
"id": "403a6451-c486-4868-acdc-7dd80a4c8f9d",
"id": "5a705ef4-b2b7-4006-b518-6e030de8fd30",
"metadata": {},
"outputs": [
{
@@ -310,7 +308,7 @@
}
],
"source": [
"{p for p in TP if str(p) == str(p)[::-1]}"
"{p for p in TP + TPr if str(p) == str(p)[::-1]}"
]
},
{
@@ -480,7 +478,7 @@
"\n",
"# Primality Testing\n",
"\n",
"I was very impressed with 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:"
"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:"
]
},
{
@@ -532,7 +530,7 @@
"id": "05b8cbf7-ea10-4047-bcdb-a5ac7981384b",
"metadata": {},
"source": [
"That's a noticeable improvement, but testing a 24-digit number would still take hours. We need a massive breakthrough. \n",
"For a 24-digit prime, `isprime_simple` would need to test trillions of trillions of divisors, while `isprime_faster` would \"only\" need to test trillions. That's the difference between never completing and taking a few hours. But we would like the test to take milliseconds. We need a massive breakthrough. \n",
"\n",
"Fortunately, [Pierre de Fermat](https://en.wikipedia.org/wiki/Pierre_de_Fermat) provided that breakthrough in 1640! \n",
"\n",
@@ -562,7 +560,7 @@
{
"cell_type": "code",
"execution_count": 16,
"id": "476282dd-a48a-4eb5-8fed-8495d4878ec2",
"id": "c8a59c5e-a01c-49be-a931-c1547c4ac712",
"metadata": {},
"outputs": [],
"source": [
@@ -571,8 +569,24 @@
" if n <= 10:\n",
" return n in (2, 3, 5, 7)\n",
" else:\n",
" return all(pow(a, n - 1, n) == 1 for a in sample(2, n, k))\n",
"\n",
" return all(pow(a, n - 1, n) == 1 for a in sample(2, n, k))"
]
},
{
"cell_type": "markdown",
"id": "6746894c-00e7-41df-b4a3-d5c85862cc94",
"metadata": {},
"source": [
"We need a way to take random samples of integers from a range:"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "476282dd-a48a-4eb5-8fed-8495d4878ec2",
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"from typing import Iterable\n",
" \n",
@@ -601,7 +615,7 @@
},
{
"cell_type": "code",
"execution_count": 17,
"execution_count": 18,
"id": "45a11a93-9ef3-48d1-80fa-e0113041346e",
"metadata": {},
"outputs": [],
@@ -624,26 +638,26 @@
},
{
"cell_type": "code",
"execution_count": 18,
"execution_count": 19,
"id": "9eba96df-c298-4d71-b68a-3bf36bf15095",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{3: 1.420427496905646,\n",
" 4: 0.4795347492645433,\n",
" 5: 0.13674836646356595,\n",
" 6: 0.03350572569618954,\n",
" 7: 0.010703085699607196,\n",
" 8: 0.0010595970352474955,\n",
" 9: 0.0010529807251878254,\n",
"{3: 1.433229785995569,\n",
" 4: 0.47277977521728054,\n",
" 5: 0.12450624459417024,\n",
" 6: 0.04328208013677137,\n",
" 7: 0.010154128989503837,\n",
" 8: 0.0021231309812580613,\n",
" 9: 0.0005263573440008422,\n",
" 10: 0,\n",
" 11: 0,\n",
" 12: 0}"
]
},
"execution_count": 18,
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
@@ -664,7 +678,7 @@
},
{
"cell_type": "code",
"execution_count": 19,
"execution_count": 20,
"id": "8880c588-32f5-46e8-b59c-f9b49d492d3b",
"metadata": {},
"outputs": [
@@ -674,8 +688,8 @@
"{3: 0,\n",
" 4: 0,\n",
" 5: 0,\n",
" 6: 0.0010824385174922064,\n",
" 7: 0,\n",
" 6: 0.0003613604498214879,\n",
" 7: 0.000356548185704557,\n",
" 8: 0,\n",
" 9: 0,\n",
" 10: 0,\n",
@@ -683,13 +697,13 @@
" 12: 0}"
]
},
"execution_count": 19,
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"{d: false_prime_percent(sample(10**(d-1), 10**d, 200_000), k=25) for d in range(3, 13)}"
"{d: false_prime_percent(sample(10**(d-1), 10**d, 300_000), k=25) for d in range(3, 13)}"
]
},
{
@@ -697,44 +711,44 @@
"id": "61ee44c3-f963-4fb1-ac20-4a27adc90f83",
"metadata": {},
"source": [
"In most runs this produces no false primes; in some runs one or two get through. \n",
"The results are randomized, and in some runs this produces no false primes; in some runs a few get through. \n",
"\n",
"There are a few numbers, called [**Carmichael numbers**](https://en.wikipedia.org/wiki/Carmichael_number), that have a high false prime percentage. [Some Carmichael numbers](https://oeis.org/A033502) are worse than others. \n",
"There are a few numbers, called [**Carmichael numbers**](https://en.wikipedia.org/wiki/Carmichael_number), that have a very high false prime percentage. [Some Carmichael numbers](https://oeis.org/A033502) are worse than others. \n",
"\n",
"We can test some of them:"
]
},
{
"cell_type": "code",
"execution_count": 20,
"execution_count": 21,
"id": "7871aaa7-af1c-462b-85bb-d46818ccabea",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{561: 56.32,\n",
" 1105: 68.95,\n",
" 1729: 74.45,\n",
" 2465: 72.27,\n",
" 2821: 77.05,\n",
" 6601: 80.08,\n",
" 8911: 80.47,\n",
" 41041: 71.11,\n",
" 101101: 71.25,\n",
" 294409: 95.19,\n",
" 340561: 82.67,\n",
" 825265: 59.95,\n",
" 56052361: 98.96,\n",
" 118901521: 99.4,\n",
" 172947529: 99.43,\n",
" 216821881: 99.55,\n",
"{561: 56.84,\n",
" 1105: 69.72,\n",
" 1729: 74.68,\n",
" 2465: 72.79,\n",
" 2821: 76.78,\n",
" 6601: 80.1,\n",
" 8911: 80.1,\n",
" 41041: 70.28,\n",
" 101101: 71.63,\n",
" 294409: 95.18,\n",
" 340561: 81.13,\n",
" 825265: 59.82,\n",
" 56052361: 99.19,\n",
" 118901521: 99.24,\n",
" 172947529: 99.45,\n",
" 216821881: 99.4,\n",
" 1299963601: 99.65,\n",
" 2301745249: 99.82,\n",
" 9624742921: 99.86}"
" 2301745249: 99.76,\n",
" 9624742921: 99.9}"
]
},
"execution_count": 20,
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
@@ -756,7 +770,7 @@
},
{
"cell_type": "code",
"execution_count": 21,
"execution_count": 22,
"id": "de492047-77b6-46a3-8a77-46308f219efd",
"metadata": {},
"outputs": [
@@ -772,25 +786,25 @@
" 8911: 0,\n",
" 41041: 0,\n",
" 101101: 0,\n",
" 294409: 8.05,\n",
" 294409: 4,\n",
" 340561: 0,\n",
" 825265: 0,\n",
" 56052361: 64.72,\n",
" 118901521: 71.33,\n",
" 172947529: 74.18,\n",
" 216821881: 75.86,\n",
" 1299963601: 85.84,\n",
" 2301745249: 87.53,\n",
" 9624742921: 92.3}"
" 56052361: 58,\n",
" 118901521: 66,\n",
" 172947529: 80,\n",
" 216821881: 86,\n",
" 1299963601: 90,\n",
" 2301745249: 87,\n",
" 9624742921: 94}"
]
},
"execution_count": 21,
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"{n: false_prime_percent([n] * 10_000, k=50) for n in carmichael_numbers}"
"{n: false_prime_percent([n] * 100, k=50) for n in carmichael_numbers}"
]
},
{
@@ -809,7 +823,7 @@
},
{
"cell_type": "code",
"execution_count": 22,
"execution_count": 23,
"id": "be5f2fe7-b1e0-4a02-b7f0-7903e865c4b8",
"metadata": {},
"outputs": [],
@@ -831,7 +845,7 @@
},
{
"cell_type": "code",
"execution_count": 23,
"execution_count": 24,
"id": "8109d9fc-bbae-4ef0-a75c-908d9746a557",
"metadata": {},
"outputs": [],
@@ -870,13 +884,13 @@
},
{
"cell_type": "code",
"execution_count": 24,
"execution_count": 25,
"id": "b1ab0f3d-6ce6-4b8c-9d4d-bb78bb1ac432",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAwwAAAINCAYAAACTVPhqAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAwCFJREFUeJzs3Qd8U1X7B/Bf925pKbS07L33RkAEBRQQFHG+Aiou/IsiS30FJ8OBOFBQX9wDREBlD5EhG2RZ2bvQlrZ07yT/z3NCQnfTNmnW7+vnmpObm5tzb9pyn3uec46LTqfTgYiIiIiIqBiuxa0kIiIiIiJiwEBERERERKViCwMREREREZWIAQMREREREZWIAQMREREREZWIAQMREREREZWIAQMREREREZWIAQMREREREZXIveSXSGi1Wly+fBkBAQFwcXHhSSEiIiIiuydzN6empiIiIgKurqW3ITBgKIMEC3Xq1DHn90NEREREZBMuXryI2rVrl7oNA4YSzJ8/Xy15eXnGkxkYGGj+b4mIiIiIqIqlpKSom+KSRVMWF520R1CpJzMoKAjJyckMGIiIiIjI6a5x2emZiIiIiIhKxICBiIiIiIhKxICBiIiIiIhKxE7PRERERFYg3UhlcBWNRsPzTxbh4eEBNze3Su+HAQMRERFRFcvJycGVK1eQkZHBc08WI3OIyZCp/v7+ldoPAwYiIiKiKp4U9uzZs+rOr0ya5enpyclhySItWFevXsWlS5fQpEmTSrU0MGAoYx4GNhMSERGRuVsXJGiQMfB9fX15csliatSogXPnziE3N7dSAQM7PZdg/PjxiIqKwt69eyt8comIiIhKvAhz5WUYWT4lyRz4k0pERERERCViwEBERERERCViwEBERERkpzRaHXaeTsCvB6PVozy3pJtvvhnPPfecxfYv+faSRnPw4EHYonNVWL/69etj3rx5sAXs9ExERERkh9YevYLXfo/CleQs47paQd6YMbQlBrWuZZHPXLZsmRrb31KkI7gMNxsaGgpbVMfG62cpbGEgIiIissNg4anvDhQIFkRMcpZaL69bQkhICAICAiw2epSM5BMeHg53d9u8p+1m4/WzFAYMZHequvmViIioKsbMz8jJM2lJzcrFjN/+QXH/+hnWvfpblNrOlP3JZ1ckJemTTz5R4/t7e3sjLCwMI0eOLLDdM888o5agoCB1R/6VV14p8FmScvPGG2/g4YcfRmBgIB5//PEiKT9//vmner5u3Tp06NABPj4+uOWWWxAXF4c1a9agRYsW6r0PPPBAgUnwZNjaWbNmoUGDBuo97dq1w9KlS006xmvXruHBBx9UQ5LKe5s0aYIvv/xSvWau+plyfgpLSkrCY489puol+5TPOXToEKqCc4VHZPes0fxKRERkaZm5GrScvs4s+5JLzpiULLR5db1J20e9PhC+nuW7JNy3bx+effZZfPvtt+jZsycSExOxbdu2Att8/fXXePTRR7Fnzx61vQQEdevWxbhx44zbvPvuu5g+fTpmzJhR6ue9+uqr+Pjjj9W8FaNGjVKLl5cXfvjhB6SlpWHEiBH46KOPMHXqVLW9BAvfffcdFixYoC74t27dioceekhdbPft27fUz5ILdxlaXy745UL+1KlTyMzMNGv9TD0/+d1zzz0qIJF6SZCxcOFC9O/fHydOnFAtP5bEgIHsrvm1cOxtaH799KGODBqIiIiqwIULF+Dn54chQ4aoFKV69eqpO+yF8/3ff/99dQe+WbNmOHLkiHqe/4JY7pK/8MILxudyB784b775Jnr16qXKcpH94osv4vTp02jYsKFaJ60bmzdvVhfk2dnZmDlzJjZu3IgePXqo12W77du3q4vssgIGOTY5ls6dOxtbQspSnvqV5/wYSN0lsJCWCwlEDMHWihUrVMuJBBuWxICB7IKkHUnLQknNrzItibx+a8twuLmaZ5ISIoen1QDndwBpsYB/GFCvJ+Ba8ZlAiajifDzc1J1+U+w5m4gxX5Y9sexXY7uga4MQkz67vG699VYVJMgF8aBBg9Qid9Hzz1zdvXv3AhOHycX7e++9B41GY5x12HBRXpa2bdsay5L+JJ9juBg3rJMLaiEtApL+I3Us3EeicFBTnKeeegp33303Dhw4gNtuuw3Dhw9XrSjmql95zo+BpB5JS0X16tULrJeWDwlMLI0BQwnmz5+vFvnSyPrkj2Phjl2FgwZ5Xbbr0ajgLxMRFSPqN2DtVCDl8o11gRHAoDlAy2E8ZURVTC4cTU0L6t2khkrHlRb24m6kySVoeJC32s5SN9GkVUEuqCWHf/369SqtSNJy9u7di2rVqpm8H2mlMEX+kZnkXBUeqUnWSb8FIRfWYtWqVYiMjCywneHufGkGDx6M8+fPY/Xq1diwYYNK+xk/fry6o2+O+lWEHFOtWrXU+S6sPOe7otjpuQTygyH5a/KDT9YXl5pl1u2InJoEC0seLhgsiJQr+vXyOhHZLAkCpO+eKBwOGJ7L65ZucZeRggYMGIC3334bhw8fVulEf/zxh/H13bt3F9h+165dqj9B4bvn5tayZUsVGEhqUePGjQsskgZkCunrMHr0aNUPYt68efjss8/MXs/ynJ+OHTsiJiZGnfPCx1QVQ7yyhYHsQs0Ab7NuR+TUaUjSslBagt/aaUDzO5ieRGTDZKAP6btXeCCQ8CoaCGTlypU4c+YM+vTpg+DgYHU3Xu6gSy6+gVywT5w4EU888YRqjZBOv5JyY2nS+jFp0iQ8//zzqk433XQTkpOT8ddff6nRhSQQKI20lnTq1AmtWrVS/SFWrlypRjsyt/KcHwnMJGVJ0qMkQGvatCkuX76sWlEkFczU1K6KYsBAdkFyMKX5taS0JEPzqym5mkROTfosFG5ZKEAHpETrt2vQuworRkTlJUGB9N2TdFxpYZebZvLvYFX05ZM0GJnETdKQsrKy1J3xH3/8UV1kG8hwqZJj37VrV3XXfMKECRbvnGsgw7VKK4GMliSBjdRX7tK/9NJLZb7X09NTdVqWFhMZlah379746aefzF7H8pwfSWmSoOzll1/G2LFjcfXqVTUfhARs0j/C0lx05Rl81wmlpKSooaskMpWolKw7StKT3x0ost7wZ5GjJBGZ4MhS4JdHy97u7v8BbW6MqU5E5iMX2GfPnlVzBMgcBo5I5hlo3769Such652f0n7WynONyz4MZDdualIDnm5Ff2SlZYHBApGJZDQkc25HREQOjylJZDdWH76CHI0WDUJ9MXNEG8SlZldp8yuRQ6jbA/D0B3L0o4gU5aIfLUmGWCUickBPPvmk6sxcHJncTSZ7o4IYMJDdWLLvonoc1bkuejSy/IgARA5HhvRbM6X0YEEMms0Oz0RUKcUN/2krXn/9ddUpujhVlX7+pw2fn+IwYCC7cPpqGvadv6ZaEu7uWHBMZSIygSYP+O0Z4NCP+sCg0xjg5Lpi5mGYzXkYiMih1axZUy1kOgYMZBeW7r+kHvs2rYGagY7ZQYzIYvJy9B2d//0NcHEDRiwE2t7DmZ6JiMgkDBjI5uVptPjlesAwqnNta1eHyL7kZgKL/wOc2gC4eQIjvwRaDNG/5urGoVOJiKhMDBjI5m07Ga86OIf4eeKW5hy5hchk2anAD/cB57cD7j7A/T8AjW7hCSQionJhwEB209l5ePtIeLpzJGAik2QkAt+PBKL3A54BwINLOPIRERFVCAMGsmmJ6TnY+G+sKt/DdCQi06TFAd+OAGKPAj7BwEPLgMiOPHtERFQhTnG7duXKlWjWrJmatvyLL76wdnWoHFb8HY1cjQ5tIoPQohZn2iYqU3I08OVgfbAgk6+NWc1ggciRaTXA2W36WdzlUZ5beIbi5557zmL7P3fuHFxcXHDw4EFY019//YU2bdrAw8MDw4cPh7Nz+BaGvLw8TJw4EZs3b1bTX3fq1AkjRoxA9erVrV01KoNOp8s39wI7OxOVKfEM8M2dQNIFILA2MPo3oHojnjgiRxX1G7B2ajHDI8+x2PDIy5YtUxfRllKnTh1cuXIFoaHWnW9Jrh3bt2+PNWvWwN/fv9L7e/XVV7FixQqrB0IV5fAtDHv27EGrVq0QGRmpvvDBgwdj/fr11q4WmeCfyyk4FpOq+i0Ma8e5F4hKFXcMWDRYHyyENAQeWctggcjRg4UlDxcMFkTKFf16ed0CQkJCEBAQYJF95+TkwM3NDeHh4XB3t+497dOnT+OWW25B7dq1Ua1aNdgKOUfWYPMBw9atWzF06FBERESoJiqJzgqbP38+6tevD29vb3Tr1k0FCQaXL19WwYKBlKOjo6us/lRxhtaFga3CEeRrubsZRHbv8kHgq9uBtBigZktg7FqgWh1r14qIykOnA3LSTVuyUvSztkNX3I70D9LyINuZsj/57AqkJH3yyScq3Vuuv8LCwjBy5MgC2z3zzDNqkQwPaTF45ZVXVPaAgVy7vfHGG3j44YfVDMuPP/54kZQkmRFZnq9btw4dOnSAj4+PupCPi4tTd/9btGih3vvAAw8gIyPDuG+tVotZs2ahQYMG6j3t2rXD0qVLyzw+w+cnJCTgkUceUeWvvvoKGo0Gjz76qHF/kur+wQcfFHiv1LVr167w8/NTQUavXr1w/vx59f7XXnsNhw4dUvsz7FMkJSXhscceQ40aNdRxyLHJdvlbJqSlQ1Lq5bPlXFuDzackpaenqy9ZvrS77rqryOuLFy9WzUYLFixQwcK8efMwcOBAHD9+nLP42bGsXA1+Pai/a3JPJ6YjEZXowm7g+3uA7GQgooO+g7NvCE8Ykb3JzQBmRphpZzp9y8NsE28cvHQZ8PQr1yfs27cPzz77LL799lv07NkTiYmJ2LZtW4Ftvv76a3WRLTdyZXsJCOrWrYtx48YZt3n33Xcxffp0zJgxo9TPkwvnjz/+GL6+vhg1apRavLy88MMPPyAtLU2lm3/00UeYOnWq2l6Che+++05dH0pQIzegH3roIXVLine truncated
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAwwAAAINCAYAAACTVPhqAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAwGpJREFUeJzs3Qd8U1X7B/BfuncpnbTsIVL2liUyFJQhQ/EVF+6BrwiC4MTXAbgQB/5Bfd0TEX2VJYjI3hsqsjedQPdM8v88J01oS0fSJs36ff1cc3Jzc3PuTWnvc89zztHo9Xo9iIiIiIiIyuFR3koiIiIiIiIGDEREREREVCm2MBARERERUYUYMBARERERUYUYMBARERERUYUYMBARERERUYUYMBARERERUYUYMBARERERUYW8Kn6JhE6nw7lz5xAcHAyNRsOTQkREREROT+ZuzszMRGxsLDw8Km9DYMBQBQkWGjRoYM3vh4iIiIjIIZw+fRr169evdBsGDFWQlgXjyQwJCbHet0NEREREZCcZGRnqprjxWrcyDBiqYExDkmCBAQMRERERuRJzUu7Z6ZmIiIiIiCrEgIGIiIiIiCrEgIGIiIiIiCrEPgxEREREdhrWsqioCFqtluefbMLb2xuenp413g8DhgrMnTtXLfxHTERERNZWUFCA8+fPIycnhyeXbNqhWYZMDQoKqtl+9BLeUqVDToWGhiI9PZ2jJBEREZFVJoU9fPiwuvMbGRkJHx8fTg5LVieX+CkpKSoobdGixRUtDZZc47KFgYiIiKiWWxckaJAx8AMCAnjuyWYkID1x4gQKCwtrlJrETs9EREREduDhwcswsv8cC+bgTyoREREREVWIAQMREREREVWIAQMRERGRk9Lq9Nh0NA3/231WPcpzW7ruuuvw5JNP2mz/km8vaTS7d++GIzpRi/Vr3Lgx5syZA0fATs9ERERETmj5/vP4z28JOJ+eZ1pXL9QP04fFY3Cbejb5zEWLFqmx/W1FOoLLcLMRERFwRA0cvH62whYGIiIiIicMFh79emepYEEkpuep9fK6LdStWxfBwcE2Gz1KRvKJiYmBl5dj3tP2dPD62QoDBnI6td38SkREVBtj5ucUFJm1ZOYVYvqvB1DeXz/jupd+TVDbmbM/S6bkKpmS9OGHH6rx/f38/BAdHY1bbrml1HaPP/64WmSsf7kj/8ILL5T6LEm5eeWVV3D33XereQAeeuihK1J+/vrrL/X8999/R8eOHeHv74/+/fsjOTkZy5YtQ6tWrdR7x44dW2oSPBm2dubMmWjSpIl6T/v27bFw4UKzjvHixYu444471JCk8t4WLVrgs88+U69Zq37mnJ+yLl26hAceeEDVS/Ypn7Nnzx7UBvcKj8jp2aP5lYiIyNZyC7WIf/F3q+xLLjkTM/LQ9qUVZm2f8PIgBPhYdkm4fft2PPHEE/jqq6/Qs2dPXLhwAevWrSu1zRdffIH7778fW7duVdtLQNCwYUM8+OCDpm3eeustvPjii5g+fXqln/fSSy/hgw8+UPNWjBkzRi2+vr749ttvkZWVhZEjR+L999/H1KlT1fYSLHz99deYN2+euuBfu3Yt7rzzTnWx3bdv30o/Sy7cExIS1AW/XMgfOXIEubm5Vq2fueenpFtvvVUFJFIvCTLmz5+PAQMG4NChQ6rlx5YYMJDTNb+Wjb2Nza//d2cnBg1ERES14NSpUwgMDMTQoUNVilKjRo3UHfay+f7vvPOOugPfsmVL7Nu3Tz0veUEsd8mfeuop03O5g1+eV199Fb169VJluch+5plncPToUTRt2lStk9aN1atXqwvy/Px8zJgxA3/88Qd69OihXpft1q9fry6yqwoY5NjkWLp06WJqCamKJfWz5PwYSd0lsJCWCwlEjMHWL7/8olpOJNiwJQYMFZg7d65atFqtTb8AMo+kHUnLQkXNrzItibx+fXwMPD2sM0kJkcvTaYGTG4GsJCAoGmjUE/Co/kygRFR9/t6e6k6/ObYev4Bxn22rcrvP7+2Kbk3qmvXZlrr++utVkCAXxIMHD1aL3EUvOXP1NddcU2riMLl4f/vtt9W1lXHWYeNFeVXatWtnKkv6k3yO8WLcuE4uqIW0CEj6j9SxbB+JskFNeR599FGMHj0aO3fuxA033IARI0aoVhRr1c+S82MkqUfSUhEeHl5qvbR8SGBiawwYKjB+/Hi1ZGRkqGYfsi/55Vi2Y1fZoEFel+16NCv9j4mIypHwK7B8KpBx7vK6kFhg8OtA/HCeMqJaJheO5qYF9WkRqdJxpYW9vBtpcgkaE+qntrPVTTRpVZALasnhX7FihUorkrScbdu2oU6dOmbvR1opzFFyZCY5V2VHapJ10m9ByIW1WLJkCeLi4kptZ7w7X5kbb7wRJ0+exNKlS7Fy5UqV9iPXhHJH3xr1qw45pnr16qnzXZYl57u62OmZnEJyZp5VtyNyaxIsLLi7dLAgMs4b1svrROSwJAiQvnuibDhgfC6v27rFXUYKGjhwIN544w3s3btXpRP9+eefpte3bNlSavvNmzer/gRl755bW3x8vAoMJLWoefPmpRZJAzKH9HW45557VD+IOXPm4KOPPrJ6PS05P506dUJiYqI652WPqTaGeGULAzmFqGA/q25H5NZpSNKyUFmC3/JpwNVDmJ5E5MBkoA/pu1d2IJCYWhoIZPHixTh27BiuvfZahIWFqbvxcgddcvGN5IJ90qRJePjhh1VrhHT6lZQbW5PWj8mTJ2PixImqTr1790Z6ejo2bNigRheSQKAy0lrSuXNntG7dWvWHWLx4sRrtyNosOT8SmEnKkqRHSYB21VVX4dy5c6oVRVLBzE3tqi4GDOQUJAdTml8rSksyNr+ak6tJ5Nakz0LZloVS9EDGWcN2TfrUYsWIyFISFEjfPUnHlRZ2uWkmfwdroy+fpMHIJG6ShpSXl6fujH/33XfqIttIhkuVHPtu3bqpu+YTJkyweedcIxmuVVoJZLQkCWykvnKX/tlnn63yvT4+PqrTsrSYyKhEffr0wffff2/1OlpyfiSlSYKy5557Dvfeey9SUlLUfBASsEn/CFvT6C0ZfNcNGfswSGQqUSnZd5SkR77eecV6469FjpJEZIZ9C4Gf7q96u9H/BdpeHlOdiKxHLrCPHz+u5giQOQxckcwz0KFDB5XOQ/Y7P5X9rFlyjcs+DOQ0ereIhI/XlT+y0rLAYIHITDIakjW3IyIil8eUJHIaS/eeR0GRDk3CAzBjVFskZ+bXavMrkUtocA3gHQAUXp5xtDSNYbQkGWKViMgFPfLII6ozc3lkcjeZ7I1KY8BATuPHHafV461dG6BHM9uPCEDkcrRFwG//rjxYEINnscMzEdVIecN/OoqXX35ZdYouT22ln//lwOenPAwYyCkcS8nCthMXIQ0JozvVt3d1iJyPthBY9CBw4GdA4wl0fxhI+KWceRhmcR4GInJpUVFRaiHzMWAgp7Bwxxn1eO1VkYgOcc0OYkQ2U5gH/DgOOLQM8PAGbv0MaDUMuOFVzvRMRERVYsBADk+r02PRzrOqfGtn8yZcIaJiBTnA92OBY6sBLz/gtm+AFgMNr3l4cuhUIiKqEgMGcnjrDqcgMSMPdQK8MTCeTYhEZsvPBL69DTi5AfAOBMZ+DzS5lieQiIgswoCBHN6P2w3pSCM6xMHXy7bTyRO5jNyLwNe3AGe3A74hwB0LgYbd7V0rIiJyQm4xD4NM6S1TlcsshJ988om9q0MWuJRTgJUJSap8S2d2diYyS3Yq8MUwQ7DgHwbc8yuDBSIiqjaXDxiKioowadIk/Pnnn9i1axfefPNNpKWl2btaZKb/7T6HAq0O8fVC0CYulOeNqCqZicDnQ4DEfUBgFDBuCRDbkeeNyFXptMDxdYZZ3OVRntt4huInn3zSZvs/ceIENBoNdu/eDXvasGED2rZtC29vb4wYMQLuzuVTkrZu3YrWrVsjLi5OPb/xxhuxYsUK3H777fauGlky90IXti4QVenSaeDL4cCFY0BwrKFlIaIFTxyRq0r4FVg+tZzhkV+32fDIixYtUhfRttKgQQOcP38eERH2nW9JbjZ36NABy5YtQ1BQUI3399JLL+GXX36xeyDksi0Ma9euxbBhwxAbG6siTjnZZc2dOxeNGzeGn58funfvroIEo3PnzpmCBSHls2cNI+6QY0s4l4H9ZzPg7anBzR0uf4dEVA4JEj67yfBYpyFw3zIGC0SuHiwsuLt0sCAyzhvWy+s2ULduXQQHB9tk3wUFBfD09ERMTAy8vOx7T/vo0aPo378/6tevjzp16sBRyDmyB4cPGLKzs9G+fXsVFJTnhx9+UFHg9OnTsXPnTrXtoEGDkJycXOt1Jdu0LlwfH426gT48vUQVSTlkCBbSTwHhzYF7lwNhjXm+iJyJXg8UZJu35GUAy56WN5W3I8ODtDzIdubsTz67GilJH374oeofKjdso6Ojccstt5Ta7vHHH1dLaGioajF44YUXoC/xWXKz95VXXsHdd9+tZlh+6KGHrkhJkhmR5fnvv/+Ojh07wt/fX13Iy3We3P1v1aqVeu/YsWORk3N5FnudToeZM2eiSZMm6j1yfbhw4cIqj8/4+ZK+ft9996ny559/Dq1Wi/vvv9+0P+kb++6775Z6r9S1W7duCAwMVEFGr169cPLkSfX+//znP9izZ4/an3Gf4tKlS3jggQcQGRmpjkOOTbYr2TIhLR3SB1c+W861PTh8SpKkEMlSkdmzZ+PBBx/Evffeq57PmzcPS5Yswaeffopp06aplomSLQpSli+zIvn5+WoxysjIUI+FhYWmPhFCIl9ZJ1962bIxQpalbFle9/DwUJ8hTXrllX18DBfHsn3Jsq+vr/oHIJ9VXlnqJtvLD7UsZcvyuvxDlc8qW3a0Y9J4euOXXZfnXnCFY3LF74nH5ADf04V/gK9GQpOTCkTFQ3fnIhT61oVv8R9Mfk/898TfEY73e8/Line truncated
"text/plain": [
"<Figure size 900x600 with 1 Axes>"
]
@@ -915,7 +929,7 @@
},
{
"cell_type": "code",
"execution_count": 25,
"execution_count": 26,
"id": "4f377075-e352-46f9-a716-f1de673a7086",
"metadata": {},
"outputs": [],
@@ -930,18 +944,20 @@
"id": "1347399a-24f2-4d5f-aa7f-822951126bf5",
"metadata": {},
"source": [
"This was fun for me! I remember learning being excited about Fermat's Little Theorem 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",
"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. 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*<sup>(*n* - 1)</sup> (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)`, we'd have two problems: we'd need about 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 because log<sub>2</sub>(10<sup>24</sup>) is only 80. \n",
"Just one more thing: none of this would work unless we can efficiently compute *a*<sup>(*n* - 1)</sup> (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)`, we'd have two problems: we'd need nearly 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: 10<sup>24</sup> is a trillion trillion, and log<sub>2</sub>(10<sup>24</sup>) is only 80. \n",
"\n",
"The key idea is that *b*<sup>2*e*</sup> is equal to (*b* × *b*)<sup>*e*</sup>. Here is an implementation:"
]
},
{
"cell_type": "code",
"execution_count": 26,
"execution_count": 27,
"id": "e71eb140-7e1a-4146-87a7-8c4eebf61486",
"metadata": {},
"outputs": [],
@@ -959,12 +975,12 @@
"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 follwoing:"
"We can see this function is fast and that it correctly computes the remainder of 1 for the following:"
]
},
{
"cell_type": "code",
"execution_count": 27,
"execution_count": 28,
"id": "01838139-52dd-48a0-8f62-bcdef351345e",
"metadata": {},
"outputs": [
@@ -972,8 +988,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 25 μs, sys: 1 μs, total: 26 μs\n",
"Wall time: 28.1 μs\n"
"CPU times: user 25 μs, sys: 0 ns, total: 25 μs\n",
"Wall time: 27.2 μs\n"
]
},
{
@@ -982,7 +998,7 @@
"1"
]
},
"execution_count": 27,
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}