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

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.

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.

Related

Sample from list of lists until condition is met

I have a list of lists that I am randomly choosing a value from. The issue I am having now is that since the loop I wrote is using random.choice() there is the possibility that a given value won't be present, which will cause an error in downstream code. So I basically want to sample from this list of lists until the final list has at least one occurrence of each value (so at least one occurrence of 1-8).
Below is my current code and the output list for probe which doesn't include a single occurrence of 3 in this iteration, which will cause the subsequent code to fail.
Some restrictions I have:
The final list probe needs to be only 16 items.
One of the two values needs to be picked at each index because it codes for a later stimulus. So for example, probe[0] in this case needs to be either 7 or 6 and probe[15] needs to be either 3 or 2. But still with the condition that across all indexes, 1-8 appears at least once.
probe_list = [['7', '6'], ['5', '8'], ['3', '2'], ['1', '4'], ['7', '6'], ['1', '4'], ['5', '8'], ['7', '6'], ['1', '4'], ['3', '2'], ['7', '6'], ['1', '4'], ['7', '6'], ['1', '4'], ['5', '8'], ['3', '2']]
probe=[]
for i in range(len(probe_list)):
choose = random.choice(probe_list[i])
probe.append(choose)
probe = ['7', '8', '2', '1', '6', '1', '5', '6', '4', '2', '7', '4', '6', '4', '8', '2']
You could create a set, which contains all values which are already in the probe in the current "run". Then you could always check, whether the size of the set is 8. If you begin a new "run", then you have to clear() the set and the probe.
import random
probe_list = [['7', '6'], ['5', '8'], ['3', '2'], ['1', '4'], ['7', '6'], ['1', '4'], ['5', '8'], ['7', '6'], ['1', '4'], ['3', '2'], ['7', '6'], ['1', '4'], ['7', '6'], ['1', '4'], ['5', '8'], ['3', '2']]
probe=[]
while True:
contained=set()
probe = []
for i in range(len(probe_list)):
choose = random.choice(probe_list[i])
contained.add(choose)
probe.append(choose)
if len(contained) == 8:
break
print(probe, len(probe), len(set(probe)))
It would alway ensure, that the length of the probe is 16 and the number of unique items in the probe is 8.
For example the output:
['7', '5', '2', '4', '6', '1', '8', '7', '1', '3', '6', '4', '6', '1', '5', '3'] 16 8

How to add a list to another list

