Writing cubic sums a better way

Posted on

Problem

I was asked the following problem:

What is the smallest positive integer n that can be written as a sum of three positive cubes in at least 12 ways?

Can this be coded better way such that it is easy to compute the smallest positive integer that is sum of m nth powers? I’m not a professional programmer, so I managed only to write one solution that works.

l = list()
for i in range(1,500):
    for j in range(i,500):
        for k in range(j,500):
            l.append(i**3+j**3+k**3)
l.sort()
for i in range(0,len(l)-12):
    if l[i] == l[i+12]:
        number = l[i]
for i in range(1,500):
    for j in range(i,500):
        for k in range(j,500):
            if i**3+j**3+k**3 == number:
                print(str(number)+"="+str(i)+"^3+"+str(j)+"^3+"+str(k)+"^3")

Solution

Instead of three nested for loops, you can use itertools to enumerate combinations. Also, I would personally prefer to keep a count in a dictionary, as opposed to storing the same numbers over and over.

import itertools


cube_counts = {}

for i, j, k in itertools.combinations(range(1, 500), 3):
    cube = i**3 + j**3 + k**3
    cube_counts[cube] = cube_counts.get(cube, 0) + 1

print(min([cube for cube, count in cube_counts.items() if count >= 12]))

To begin with, the l[i] == l[i+12] condition implies that there are 13 ways to represent the number.

Second, since taxi-cab problem gave raise to some very deep mathematical insights, I seriously doubt that there is a nice way to attack a 3 cube problem – unless your name is Ramanujan.

Probably there’s a more efficient mathematical solution.
In any case, the algorithm can be improved, and there’s also a bug.


First of all,
I recommend to put 500 and 12 into variables,
as you use them multiple times.
You can define them near the top of the file where they will be easy to see and tweak if necessary.
For example, since the run with 500 and 12 takes awfully long,
I used smaller values like 50 and 4 for testing while refactoring.

min_count = 12
max_range = 500

You have two nested loops:
first to calculate the cubes,
and then again to find the numbers that add up to the smallest number you found.
Assuming you have enough memory,
you could use a single pass to calculate the cubes
and at the same time store the i, j, k compoments, like this:

items = []
for i, j, k in itertools.combinations(range(1, max_range), 3):
    cube = i ** 3 + j ** 3 + k ** 3
    items.append((cube, (i, j, k)))

I renamed the original list from l (which is a very hard-to-read variable name) to items.
Instead of the nested loops,
I used @Luke’s technique with itertools.combinations which is more compact.

In this version items is not a simple list of numbers,
but a list of tuples: the cube + the tuple of (i, j, k).
You can sort this list by the first element of the tuple (by the cube),
by passing a comparison function to items.sort like this:

items.sort(lambda a, b: cmp(a[0], b[0]))

Finally, to print the items,
it’s more natural to use "".format instead of the complicated and hard to read string concatenation, like this:

for i in range(len(items) - min_count):
    if items[i][0] == items[i + min_count - 1][0]:
        number = items[i]
        for j in range(min_count):
            cube = items[i + j][0]
            x, y, z = items[i + j][1]
            print('{}={}^3+{}^3+{}^3'.format(cube, x, y, z))
        break

Leave a Reply

Your email address will not be published. Required fields are marked *