{ "cells": [ { "cell_type": "markdown", "id": "fc5197e8-5f15-4809-8356-8eb70471637a", "metadata": {}, "source": [ "
Peter Norvig
Mar 2024
\n", "\n", "# Stubborn Number Endings\n", "\n", "[Francis Su](https://www.francissu.com/)'s book *Mathematics for Human Flourishing* mentions the fact that numbers that end in \"5\" have a square that also ends in \"5\". \n", "\n", "For example, 5² = 25, 15² = 225, and 25² = 625. \n", "\n", "This leads to some questions:\n", "\n", "- Is there an easy way to calculate the square of a number ending in \"5\"?\n", "- What should we call this property of \"square has same ending\"?\n", "- Are there other digits besides 5 that have this property?\n", "- Can we prove the property, not just show some examples?\n", "- Are there multi-digit endings that have this property?\n", "\n", "\n", "## Is there an easy way to calculate the square of a number ending in \"5\"?\n", "\n", "Let's make a table of {number: square} pairs and try to see a pattern:" ] }, { "cell_type": "code", "execution_count": 1, "id": "fd3c4f25-fb63-43e5-925c-e4b7b8f134ec", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{15: 225,\n", " 25: 625,\n", " 35: 1225,\n", " 45: 2025,\n", " 55: 3025,\n", " 65: 4225,\n", " 75: 5625,\n", " 85: 7225,\n", " 95: 9025}" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "{i: i ** 2 for i in range(15, 100, 10)}" ] }, { "cell_type": "markdown", "id": "44d2cf09-b397-46d7-ab4f-e068e571895d", "metadata": {}, "source": [ "I see a pattern here: for example 95 squared is 9025, which you get by taking the \"9\", multiplying it by one more than itself to get 9⋅10 = 90, then squaring 5 to get 25, then putting the \"90\" next to the \"25\" to get \"9025\".\n", "\n", "Does the trick also work for numbers with more digits? Let's investigate:" ] }, { "cell_type": "code", "execution_count": 2, "id": "7681cb5f-bf5a-4317-b842-e0be21745d90", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{105: 11025,\n", " 115: 13225,\n", " 125: 15625,\n", " 135: 18225,\n", " 145: 21025,\n", " 155: 24025,\n", " 165: 27225,\n", " 175: 30625,\n", " 185: 34225,\n", " 195: 38025,\n", " 205: 42025,\n", " 215: 46225,\n", " 225: 50625,\n", " 235: 55225,\n", " 245: 60025}" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "{i: i ** 2 for i in range(105, 250, 10)}" ] }, { "cell_type": "markdown", "id": "89b1e783-0546-4305-989c-b3b83781cf92", "metadata": {}, "source": [ "Yes, it looks like the trick still works: 105 squared is 10⋅11 = \"110\", followed by \"25,\" to make \"11025\". And 245 squared is 24⋅25 = \"600\", followed by \"25\", to make \"60025\". \n", "\n", "\n", "## What should we call this property?\n", "\n", "Let's define an **ending** as the rightmost-digits (zero or more) of a number in decimal notation. \n", "\n", "Then we can say that an ending is **stubborn** if every number with that ending has a square with the same ending.\n", "\n", "## Are there other digits besides 5 that are stubborn?\n", "\n", "We could work this out in our heads, or with paper and pencil, or we can compute it with an expression:" ] }, { "cell_type": "code", "execution_count": 15, "id": "c86f99ed-48fa-4954-9467-b72d67252d73", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'0', '1', '5', '6'}" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "digits = '0123456789'\n", "\n", "{e for e in digits if str(int(e) ** 2).endswith(e)}" ] }, { "cell_type": "markdown", "id": "f4cb87cf-2fa8-4d2e-bc24-94c378930792", "metadata": {}, "source": [ "These are the four digits whose square ends in the same digit.\n", "\n", "## Can we prove stubborness?\n", "\n", "We have seen that 0² ends in 0, 1² ends in 1, 5² ends in 5, and 6² ends in 6. And we have checked some numbers with those endings, for example, 245² ends in 5. But can we **prove** that **every** number ending in 0, 1, 5, or 6 has a square that ends in the same digit?\n", "\n", "Some notation: I'll use quote marks, as in \"*se*\" to mean the string of staring digits \"*s*\" followed by the string of ending digits \"*e*\".\n", "\n", "With a little bit of algebra we can see that if *s* is any string of digits and *e* is a single ending digit, then:\n", "\n", "\"*se*\"² = (10⋅*s* + *e*)² = (10⋅*s*)² + 2⋅(10⋅*s* ⋅ *e*) + *e*² = 10 ⋅ (10⋅*s*² + 2⋅*s*⋅*e*) + *e*²\n", "\n", "This is ten times some integer, plus *e*², so \"*se*\"² ends in the digit *e* if and only if *e*² ends in *e*, and we know that is true for 0, 1, 5, and 6, and for no other digits.\n", "\n", "## Are there multi-digit endings that are stubborn?\n", "\n", "The algebraic argument above can be extended to work with an ending string *e* that is *k* digits long:\n", "\n", "\"*se*\"² = (10*k*⋅*s* + *e*)² = (10*k*⋅*s*)² + 2⋅(10*k*⋅*s* ⋅ *e*) + *e*² = 10*k* ⋅ (10*k*⋅*s*² + 2⋅*s*⋅*e*) + *e*²\n", "\n", "This is 10*k* times some integer, plus *e*², so again \"*se*\"² ends in *e* if and only if *e*² ends in *e*. To put it another way, to test whether *e* is stubborn, all we have to do is square *e* and see if the result ends in *e*. There is one complication: we'd like to say that \"00\" is stubborn, because any number ending in \"00\", when squared, also ends in \"00\", for example 200² = 40000. But 00² is zero, which we write as \"0\", not as \"00\" or \"0000\". To make sure that \"00\" is considered stubborn, I will define the predicate function `stubborn(ending)` to test the square of \"1\" followed by the ending (I could have chosen any other starting digit string and the result would be the same). \n" ] }, { "cell_type": "code", "execution_count": 4, "id": "1419b122-257d-475b-90c6-e832c0aae8cf", "metadata": {}, "outputs": [], "source": [ "def stubborn(ending: str) -> bool:\n", " \"\"\"Does the square of any number with this ending also end with this ending?\"\"\"\n", " return str(int(\"1\" + ending) ** 2).endswith(ending)" ] }, { "cell_type": "markdown", "id": "9ec3ae19-96cb-4bea-ae6f-075b3ade7d3b", "metadata": {}, "source": [ "Now we can find all two-digit stubborn endings as follows:" ] }, { "cell_type": "code", "execution_count": 17, "id": "57e64e31-0674-4849-bf72-f6c6b437009a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['00', '01', '25', '76']" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[s + e for s in digits for e in digits if stubborn(s + e)]" ] }, { "cell_type": "markdown", "id": "61a2b8e2-2bdf-458f-adaf-ffde56125862", "metadata": {}, "source": [ "We could easily continue on in this way, enumerating all three-, four- or even six-digit endings, and checking each one to see if it is stubborn. There are only a million six-digit endings. But there are a quadrillion 15-digit endings, so it would take a *long* time to check all of those. And 100-digit endings? Forget about it. Instead we need to rely on a simplification:\n", "- Any two-digit stubborn ending ('00', '01', '25', '76') has to end in a one-digit-stubborn ending ('0', '1', '5', '6').\n", "- In general, any *d*-digit stubborn ending has to end in a (*d*-1)-digit stubborn ending.\n", "- So, to find the stubborn endings of length 100, I don't need to generate and test all 10100 endings, I only need to consider the stubborn endings of length 99 and check each one of them. (If this simplification is not obvious, stop and convince yourself it is true.)\n", "\n", "Using this simplification we can efficiently compute all stubborn-endings of a given length *d* as follows (caching greatly improves efficiency):" ] }, { "cell_type": "code", "execution_count": 6, "id": "abeba9cb-1357-4e2b-a661-8616d0d3f768", "metadata": {}, "outputs": [], "source": [ "from functools import lru_cache\n", "\n", "@lru_cache(None)\n", "def stubborn_endings(d: int) -> list:\n", " \"\"\"A list of all stubborn endings of length `d` digits.\"\"\"\n", " if d == 0:\n", " return [''] # The empty ending is the sole stubborn ending of length 0.\n", " else:\n", " return [(s + e) for e in stubborn_endings(d - 1) \n", " for s in digits \n", " if stubborn(s + e)]" ] }, { "cell_type": "markdown", "id": "05c524e7-6fa8-4fdd-875d-ef5cc0453560", "metadata": {}, "source": [ "For example:" ] }, { "cell_type": "code", "execution_count": 7, "id": "3943be37-ec8a-4ec0-b946-acb2f449dd72", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['000', '001', '625', '376']" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "stubborn_endings(3)" ] }, { "cell_type": "code", "execution_count": 8, "id": "d1dc31eb-e5dd-405e-b16d-eceef5c9493c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['0000', '0001', '0625', '9376']" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "stubborn_endings(4)" ] }, { "cell_type": "code", "execution_count": 9, "id": "81730e6b-7218-4fca-9107-2a9539f62ea3", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['00000', '00001', '90625', '09376']" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "stubborn_endings(5)" ] }, { "cell_type": "code", "execution_count": 10, "id": "288506c8-6dc1-40d5-842a-eb65ed94ae98", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['000000', '000001', '890625', '109376']" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "stubborn_endings(6)" ] }, { "cell_type": "code", "execution_count": 11, "id": "73491ea5-a4c8-4221-8fc7-d6ce44d17bb4", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',\n", " '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001',\n", " '3953007319108169802938509890062166509580863811000557423423230896109004106619977392256259918212890625',\n", " '6046992680891830197061490109937833490419136188999442576576769103890995893380022607743740081787109376']" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "stubborn_endings(100)" ] }, { "cell_type": "markdown", "id": "37a45341-8dac-4720-be7d-388fa47eec1a", "metadata": {}, "source": [ "## More questions!\n", "\n", "This leads to a few new questions.\n", "\n", "## Can each stubborn ending be extended exactly one way?\n", "\n", "We know that each stubborn ending of length *d* has to build on the endings of length *d - 1*, but is it always the case that for any length *d* there will be exactly four endings? Might there be a case where two digits work with one of the endings, or no digits?\n", "\n", "I can show that there are always 4 endings up to length *d* = 2000, but I leave it to you to describe a proof that this will always be true." ] }, { "cell_type": "code", "execution_count": 12, "id": "654d53af-e58f-4ed2-874b-c4573f3a9a7a", "metadata": {}, "outputs": [], "source": [ "for d in range(1, 2000):\n", " assert len(stubborn_endings(d)) == 4 " ] }, { "cell_type": "markdown", "id": "537556a3-d570-4a50-93bf-9632b40f9b47", "metadata": {}, "source": [ "One cool implication: if it is true that a stubborn ending of any length can always be extended, that means there is an infinitely long integer whose square ends with itself!\n", "\n", "## What digits are used to extend each ending?\n", "\n", "There doesn't seem to be a pattern, and all digits seemingly get used roughly evenly. \n", "\n", "I don't have a theory of which digit comes next, but I can count how many times each digit appears in the 2000-digit endings that end in \"5\" and \"6\", and see that each of the ten digits appears about 200 times:" ] }, { "cell_type": "code", "execution_count": 13, "id": "4dbd7ace-8b41-4ea7-b71f-8f7e318243e1", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Counter({'0': 205,\n", " '3': 173,\n", " '2': 208,\n", " '6': 197,\n", " '9': 198,\n", " '5': 197,\n", " '4': 206,\n", " '8': 214,\n", " '7': 205,\n", " '1': 197})" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from collections import Counter\n", "\n", "zero, one, five, six = stubborn_endings(2000)\n", "\n", "Counter(five)" ] }, { "cell_type": "code", "execution_count": 14, "id": "96bdab37-c5a3-4597-b4ca-fb560e4c3163", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Counter({'9': 205,\n", " '6': 174,\n", " '7': 208,\n", " '3': 197,\n", " '0': 198,\n", " '4': 196,\n", " '5': 206,\n", " '1': 214,\n", " '2': 205,\n", " '8': 197})" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Counter(six)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "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.8.15" } }, "nbformat": 4, "nbformat_minor": 5 }