I doubt you will see my answer. Hopefully it can help someone else in the future. First I want to compare the answers above speedwise. As a tease I will include my own attempt
Comparison
-------------------------------
| Name | Time |
| Joshua (OP) | 2000 ms |
| clutton | 500 ms |
| Edward | 300 ms |
| Nebuchadnezzar | 20 ms |
-------------------------------
So the answers above give a rough 4x-5x speed improvement. So how can we increase this to a 100x speed improvement? Well naively you check every number up to the limit \$ 5*9^5 \$. This means your code does 443839 checks, and for every check it has to compute the digit sum. In order to get a significant speed improvement we need a new algorithm.
New algorithm
One key observation is to note that once we have checked \$0145\$ we do not need to check \$1450\$ or any other combination of \$(0, 1, 4, 5)\$ since they all the the same power sum
$$
P_5[(0, 1, 4, 5)] = 0^5 + 1^5 + 4^5 + 5^5 = 4150
$$
To check whether if some permutation of \$(0, 1, 4, 5)\$ is equal to it's power sum all we need to do is to sort the digits in the power sum and compare (assuming the tuple is already sorted).
Implementation
One way to find the unique tuples is with combinations_with_replacement()
from the itertools libary.
The code uses two checks to further decrease the number of combinations to check. Naively with combinations_with_replacements()
there are a total of 5005 numbers to check. The check
if power_sum % 10 in perm:
is pretty intuitively. It checks whether the last number in the power_sum
is in the permutations. A necessity if some permutation of perm is equal to the digit power sum. This reduces the number to check to 730. The final check is
if len_power > len_perm: return False
This cuts the number needed to check down to 591. Not this is not a huge improvement, however it is a very cheap test to perform and it is more useful for higher powers. I have attached a small table below
P S Sum # 0 # 1 # 2 time
--------------------------------------------------------------
3 4 1301 220 60 50 0.8685 ms
4 3 19316 715 224 165 5.0320 ms
5 5 248860 2002 730 591 10.7200 ms
6 1 548834 5005 1974 1603 32.0400 ms
7 4 25679675 11440 5040 4220 77.1300 ms
8 3 137949578 24310 12504 10812 136.8000 ms
9 4 2066327172 48620 24380 21224 428.4000 ms
10 1 4679307774 92378 48706 42362 858.0000 ms
11 8 418030478906 167960 92512 82456 2502.0000 ms
12 0 0 293930 175452 159471 7021.0000 ms
P
is the power, S
is the number of solutions. # 0
is the total number of permutations to iterate over. # 1
is the remaining numbers after the first check, and # 2
is the remaining numbers after the second check.
Code
from itertools import combinations_with_replacement as CnR
def digit_power(power):
total = -1
for perm in CnR(xrange(10), power):
power_sum = sum(i**power for i in perm)
if power_sum % 10 in perm:
power_sum_list = map(int, str(power_sum))
if is_fift_power(power_sum_list, perm, power):
total += power_sum
return total
def is_fift_power(power_lst, perm, len_perm):
len_power = len(power_lst)
if len_power > len_perm: return False
power_sort = [0]*(len_perm - len_power) + sorted(power_lst)
return power_sort == list(perm)
if __name__ == '__main__':
import timeit
print digit_power(5)
times = 100
result = timeit.timeit(
"digit_power(5)", number=times, setup="from __main__ import digit_power")
print 1000*result/float(times), 'ms'