I have a function called "organizer" which takes 2 values a list and an index(number), to reorganize this list, after that it return a new list.
I want to execute this function 10 times to create 10 lists and add this lists to a single list of lists for later use, the program acomplish this, however it repeat the last list 10 times, it does not add the other lists.
I put the function "organizer" in here,because Im unable to find where the problem relies so more information may be needed.
I have been using the print function along the function to see where it fails, and it creates the lists as I desire, the problem is that after creating them it just copy the last created list as many times as the loop goes on. It takes the final list produced and copy it several times
Here is the code:
number_list = ["12","11","10","9","8","7","6","5","4","3","2","1"]
indexes = [0,5,6,8,10,4,5,2,1,9]
def organizer(list,index): # Takes a list and reorganize it by a number which is an index.
number1 = index - 1
count_to_add = -1
list_to_add = []
for item in range(number1):
count_to_add += 1
a = list[count_to_add]
list_to_add.append(a)
number2 = index - 1
for item1 in range(number2):
del list[0]
list.extend(list_to_add)
return list
def lists_creator(list_indexes): # Create a list of 10 lists ,with 12 elements each.
final_list = []
count = -1
for item in list_indexes:
count += 1
result = organizer(number_list, list_indexes[count])
final_list.append(result)
return final_list
final_output = lists_creator(indexes)
print(final_output)
Here is the result:(the same list 10 times)
[['7', '6', '5', '4', '3', '2', '1', '12', '11', '10', '9', '8'], ['7', '6', '5', '4', '3', '2', '1', '12', '11', '10', '9', '8'], ['7', '6', '5', '4', '3', '2', '1', '12', '11', '10', '9', '8'], ['7', '6', '5', '4', '3', '2', '1', '12', '11', '10', '9', '8'], ['7', '6', '5', '4', '3', '2', '1', '12', '11', '10', '9', '8'], ['7', '6', '5', '4', '3', '2', '1', '12', '11', '10', '9', '8'], ['7', '6', '5', '4', '3', '2', '1', '12', '11', '10', '9', '8'], ['7', '6', '5', '4', '3', '2', '1', '12', '11', '10', '9', '8'], ['7', '6', '5', '4', '3', '2', '1', '12', '11', '10', '9', '8'], ['7', '6', '5', '4', '3', '2', '1', '12', '11', '10', '9', '8']]
Note: If I change the line 28 in list_creator function
final_list.append(result)
for
final_list.extend(result)
the result is:
['12', '11', '10', '9', '8', '7', '6', '5', '4', '3', '2', '1', '8', '7', '6', '5', '4', '3', '2', '1', '12', '11', '10', '9', '3', '2', '1', '12', '11', '10', '9', '8', '7', '6', '5', '4', '8', '7', '6', '5', '4', '3', '2', '1', '12', '11', '10', '9', '11', '10', '9', '8', '7', '6', '5', '4', '3', '2', '1', '12', '8', '7', '6', '5', '4', '3', '2', '1', '12', '11', '10', '9', '4', '3', '2', '1', '12', '11', '10', '9', '8', '7', '6', '5', '3', '2', '1', '12', '11', '10', '9', '8', '7', '6', '5', '4', '3', '2', '1', '12', '11', '10', '9', '8', '7', '6', '5', '4', '7', '6', '5', '4', '3', '2', '1', '12', '11', '10', '9', '8']
Which is the result that I desire but is not a group of lists.
you are using the same list every time, so the result consists of actually one list,
in your list_creator method, you have to give to organizer method a copy of your original list number_list, otherwise you just mutate over and over again the same one list:
def lists_creator(list_indexes): # Create a list of 10 lists ,with 12 elements each.
final_list = []
count = -1
for item in list_indexes:
count += 1
result = organizer(list(number_list), list_indexes[count])
final_list.append(result)
return final_list
def lists_creator(list_indexes): # Create a list of 10 lists ,with 12 elements each.
final_list = []
list = []
for i in range(len(list_indexes)):
result = organizer(number_list, list_indexes[i])
list.extend(result)
n = 0
m = 12
for n_times in range(len(list_indexes)):
final_list.append(list[n:m])
n = m
m += 12
return final_list

How to count the number of separate regions in python array

