{ "cells": [ { "cell_type": "markdown", "metadata": { "button": false, "collapsed": true, "new_sheet": false, "run_control": { "read_only": false } }, "source": [ "
\n", "* Are we justified in having a single value for each key in the `Czroots` table? Consider that $81 = 3^4 = 9^2$. We put `{81: 3}` in the table and discard `{81: 9}`, because any number that has 9 as a factor will always have 3 as a factor as well, so 3 is all we need to know. But what if a number could be formed with two bases where neither was a multiple of the other? For example, what if $2^7 = 5^3 = s$; then wouldn't we have to have both 2 and 5 as values for $s$ in the table? Fortunately, that can never happen, because of the [fundamental theorem of arithmetic](https://en.wikipedia.org/wiki/Fundamental_theorem_of_arithmetic).\n", "\n", "
\n", "* Could there be a rounding error involving the `exponent` function that was not caught by the tests? Possibly; but `exponent` is not used to find counterexamples, only to print them, so any such error wouldn't cause us to miss a counterexample.\n", "\n", "
\n", "* Are we justified in only considering exponents that are odd primes, or the number 4? In one sense, yes, because when we consider the two terms $A^{(qp)}$ and $(A^q)^p$, we find they are always equal, and always have the same prime factors (the factors of $A$), so for the purposes of the Beal problem, they are equivalent, and we only need consider one of them. In another sense, there is a difference. With this optimization, when we run `beal(6, 10)`, we are no longer testing $512$ as a value of $A$ or $B$, even though $512 = 2^9$ and both $2$ and $9$ are within range, because the program chooses to express $512$ as $8^3$, and $8$ is not in the specified range. So the program is still correctly searching for counterexamples, but the space that it searches for given `max_A` and `max_x` is different with this optimization.\n", "\n", "
\n", "* Are we really sure that when $A$ and $B$ have a common factor greater than 1, then\n", "$C$ also shares that common factor? Yes, because if $p$ is a factor of both $A$ and $B$, then it is a factor of $A^x + B^y$, and since we know this is equal to $C^z$, then $p$ must also be a factor of $C^z$, and thus a factor of $C$.\n", "\n" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false } }, "source": [ "\n", "# Faster Arithmetic (mod *p*)\n", "\n", "Arithmetic is slow with integers that have thousands of digits. If we want to explore much further, we'll have to make the program more efficient. An obvious improvement would be to do all the arithmetic modulo some number $m$. Then we know:\n", "\n", "$$\\mbox{if} ~~ \n", "A^x + B^y = C^z\n", "~~ \\mbox{then} ~~\n", "(A^x (\\mbox{mod} ~ m) + B^y (\\mbox{mod} ~ m)) (\\mbox{mod} ~ m) = C^z \\;(\\mbox{mod} ~ m)$$\n", "\n", "\n", "So we can do efficient tests modulo $m$, and then do the full arithmetic only for combinations that work modulo $m$. Unfortunately there will be collisions (two numbers that are distinct, but are equal mod $m$), so the tables will have to have lists of values. Here is a simple, unoptimized implementation:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "button": false, "collapsed": true, "new_sheet": false, "run_control": { "read_only": false } }, "outputs": [], "source": [ "from math import gcd\n", "from itertools import combinations, product\n", "from collections import defaultdict\n", " \n", "def beal_modm(max_A, max_x, m=2**31-1):\n", " \"\"\"See if any A ** x + B ** y equals some C ** z (mod p), with gcd(A, B) == 1.\n", " If so, verify that the equation works without the (mod m).\n", " Consider any 1 <= A,B <= max_A and x,y <= max_x, with x,y prime or 4.\"\"\"\n", " assert m >= max_A\n", " Apowers = make_Apowers_modm(max_A, max_x, m)\n", " Czroots = make_Czroots_modm(Apowers)\n", " for (A, B) in combinations(Apowers, 2):\n", " if gcd(A, B) == 1:\n", " for (Axm, x), (Bym, y) in product(Apowers[A], Apowers[B]): \n", " Czm = (Axm + Bym) % m\n", " if Czm in Czroots:\n", " lhs = A ** x + B ** y\n", " for (C, z) in Czroots[Czm]:\n", " if lhs == C ** z:\n", " print('{} ** {} + {} ** {} == {} ** {} == {}'\n", " .format(A, x, B, y, C, z, C ** z)) \n", " \n", "\n", "def make_Apowers_modm(max_A, max_x, m): \n", " \"A dict of {A: [(A**3 (mod m), 3), (A**4 (mod m), 4), ...]}.\"\n", " exponents = exponents_upto(max_x)\n", " return {A: [(pow(A, x, m), x) for x in (exponents if (A != 1) else [3])]\n", " for A in range(1, max_A+1)}\n", "\n", "def make_Czroots_modm(Apowers): \n", " \"A dict of {C**z (mod m): [(C, z),...]}\"\n", " Czroots = defaultdict(list)\n", " for A in Apowers:\n", " for (Axm, x) in Apowers[A]:\n", " Czroots[Axm].append((A, x))\n", " return Czroots " ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false } }, "source": [ "Here we see that each entry in the `Apowers` table is a list of `(A**x (mod p), x)` pairs.\n", "For example, $6^7 = 279,936$, so in our (mod 1000) table we have the pair `(936, 7)` under `6`." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "{1: [(1, 3)],\n", " 2: [(8, 3), (16, 4), (32, 5), (128, 7)],\n", " 3: [(27, 3), (81, 4), (243, 5), (187, 7)],\n", " 4: [(64, 3), (256, 4), (24, 5), (384, 7)],\n", " 5: [(125, 3), (625, 4), (125, 5), (125, 7)],\n", " 6: [(216, 3), (296, 4), (776, 5), (936, 7)]}" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Apowers = make_Apowers_modm(6, 10, 1000)\n", "Apowers" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false } }, "source": [ "And each item in the `Czroots` table is of the form `{C**z (mod m): [(C, z), ...]}`.\n", "For example, `936: [(6, 7)]`." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "button": false, "collapsed": false, "new_sheet": false, "run_control": { "read_only": false } }, "outputs": [ { "data": { "text/plain": [ "defaultdict(list,\n", " {1: [(1, 3)],\n", " 8: [(2, 3)],\n", " 16: [(2, 4)],\n", " 24: [(4, 5)],\n", " 27: [(3, 3)],\n", " 32: [(2, 5)],\n", " 64: [(4, 3)],\n", " 81: [(3, 4)],\n", " 125: [(5, 3), (5, 5), (5, 7)],\n", " 128: [(2, 7)],\n", " 187: [(3, 7)],\n", " 216: [(6, 3)],\n", " 243: [(3, 5)],\n", " 256: [(4, 4)],\n", " 296: [(6, 4)],\n", " 384: [(4, 7)],\n", " 625: [(5, 4)],\n", " 776: [(6, 5)],\n", " 936: [(6, 7)]})" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "make_Czroots_modm(Apowers)" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false } }, "source": [ "Let's run the program:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "button": false, "collapsed": false, "new_sheet": false, "run_control": { "read_only": false } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 56 s, sys: 436 ms, total: 56.4 s\n", "Wall time: 59.2 s\n" ] } ], "source": [ "%time beal_modm(1000, 100)" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false } }, "source": [ "We don't see a speedup here, but the idea is that as we start dealing with much larger integers, this version should be faster. I could improve this version by caching certain computations, managing the memory layout better, moving some computations out of loops, considering using multiple different numbers as the modulus (as in a Bloom filter), finding a way to parallelize the program, and re-coding in a faster compiled language (such as C++ or Go or Julia). Then I could invest thousands (or millions) of CPU hours searching for counterexamples. \n", "\n", "But [Witold Jarnicki](https://plus.sandbox.google.com/+WitoldJarnicki/posts) and [David Konerding](http://www.konerding.com/~dek/) already did that: they wrote a C++ program that, in parallel across thousands of machines, searched for $A, B$ up to 200,000 and $x, y$ up to 5,000, but found no counterexamples. So I don't think it is worthwhile to continue on that path.\n", "\n", "# Conclusion\n", "\n", "This was fun, but I can't recommend anyone spend a serious amount of computer time looking for counterexamples to the Beal Conjecture—the money you would have to spend in computer time would be more than the expected value of your prize winnings. I suggest you work on a proof rather than a counterexample, or work on some other interesting problem instead!" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.6.0" } }, "nbformat": 4, "nbformat_minor": 1 }