I don't understand why is this for loop so fast?

Question:

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.

Answers:

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.

Answered By: Barmar