#!/usr/bin/env python # coding: utf-8 # # Euler's Sum of Powers Conjecture # # 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. # # 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 # *a*5 + *b*5 + *c*5 + *d*5 = *e*5 ? # # ## Algorithm # # 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 # are looking for # #      *pair*1 + *pair*2 = *e*5    **where**   *pair*1 = *a*5 + *b*5 **and** *pair*2 = *c*5 + *d*5. # # 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)}`. # 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): # In[1]: import itertools def euler(m): """Yield tuples (a, b, c, d, e) such that a^5 + b^5 + c^5 + d^5 = e^5, where all are integers, and 1 < a ≤ b ≤ c ≤ d < e < m.""" powers = [e**5 for e in range(2, m)] pairs = {sum(pair): pair for pair in itertools.combinations_with_replacement(powers, 2)} for pair1 in pairs: for e5 in powers: pair2 = e5 - pair1 if pair2 in pairs: yield fifthroots(pairs[pair1] + pairs[pair2] + (e5,)) def fifthroots(nums): "Sorted integer fifth roots of a collection of numbers." return tuple(sorted(int(round(x ** (1/5))) for x in nums)) # Let's look for a solution (arbitrarily choosing *m*=500): # In[2]: get_ipython().run_line_magic('time', 'next(euler(500))') # That was easy, and it turns out this is the same answer that Lander and Parkin got: 275 + 845 + 1105 + 1335 = 1445. # # We can keep going, collecting all the solutions up to `*m*=1000`: # In[3]: get_ipython().run_line_magic('time', 'set(euler(1000))') # All the answers are multiples of the first one (this is easiest to see in the middle column: 110, 220, 330, ...). # 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.