Python power towers

If you have a list of numbers, say [3, 2, 2, 2], what’s the best way to turn it into a power tower?

In other words, the aim is to take the list and calculate the answer to 3**(2**(2**2)), which is 43046721 (the ** operator is exponentiation in Python).

Trivial… or so I thought.

It turns out that this is a bit of a pain to do nicely. Unlike addition and multiplication, exponentiation is not commutative. That is, x**y does not necessarily equal y**x. This means you can’t simply reduce the list using pow (Python’s built in function for exponentiation) because you’ll get 6561, that is ((3**2)**2)**2. Power towers are collapsed from the top; the list must somehow be reduced ‘backwards’ or ‘from the inside-out’.

For a first attempt at turning a list into a power tower, recall that Python is smart enough to evaluate an expression with chained powers from right to left:

>>> 3**2**2**2
43046721

Therefore, if x is your list of numbers [3, 2, 2, 2], you could create the same expression by writing:

>>> eval('**'.join([str(n) for n in x]))
43046721

But this is unspeakably ugly. It turns each number into a string, builds the expression string and then finally uses the much-maligned eval to calculate the answer (using the safer function ast.literal_eval won’t work here). I never want to look at it again.

There has to be a better way! After some pencil-on-paper scribbling, I arrived at this:

from functools import reduce # not necessary in Python 2.x

def pow_swapped_args(x, y):
    return y**x

Then we can evaluate a list as a power tower by writing:

>>> reduce(pow_swapped_args, reversed([3, 2, 2, 2]))
43046721

This is a definite improvement over the previous method. Reversing the list takes care of the reduce right-to-left evaluation requirement; a power function with swapped arguments completes the calculation.

I’m still sure there’s another nicer way. I want to find either a different way to swap the arguments of pow, or a way to order the list that avoids the need to create a new function. Ultimately I’d like to find a fast vectorised way to do this on NumPy arrays instead of lists. If I discover any improved ways to make power towers, I’ll post an update here.

Written on August 29, 2015