So that is the size of our search space, not incredibly large, but the problem gets trickier. They only want pandigitals which small portions of are divisible by prime numbers. I believe that their example makes it clear exactly what they want.
Take the pandigital 1406357289. Then lets look at a small section of it 3 digits at a time:
- 1406357289 is divisible by 2
- 1406357289 is divisible by 3
- 1406357289 is divisible by 5
- 1406357289 is divisible by 7
- 1406357289 is divisible by 11
- 1406357289 is divisible by 13
- 1406357289 is divisible by 17
So as usual I coded a naive approach to see how it performs and to make sure I can answer the question. I implemented a function that takes in a 10 digit number and checks to see if the digits in index 2 through 4 are divisible by 2, then the digits 3 through 5 are divisible by 3 then.... etc
. Then I simply generated every single permutation of the digits 0-9 and threw away any that did not pass the function, from there you can simply sum up all of the remaining ones and you have your answer.
Hopefully the python makes this clear.
from itertools import permutations def wtf_problem_43(number): primes = [2,3,5,7,11,13,17] for i in range(len(primes)): if int(number[1 + i:4 + i])%primes[i] != 0: return False return True print sum(map(int,filter(wtf_problem_43,map(''.join,permutations("1234568790")))))
This code does indeed work, but it takes 47.6 seconds to execute. Surely we can do better!
First off there is no need to generate every permutation of the digits 0-9. Surely we can cut a large amount of those out of the search space very quickly. Right off the bat what we could do is simply focus on generating only a 3 digit chunk that is divisible by its relevant prime number. It would be like instead of generating 1406357289 we only generate 406, because it is divisible by 2. Then we would only need to try every remaining number (1,2,3,5,7,8,9) in the last slot of 06_ and see if it is divisible by 3.
This turns the problem into a constraint satisfaction problem (sudoku is a famous CSP) and a nice trick for solving these very quickly is to first make choices where it cuts out the large number of potentials. For instance there are 360 diffent ways to start if we start with the ones divisible by 2, but if we start backwards, with different ways to make numbers that are divisible by 17 we only have 44 of them.
So I created a recursive generator that will yield all of the relevant numbers.
This code is significantly more complex, I left some comments, but recursive generators are a little hard to understand. I personally recommend adding the decorator that prints out the arguments that a function gets when it is called. (add this by putting @dump_args before the function declaration).
from itertools import permutations def generator(divisors,remaining, previous=""): if divisors: #if divisors is empty, then only one digit remains if previous == "": #This iteration is the starting iteration, it simply recalls its self with every permutation of the last two digits for permutation in permutations(remaining,2): permutation = ''.join(permutation) #('1','2') -> "12" for i in generator(divisors,remaining.translate(None,permutation),permutation): #translate strips the used digits, ensuring pandigitality yield i + permutation else: for permutation in permutations(remaining,1): #unnecessarily complex, I just wanted it to look consistent permutation = ''.join(permutation) + previous[:2] if int(permutation)%divisors[0] == 0:# for i in generator(divisors[1:],remaining.translate(None,permutation),permutation): yield i + permutation[0] else: yield remaining print sum(map(int,generator([17,13,11,7,5,3,2],"1234568790")))Even though this code could still be speed up it solves the problem in just 0.008 seconds! Again, if after looking at the code with the dump_args decorator you still do not understand it, please leave me a comment.
No comments:
Post a Comment