I need to count the number of separate regions in an array.
An example array is below. I can count the number of unique strings but if there are two distinct islands like in the grid below where 64733 is in the upper left and bottom right my function won't properly count the number of regions.
Can anyone help me to find a way to count the number of regions if two of the regions are comprised of the same numbers? Im writing in python 2.x.
|64733|20996|92360|92360|04478|04478|04478|04478|04478|98101
|64733|92360|92360|92360|04478|04478|04478|04478|04478|04478
|64733|92360|29136|92360|04478|04478|04478|04478|04478|04478
|64733|92360|29136|92360|04478|04478|04478|04478|04478|04478
|92360|92360|92360|92360|04478|04478|04478|04478|04478|04478
|04478|04478|04478|04478|04478|04478|04478|04478|04478|04478
|04478|04478|04478|04478|04478|04478|04478|04478|04478|04478
|04478|04478|04478|04478|04478|04478|04478|04478|04478|04478
|04478|04478|04478|04478|04478|04478|04478|04478|04478|64773
|04478|04478|04478|04478|04478|04478|04478|04478|64773|64773
The input is a 3d array and looks like
[[['6', '4', '7', '3', '3'],
['2', '0', '9', '9', '6'],
['9', '2', '3', '6', '0'],
['9', '2', '3', '6', '0'],
['0', '4', '4', '7', '8'],
['0', '4', '4', '7', '8'],
['0', '4', '4', '7', '8'],
['0', '4', '4', '7', '8'],
['0', '4', '4', '7', '8'],
['9', '8', '1', '0', '1']],
[['6', '4', '7', '3', '3'],
['9', '2', '3', '6', '0'],
['9', '2', '3', '6', '0'],
['9', '2', '3', '6', '0'],
['0', '4', '4', '7', '8'],
['0', '4', '4', '7', '8'],
['0', '4', '4', '7', '8'],
['0', '4', '4', '7', '8'],
['0', '4', '4', '7', '8'],
['0', '4', '4', '7', '8']],
[['6', '4', '7', '3', '3'],
['9', '2', '3', '6', '0'],
This is not the complete input (because it was huge) but it gets the point across i think
So would be where any unique symbol string of numbers lies and the total number of adjacent strings that are identical (adjacent being up down left right, not diagonal).
enter code here
You didn't post your full array, so I made some assumptions about the original pip separated value you gave us. I've written this in Python 3, but I think it should work in Python 2 only modifying the print function calls to statements.
arr = [["64733","20996","92360","92360","04478","04478","04478","04478","04478","98101"],
["64733","92360","92360","92360","04478","04478","04478","04478","04478","04478"],
["64733","92360","29136","92360","04478","04478","04478","04478","04478","04478"],
["64733","92360","29136","92360","04478","04478","04478","04478","04478","04478"],
["92360","92360","92360","92360","04478","04478","04478","04478","04478","04478"],
["04478","04478","04478","04478","04478","04478","04478","04478","04478","04478"],
["04478","04478","04478","04478","04478","04478","04478","04478","04478","04478"],
["04478","04478","04478","04478","04478","04478","04478","04478","04478","04478"],
["04478","04478","04478","04478","04478","04478","04478","04478","04478","64773"],
["04478","04478","04478","04478","04478","04478","04478","04478","64773","64773"]]
x = len(arr)
y = len(arr[0])
#create a new array the same size as the original
regions = [[None for _ in range(y)] for _ in range(x)]
print(regions)
label = 0
queue = []
def check_neighbor(i, j, v):
if not regions[i][j] and arr[i][j] == v:
regions[i][j] = label
queue.insert(0, (i, j))
for i in range(x):
for j in range(y):
#don't check an already labelled region
if regions[i][j]: continue
label += 1 #new label
regions[i][j] = label
queue = [(i, j)]
v = arr[i][j]
#keep checking neighbours until we run out
while queue:
(X, Y) = queue.pop()
if X > 0:
check_neighbor(X-1, Y, v)
if X < x-1:
check_neighbor(X+1, Y, v)
if Y > 0:
check_neighbor(X, Y-1, v)
if Y < y-1:
check_neighbor(X, Y+1, v)
print(regions)
print(label) # this is the number of regions

How to check columns and subgrids (Sudoku Checker in Python)?

I'm doing the traditional "Sudoku Puzzle Checker" activity, and I've only been able to figure out how to check the rows so far. As per instruction, I'm suppose to check (in 3 separate functions), the rows, columns, and subgrids (the 3x3 squares) of a solved sudoku puzzle (given in a .csv file). I understand how to do the columns (add 9 each time, 9 times starting numbers ranging from 0-8) and the subgrids (range of 3 numbers, add 6 to get to the next range of 3 numbers e.t.c) but I'm confused as how to implement these concepts into my code. Here's what I have right now:
def load_puzzle(filename):
file = open(filename, 'r')
puzzle = list(file)
#the puzzle currently looks like this:
['5,3,4,6,7,8,9,1,2\n', '6,7,2,1,9,5,3,4,8\n', '1,9,8,3,4,2,5,6,7\n', '8,5,9,7,6,1,4,2,3\n', '4,2,6,8,5,3,7,9,1\n', '7,1,3,9,2,4,8,5,6\n', '9,6,1,5,3,7,2,8,4\n', '2,8,7,4,1,9,6,3,5\n', '3,4,5,2,8,6,1,7,9\n']
print_puzzle(puzzle)
def print_puzzle(puzzle):
print(" ")
count = 0
while count <= 8:
line = list(puzzle[count])
line = [x for x in line if x != ',']
line.remove('\n')
line.insert(3, '|')
line.insert(7, '|')
if count == 3 or count == 6:
print("---------------------")
fresh = ' '.join(line)
count += 1
print('''
Checking puzzle...
''')
delimiter = ','
together = delimiter.join(puzzle)
new = list(together)
new[:] = [x for x in new if x != ',']
new[:] = [x for x in new if x != '\n']
check_rows(new)
#the puzzle now looks like this:
['5', '3', '4', '6', '7', '8', '9', '1', '2', '6', '7', '2', '1', '9', '5', '3', '4', '8', '1', '9', '8', '3', '4', '2', '5', '6', '7', '8', '5', '9', '7', '6', '1', '4', '2', '3', '4', '2', '6', '8', '5', '3', '7', '9', '1', '7', '1', '3', '9', '2', '4', '8', '5', '6', '9', '6', '1', '5', '3', '7', '2', '8', '4', '2', '8', '7', '4', '1', '9', '6', '3', '5', '3', '4', '5', '2', '8', '6', '1', '7', '9']
def check_numbers(numbers):
my_list = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
numbers.sort()
if numbers == my_list:
return True
else:
return False
def check_rows(n):
count = 0
start = 0
while count <= 8:
row = n[start:(start + 9)]
if check_numbers(row) is True:
count += 1
start += 9
elif check_numbers(row) is False:
break
if count == 9:
print("Rows okay...")
else:
print("Error in row", count)
check_columns(n)
def check_columns(n):
count = 0
start = 0
while count <= 8:
col = n[start::9]
if check_numbers(col) is True:
count += 1
start += 1
elif check_numbers(col) is False:
break
if count == 9:
print("Columns okay...")
else:
print("Error in row", count)
def check_subgrids(n):
filename = input("Enter file name of solved sudoku puzzle: ")
load_puzzle(filename)
I'm looking for a way to find the columns and subgrids similar to how I found the rows (i.e. row = n[start:(start + 9)]) if that's possible. I'm a beginner, so I'm also happy to learn about different tools/ways I could improve my code!
Edit: I've solved my problem with the check_columns function (code updated) but still would appreciate some guidance for the check_subgrid function!

Printing a variable and appending it with a line from a file in a loop

I've researched this already but I can't find an exact answer. I've found answers for appending when there's 2 lists involved but this is different so here goes.
I'm creating a really basic fuzzer but I'm having issues appending the directory names to the end of the address. Here's what I have so far.
The expected output is as follows;
www.website.com/1
www.website.com/2
www.website.com/3
www.website.com/4
etc. But I'm getting something completely different. Here's the first piece of code I tested.
>>> host = "www.website.com/"
>>> path = [line.strip() for line in open("C:/Users/Public/Documents/tester.txt", 'r')]
>>> print str(host) + str(path)
which returns the following
www.website.com/['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
The second attempt was this;
>>> host = "www.website.com/"
>>> path = [line.strip() for line in open("C:/Users/Public/Documents/tester.txt", 'r')]
>>> for line in path:
print str(host) + str(path)
Which returned this;
www.website.com/['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
www.website.com/['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
www.website.com/['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
www.website.com/['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
www.website.com/['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
www.website.com/['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
www.website.com/['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
www.website.com/['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
www.website.com/['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
www.website.com/['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
I can see exactly what's happening and why it's happening but I can't figure out how to arrive at the expected output. I'll also need to filter out the special characters which I didn't think 'print' would pick up. Maybe there's different rules for print when it's reading something as a list.
I've thought of stupid complex methods such as counting the lines in the file and then throwing it into a while loop using that count but I'm sure there's something I can use or something I've done wrong. My Python knowledge isn't fantastic.
Can anybody help with this?
You were nearly there , when using the for loop , concatenate the elements from the list (which in your case is in variable 'line') , not the complete list again .
Code -
for line in path:
print str(host) + str(line)

Categories