From b331d71218f09a238d7b5e5ad78750f2acb8dd69 Mon Sep 17 00:00:00 2001 From: Peter Norvig Date: Mon, 9 Jul 2018 13:35:54 -0700 Subject: [PATCH] Add files via upload --- ipynb/Euler's Conjecture.ipynb | 163 +++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 ipynb/Euler's Conjecture.ipynb diff --git a/ipynb/Euler's Conjecture.ipynb b/ipynb/Euler's Conjecture.ipynb new file mode 100644 index 0000000..e40c82f --- /dev/null +++ b/ipynb/Euler's Conjecture.ipynb @@ -0,0 +1,163 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Euler's Sum of Powers Conjecture\n", + "\n", + "In 1769, Leonhard Euler [conjectured that](https://en.wikipedia.org/wiki/Euler%27s_sum_of_powers_conjecture) for all integers *n* and *k* greater than 1, if the sum of *n* *k*th powers of positive integers is itself a *k*th power, then *n* is greater than or equal to *k*. For example, this would mean that no sum of a pair of cubes (a3 + b3) can be equal to another cube (c3), but a sum of three cubes can, as in 33 + 43 + 53 = 63. \n", + "\n", + "It took 200 years to disprove the conjecture: in 1966 L. J. Lander and T. R. Parkin published a refreshingly short [article](https://projecteuclid.org/download/pdf_1/euclid.bams/1183528522) giving a counterexample of four fifth-powers that summed to another fifth power. They found it via a program that did an exhaustive search. Can we duplicate their work and find integers greater than 1 such that \n", + "*a*5 + *b*5 + *c*5 + *d*5 = *e*5 ?\n", + "\n", + "## Algorithm\n", + "\n", + "An exhaustive *O*(*m*4) algorithm woud be to look at all values of *a, b, c, d* < *m* and check if *a*5 + *b*5 + *c*5 + *d*5 is a fifth power. But we can do better: a sum of four numbers is a sum of two pairs of numbers, so we\n", + "are looking for\n", + "\n", + "     *pair*1 + *pair*2 = *e*5    **where**   *pair*1 = *a*5 + *b*5 **and** *pair*2 = *c*5 + *d*5.\n", + "\n", + "We will define *pairs* be a dict of `{`*a*5 + *b*5`: (`*a*5`, ` *b*5`)}` entries for all *a* ≤ *b* < *m*; for example, for *a*=2 and *b*=10, the entry is `{100032: (32, 100000)}`.\n", + "Then we can ask for each *pair*1, and for each *e*, whether there is a *pair*2 in the `dict` that makes the equation work. There are *O*(*m*2) pairs and *O*(*m*) values of *e*, and `dict` lookup is *O*(1), so the whole algorithm is *O*(*m*3):" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import itertools\n", + "\n", + "def euler(m):\n", + " \"\"\"Yield tuples (a, b, c, d, e) such that a^5 + b^5 + c^5 + d^5 = e^5,\n", + " where all are integers, and 1 < a ≤ b ≤ c ≤ d < e < m.\"\"\"\n", + " powers = [e**5 for e in range(2, m)] \n", + " pairs = {sum(pair): pair \n", + " for pair in itertools.combinations_with_replacement(powers, 2)}\n", + " for pair1 in pairs:\n", + " for e5 in powers:\n", + " pair2 = e5 - pair1\n", + " if pair2 in pairs:\n", + " yield fifthroots(pairs[pair1] + pairs[pair2] + (e5,))\n", + " \n", + "def fifthroots(nums): \n", + " \"Sorted integer fifth roots of a collection of numbers.\" \n", + " return tuple(sorted(int(round(x ** (1/5))) for x in nums))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's look for a solution (arbitrarily choosing *m*=500):" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 1.07 s, sys: 21.4 ms, total: 1.09 s\n", + "Wall time: 1.11 s\n" + ] + }, + { + "data": { + "text/plain": [ + "(27, 84, 110, 133, 144)" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%time next(euler(500))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "That was easy, and it turns out this is the same answer that Lander and Parkin got: 275 + 845 + 1105 + 1335 = 1445.\n", + "\n", + "We can keep going, collecting all the solutions up to `*m*=1000`:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 1min 53s, sys: 706 ms, total: 1min 54s\n", + "Wall time: 1min 57s\n" + ] + }, + { + "data": { + "text/plain": [ + "{(27, 84, 110, 133, 144),\n", + " (54, 168, 220, 266, 288),\n", + " (81, 252, 330, 399, 432),\n", + " (108, 336, 440, 532, 576),\n", + " (135, 420, 550, 665, 720),\n", + " (162, 504, 660, 798, 864)}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%time set(euler(1000))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All the answers are multiples of the first one (this is easiest to see in the middle column: 110, 220, 330, ...).\n", + "Since 1966 other mathematicians have found [other solutions](https://en.wikipedia.org/wiki/Euler%27s_sum_of_powers_conjecture), but all we need is one to disprove Euler's conjecture." + ] + } + ], + "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": 2 +}