Today I was solving Project Euler's problem #43 Problem and I ran into a somewhat interesting problem. I don't understand why is my code so fast?
from itertools import permutations
def main():
numbers1_9 = [0,1,2,3,4,5,6,7,8,9]
list_of_all_permutations = list(permutations(numbers1_9, 10))
length_of_my_list = len(list_of_all_permutations)
number_of_times_it_ran=0
result = []
for n in list_of_all_permutations:
number_of_times_it_ran =1
if n[0] == 0:
continue
elif n[3] % 2 == 0 and (n[2] n[3] n[4]) % 3 == 0 and n[5] % 5 ==0 and int(str(n[4]) str(n[5]) str(n[6])) % 7 == 0 and (n[5] n[7]-n[6]) % 11 == 0 and int(str(n[6]) str(n[7]) str(n[8])) % 13 == 0 and int(str(n[7]) str(n[8]) str(n[9])) % 17 == 0:
temp_list = []
for digits_of_n in n:
temp_list.append(str(digits_of_n))
result.append(int("".join(temp_list)))
print(f"Added {temp_list}, Remaining: {length_of_my_list-number_of_times_it_ran}")
print(f"The code ran {number_of_times_it_ran} times and the result is {sum(result)}")
if __name__ == "__main__":
main()
I mean it went through the for loop 3,628,800 times, checked all those parameters, and only took a second.
Added ['1', '4', '0', '6', '3', '5', '7', '2', '8', '9'], Remaining: 3142649
Added ['1', '4', '3', '0', '9', '5', '2', '8', '6', '7'], Remaining: 3134251
Added ['1', '4', '6', '0', '3', '5', '7', '2', '8', '9'], Remaining: 3124649
Added ['4', '1', '0', '6', '3', '5', '7', '2', '8', '9'], Remaining: 2134649
Added ['4', '1', '3', '0', '9', '5', '2', '8', '6', '7'], Remaining: 2126251
Added ['4', '1', '6', '0', '3', '5', '7', '2', '8', '9'], Remaining: 2116649
The code ran 3628800 times, and the result is 16695334890
This is the output. The code finished in 1.3403266999521293 seconds.
CodePudding user response:
It runs so quickly because of the short-circuiting feature of logical operators. The first three conditions in the if
statement are easy to calculate, and they filter out the vast majority (around 97%) of all the permutations, so you hardly ever need to execute the more expensive operations like int(str(n[4]) str(n[5]) str(n[6]))
.
So when you have a bunch of conditions that you're connecting with and
, and the order that they're tested doesn't matter for the logic, you should put the ones that are easiest to test or are most likely to fail first.