Python - Streamlining sudoku solver code - python

I am writing a script to efficiently solve a sudoku puzzle, but there's one part of my code that I think is extremely ugly and want to streamline.
def square(cell):
rows='ABCDEFGHI'
cols='123456789'
cell_row = cell[0][0]
cell_col = cell[0][1]
if cell_row in rows[0:3]:
x = 'A'
if cell_row in rows[3:6]:
x = 'B'
if cell_row in rows[6:9]:
x = 'C'
if cell_col in cols[0:3]:
y = 'a'
if cell_col in cols[3:6]:
y = 'b'
if cell_col in cols[6:9]:
y = 'c'
return (['Aa','Ab','Ac','Ba','Bb','Bc','Ca','Cb','Cc'].index(x+y))+1
Given that a sudoku board is comprised of 9 3x3 squares the purpose of this function is to take the coordinates of a cell on the board and return the number of the 3x3 square to which the cell belongs (where the square in the top left is number 1, and the bottom right is number 9). The input 'cell' is in the form ['A5', 6] where A indicates the row, 5 the column and 6 the value of the cell.
The code that I have works but there's got to be a much more efficient or presentable way of doing it. I would be grateful for any suggestions.

Personally, I don't think magic numbers like '65' and '97' make the solution more presentable! How about:
def square(cell):
rows = 'ABCDEFGHI'
cell_row = rows.index(cell[0][0])
cell_col = int(cell[0][1]) - 1
return 3 * (cell_row // 3) + cell_col // 3 + 1

I was able to make a greatly simplified version of your formula. I started by assigning both the row and column a 0-based index. Then I used integer division to only get the information about what 3-block the square is in. Since moving down a 3-block of rows increases the index by 3 while moving to the right only increases it by 1, I multiply the row index by 3 after the division. Here's the finished function:
def square(cell):
coords = (ord(cell[0][0]) - 65,int(cell[0][1]) - 1)
return 3 * (coords[0] // 3) + coords[1] // 3 + 1

Edit: Fixed offset by 1 - even though I would rather start at 0 as you'll probably want to use the returned value as an index for another (sub-)array.
And as I cannot comment on other answers yet just my 2 cents here:
cdlane's answer is slightly slower than the one presented here. If you get rid of the .lower() (I assume you don't care about fail safes at this point) and use Brien's answer you gain another slight performance boost. I don't know how often you'll evaluate square() but maybe it's worth to ditch readability for performance ;)
I think the attached snippet should do the trick.
def square(cell):
# http://www.asciitable.com/
# https://docs.python.org/3/library/functions.html#ord
row = ord(cell[0][0].lower()) - 97
column = int(cell[0][1])-1
return 3*(row//3) + column//3 + 1

Related

How to fix wrong calculations in Python Mode for Processing?

I try to implement a visual illustration of Pascal's triangle with Python Mode for Processing for MAC OS X. One of the necessary steps is, of course, the calculation of the binomial coefficients in each row of the triangle. I chose to do it in a recursive way instead of calculating factorials. My code works well in Jupyter, but produces different outcomes in Processing. Does anybody know why and how I can fix the problem?
rows = 301
pascal=[[1], [1,1]]
for i in range (rows):
last_row = pascal[len(pascal)-1]
next_row = [1] +[last_row[i]+last_row[i+1] for i in range(len(last_row)) if i < len(last_row)-1] +[1]
pascal.append(next_row)
print (pascal[35][16])
The code produces the correct results when executed in Jupyter, but has different results in Processing. The problems begin in row 35 of the triangle (countig starts from 0). The 16th element in this row should be 4059928950 but Processing calculates -235038346. And from then on, the calculations in Processing seem to be often wrong.
The most principled approach would be to find a big integer library that you can call from Jython, but since all you need is addition, it is easy to write your own function which will take two base 10 string representations of positive integers and return the string representation of their sum:
rows = 301
def add_nums(s1,s2):
#reverse strings and 0-pad to be of the same length
s1 = s1[::-1]
s2 = s2[::-1]
s1 += '0'*(max(len(s1),len(s2)) - len(s1))
s2 += '0'*(max(len(s1),len(s2)) - len(s2))
dsum = []
c = 0 #carry
for d1,d2 in zip(s1,s2):
a,b = int(d1), int(d2)
c,r = divmod(a+b+c,10)
dsum.append(str(r))
if c > 0: dsum.append('1')
return ''.join(reversed(dsum))
pascal=[['1'], ['1','1']]
for i in range (rows):
last_row = pascal[len(pascal)-1]
next_row = ['1'] +[add_nums(last_row[i],last_row[i+1]) for i in range(len(last_row)) if i < len(last_row)-1] +['1']
pascal.append(next_row)
print (pascal[35][16]) #prints 4059928950

Most constraining variable and least constraining value for N-Queens - Python

I was trying to solve the N-Queens(Only 1 solution) problem and I succeeded but my program could only calculate up to N = 47 in a good amount of time so I tried to implement least constraining value and most constraining variable and even though it got faster, it was still slow. What can I do to be able to calculate up to N = 1000?
def solve(n, x, board, mid_rows, sd_squares):
# If we are on the last row, it means we have put all the queens:
if x >= n:
print_board(board)
sys.exit(0)
for i in sd_squares:
# If we can put a queen on the current square, do it
if isOk(board, mid_rows[x], i, n):
board[mid_rows[x]][i] = 1
# Do the same thing for the next row
solve(n, x + 1, board, mid_rows, sd_squares)
# If we are here, it means we put the queen in the wrong square so we have to remove that queen
board[mid_rows[x]][i] = 0
I can't post the whole code because it's too long but please note that isOk(board, x, y, n) is a function that tells if we put a queen on the x row and y column it threatens other queens or not.
mid_rows is an array that includes the most middle rows to the side rows so like let's say n = 5, then it's [2,3,1,4,0] or when n = 6 it's [3,2,4,1,5,0].
sd_squares is a list that contains the side squares to middle squares. Like when n = 5 it's [0,4,1,3,2] or when n = 6 it's [0,5,1,4,2,3].

Generating list of coordinates to represent route taken between two coordinates

EDIT: I asked a friend and he found a way, generate a list of possible squares the convoy can move to from the original square and choose the one closes to the end coordinates, then generate a list for that square and again choose the closest ect.
I am making a game where among other things you move a convoy. The map is basically a Cartesian coordinate system, the coordinates are a tuple (X, Y).
What i need is to find a way to determine the sectors that the convoy passes through, since one only has the start point and chosen end point, to later be able to change the corresponding values of those sectors to simulate fog of war.
I tried creating a square around the two points and then a list for all possible Y and X coordinates (X/Y_movement_coords_loc).
Now the problem is finding the shortest possible route through this square, but i cant figure out a formula, since there are quiet a lot of possibilities and what i tried includes a division which often leads to fractions or division by 0. Neither of which i can use.
y_per_x_coord = len(y_movement_coords_loc) / len(x_movement_coords_loc)
Also the code as is now, for some inexplicable reason hangs at the
for x_index in range(len(x_movement_coords_loc)):
loop, it enters the if clause, appends to the list but 3 out of 5 times doesn't reach the res_index_list, i checked with prints.
Anyone out there who can help me with my problem?
Thanks in advance.
x_movement_loc = old_coord_loc[0] - new_coord_loc[0]
y_movement_loc = old_coord_loc[1] - new_coord_loc[1]
x_movement_coords_loc = []
y_movement_coords_loc = []
moved_tiles_coords = []
negative_movement = 1
if x_movement_loc < 0:
x_movement_loc *= -1
negative_movement = -1
if y_movement_loc < 0:
y_movement_loc *= -1
negative_movement = -1
for u in range(x_movement_loc):
x_movement_coords_loc.append(old_coord_loc[0] + (u * negative_movement))
for u in range(y_movement_loc):
y_movement_coords_loc.append(old_coord_loc[1] + (u * negative_movement))
counter = 0
y_per_x_coord = len(y_movement_coords_loc) / len(x_movement_coords_loc)
for x_index in range(len(x_movement_coords_loc)):
for y_index in range(y_per_x_coord):
moved_tiles_coords.append((x_movement_coords_loc[x_index], y_movement_coords_loc[y_index + counter]))
counter += y_per_x_coord
if (x_movement_coords_loc[x_index], y_movement_coords_loc[counter]) != new_coord_loc:
moved_tiles_coords.append((x_movement_coords_loc[x_index], y_movement_coords_loc[counter]))
res_index_list = [1, 3, 5, 7, 9]

Is there a better way to write the following method in python?

I am writing a small program, in python, which will find a lone missing element from an arithmetic progression (where the starting element could be both positive and negative and the series could be ascending or descending).
so for example: if the input is 1 3 5 9 11, then the function should return 7 as this is the lone missing element in the above AP series.
The input format: the input elements are separated by 1 white space and not commas as is commonly done.
Here is the code:
def find_missing_elm_ap_series(n, series):
ap = series
ap = ap.split(' ')
ap = [int(i) for i in ap]
cd = []
for i in range(n-1):
cd.append(ap[i+1]-ap[i])
common_diff = 0
if len(set(cd)) == 1:
print 'The series is complete'
return series
else:
cd = [abs(i) for i in cd]
common_diff = min(cd)
if ap[0] > ap[1]:
common_diff = (-1)*common_diff
new_ap = []
for i in range(n+1):
new_ap.append(ap[0] + i*common_diff)
missing_element = set(new_ap).difference(set(ap))
return missing_element
where n is the length of the series provided (the series with the missing element:5 in the above example).
I am sure there are other shorter and more elegant way of writing this code in python. Can anybody help ?
Thanks
BTW: i am learning python by myself and hence the question.
Based on the fact that if an element is missing it is exactly expected-sum(series) - actual-sum(series). The expected sum for a series with n elements starting at a and ending at b is (a+b)*n/2. The rest is Python:
def find_missing(series):
A = map(int, series.split(' '))
a, b, n, sumA = A[0], A[-1], len(A), sum(A)
if (a+b)*n/2 == sumA:
return None #no element missing
return (a+b)*(n+1)/2-sumA
print find_missing("1 3 5 9") #7
print find_missing("-1 1 3 5 9") #7
print find_missing("9 6 0") #3
print find_missing("1 2 3") #None
print find_missing("-3 1 3 5") #-1
Well... You can do simpler, but it would completely change your algorithm.
First, you can prove that the step for the arithmetic progression is ap[1] - ap[0], unless ap[2] - ap[1] is lower in magnitude than it, in which case the missing element is between terms 0 and 1. (This is true as there is a single missing element.)
Then you can just take ap[0] + n * step and print the first one that doesn't match.
Here is the source code (also implementing some minor shortcuts, such as grouping your first three lines into one):
def find_missing_elm_ap_series(n, series):
ap = [int(i) for i in series.split(' ')]
step = ap[1] - ap[0]
if (abs(ap[2] - ap[1]) <= abs(step)): # Check missing elt is not between 0 and 1
return ap[0] + ap[2] - ap[1]
for (i, val) in zip(range(len(ap)), ap): # And check position of missing element
if ap[0] + i * step != val:
return ap[0] + i * step
return series # missing element not found
The code appears to be working. There is perhaps a slightly easier way to get it done. This is due to the fact that you don't have to attempt to look through all of the values to get the common difference. The following code simply looks at the difference between the 1st and 2nd as well as the last and second last.
This works in the event that only a single value is missing (and the length of the list is at least 3). As the min difference between the values will provide you the common difference.
def find_missing(prog):
# First we cast them to numbers.
items = [int(x) for x in prog.split()]
#Then we compare the first and second
first_to_second = items[1] - items[0]
#then we compare the last to second last
last_to_second_last = items[-1] - items[-2]
#Now we have to care about which one is closes
# to zero
if abs(first_to_second) < abs(last_to_second_last):
change = first_to_second
else:
change = last_to_second_last
#Iterate through the list. As soon as we find a gap
#that is larger than change, we fill in and return
for i in range(1, len(items)):
comp = items[i] - items[i-1]
if comp != change:
return items[i-1] + change
#There was no gap
return None
print(find_missing("1 3 5 9")) #7
print(find_missing("-1 1 3 5 9")) #7
print(find_missing("9 6 0")) #3
print(find_missing("1 2 3")) #None
The previous code shows this example. First of all attempting to find change between each of the values of the list. Then iterating till the change is missed, and returning the value that has been expected.
Here's the way I thought about it: find the position of the maximum difference between the elements of the array; then regenerate the expected number in the sequence from the other differences (which should be all the same and the minimum number in the differences list):
def find_missing(a):
d = [a[i+1] - a[i] for i in range(len(a)-1)]
i = d.index(max(d))
x = min(d)
return a[0] + (i+1)*x
print find_missing([1,3,5,9,11])
7
print find_missing([1,5,7,9,11])
3
Here are some ideas:
Passing the length of the series seems like a bad idea. The function can more easily calculate the length
There is no reason to assign series to ap, just do a function using series and assign the result to ap
When splitting the string, don't give the sep argument. If you don't give the argument, then consecutive white space will also be removed and leading and trailing white space will also be ignored. This is more friendly on the format of the data.
I've combined a few operations. For example the split and the list comprehension converting to integer make sense to group together. There is also no need to create cd as a list and then convert that to a set. Just build it as a set to start with.
I don't like that the function returns the original series in the case of no missing element. The value None would be more in keeping with the name of the function.
Your original function returned a one item set as the result. That seems odd, so I've used pop() to extract that item and return just the missing element.
The last item was more of an experiment with combining all of the code at the bottom into a single statement. Don't know if it is better, but it's something to think about. I built a set with all the correct numbers and a set with the given numbers and then subtracted them and returned the number that was missing.
Here's the code that I came up with:
def find_missing_elm_ap_series(series):
ap = [int(i) for i in series.split()]
n = len(ap)
cd = {ap[i+1]-ap[i] for i in range(n-1)}
if len(cd) == 1:
print 'The series is complete'
return None
else:
common_diff = min([abs(i) for i in cd])
if ap[0] > ap[1]:
common_diff = (-1)*common_diff
return set(range(ap[0],ap[0]+common_diff*n,common_diff)).difference(set(ap)).pop()
Assuming the first & last items are not missing, we can also make use of range() or xrange() with the step of the common difference, getting rid of the n altogether, it can also return more than 1 missing item (although not reliably depending on number of items missing):
In [13]: def find_missing_elm(series):
ap = map(int, series.split())
cd = map(lambda x: x[1]-x[0], zip(ap[:-1], ap[1:]))
if len(set(cd)) == 1:
print 'complete series'
return ap
mcd = min(cd) if ap[0] < ap[1] else max(cd)
sap = set(ap)
return filter(lambda x: x not in sap, xrange(ap[0], ap[-1], mcd))
....:
In [14]: find_missing_elm('1 3 5 9 11 15')
Out[14]: [7, 13]
In [15]: find_missing_elm('15 11 9 5 3 1')
Out[15]: [13, 7]

Is it possible to calculate the number of count inversions using quicksort?

I have already solved the problem using mergesort, now I am thinking is that possible to calculate the number using quicksort? I also coded the quicksort, but I don't know how to calculate. Here is my codeļ¼š
def Merge_and_Count(AL, AR):
count=0
i = 0
j = 0
A = []
for index in range(0, len(AL) + len(AR)):
if i<len(AL) and j<len(AR):
if AL[i] > AR[j]:
A.append(AR[j])
j = j + 1
count = count+len(AL) - i
else:
A.append(AL[i])
i = i + 1
elif i<len(AL):
A.append(AL[i])
i=i+1
elif j<len(AR):
A.append(AR[j])
j=j+1
return(count,A)
def Sort_and_Count(Arrays):
if len(Arrays)==1:
return (0,Arrays)
list1=Arrays[:len(Arrays) // 2]
list2=Arrays[len(Arrays) // 2:]
(LN,list1) = Sort_and_Count(list1)
(RN,list2) = Sort_and_Count(list2)
(M,Arrays)= Merge_and_Count(list1,list2)
return (LN + RN + M,Arrays)
Generally no, because during the partitioning, when you move a value to its correct side of the pivot, you don't know how many of the values you're moving it past are smaller than it and how many are larger. So, as soon as you do that you've lost information about the number of inversions in the original input.
I come across this problem for some times, As a whole, I think it should be still ok to use quick sort to compute the inversion count, as long as we do some modification to the original quick sort algorithm. (But I have not verified it yet, sorry for that).
Consider an array 3, 6, 2, 5, 4, 1. Support we use 3 as the pivot, the most voted answer is right in that the exchange might mess the orders of the other numbers. However, we might do it different by introducing a new temporary array:
Iterates over the array for the first time. During the iteration, moves all the numbers less than 3 to the temporary array. For each such number, we also records how much number larger than 3 are before it. In this case, the number 2 has one number 6 before it, and the number 1 has 3 number 6, 5, 4 before it. This could be done by a simple counting.
Then we copy 3 into the temporary array.
Then we iterates the array again and move the numbers large than 3 into the temporary array. At last we get 2 1 3 6 5 4.
The problem is that during this process how much inversion pairs are lost? The number is the sum of all the numbers in the first step, and the count of number less than the pivot in the second step. Then we have count all the inversion numbers that one is >= pivot and another is < pivot. Then we could recursively deal with the left part and the right part.

Categories

Resources