I've been told to make a program that solves the eight queens puzzle using breadth first search. This is what i've got so far:
def n_queens(n, width):
if n == 0:
return [[]
else:
return add_queen(n-1, width, n_queens(n-1, width))
def add_queen(new_row, width, previous_solutions):
solutions = []
for sol in previous_solutions:
for new_col in range(width):
if safe_queen(new_row, new_col, sol):
solutions.append(sol + [new_col])
return solutions
def safe_queen(new_row, new_col, sol):
for row in range(new_row):
if (sol[row] == new_col or
sol[row] + row == new_col + new_row or
sol[row] - row == new_col - new_row):
return 0
return 1
for sol in n_queens(8, 8):
print sol
Is there any way to improve this?
I don't think BFS is quite the way I'd reason about this problem. Rather, focus on recursively generating the possible placements. For each queen placed, there are only a certain number of possible placements in next row down that can't be attacked. "Place" a queen and recurse on each of those locations, and terminate when you've placed your total number of queens. Hopefully you'll recognize that a for loop mixed in with some recursive calls seems to be like a decent idea. Also remember to "pick up" the queens you placed as your recursion returns.
Agreed with previous entry. The algorithm described above is 'depth-first' search instead of 'breadth-first' search and is much more efficient for this type of problems.
Related
I have encountered the edit distance (Levenshtein distance) problem. I have looked at other similar stackoverflow questions, and is certain that my question is distinct from them - either from the language used or the approach.
I have used a 2D array that compares the two strings, and dynamic programming to store previous values. If i and j in the string indices match, it would output 0, as we don't need to do anything; else, the output is 1. It is as shown in the picture below, the orange arrow represents a match.
(Code below is edited after suggestions from answers)
def edit_distance(source, target):
n = len(target)+1
m = len(source)+1
# let D denote the 2-dimensional array, m is the column, n is row
D = [ [0]*m for _ in range(n)]
# the loop inside is the target string, we operate this
# while the loop outside is the source string
for j in range(0, m):
for i in range(0, n):
if target[i-1] == source[j-1]:
# match, insertion and deletion, find the path with least move
D[i][j] = min(D[i-1][j-1], D[i-1][j]+1, D[i][j-1]+1)
else:
# mismatch, insertion and deletion, find the path with least move
D[i][j] = min(D[i-1][j-1]+1, D[i-1][j]+1, D[i][j-1]+1)
return D[n-1][m-1]
print(edit_distance("distance", "editing"))
However, the final output was 8 in my code, while the optimal editing distance between the strings "editing" and "distance" should be 5, and I am very confused. Could you please help with it from the approach of dynamic programming?
You have 2 mistakes.
First is intialization. You fill everything with 0s, but then when you want to fill in D[1][m] you look in the cell above (where it should be m) and you find a 0. Make sure the borders are correctly filled in.
Second your iterations are off. range(1, n) over 'editing' will give you 'diting'. To fix it N and M by 1 (n=len(target) + 1) and in your comparison use target[i-1] == source[j-1].
Ah, looks like I have found a solution, that I'll have to answer my own question now. (I'm still confused with some parts, and am only answering this question to briefly introduce the new implementation, as to save the time of other kind helpers)
So firstly, I have missed a condition in the original code, that is, what if one of the two string inputs are empty? Then we'll have to insert everything from the other string. Henceforth, the optimal editing distance is just the length of this other string.
if i == 0:
D[i][j] = j
elif j == 0:
D[i][j] = i
Also, regarding the original for-loop of the code, I learnt my mistakes from GeeksforGeeks. If my understanding is correct, they are saying that if two indices (i and j) are consistent, all we need to do is to move diagonally upward on the graph (i-1 and j-1) without adding any counts.
Else if the indices do not match, we move either to the direction of i-1, j-1 or diagonally up dependently. I was right on this, apart from the fact the count is added after the move, whereas I have added them during the move.
I am still a bit unsure with how it worked, however I'll compare the two algorithms below, would be appreciated if someone could explain it further in the comments.
My original for-loop (present in the question)
for j in range(0, m):
for i in range(0, n):
if target[i-1] == source[j-1]:
D[i][j] = min(D[i-1][j-1], D[i-1][j]+1, D[i][j-1]+1)
else:
D[i][j] = min(D[i-1][j-1]+1, D[i-1][j]+1, D[i][j-1]+1)
And below is the new for-loop, whose output is correct after testing:
if target[i-1] == source[j-1]:
D[i][j] = D[i-1][j-1]
else:
D[i][j] = 1 + min(D[i][j-1], D[i-1][j], D[i-1][j-1])
Would be appreciated if someone could further explain how did this work, as I still only have a superfacial understanding of the new code
Final code:
def edit_distance(target, source):
m = len(target)+1
n = len(source)+1
D = [[0 for x in range(n)] for x in range(m)]
for i in range(m):
for j in range(n):
if i == 0:
D[i][j] = j
elif j == 0:
D[i][j] = i
elif target[i-1] == source[j-1]:
D[i][j] = D[i-1][j-1]
else:
D[i][j] = 1 + min(D[i][j-1], D[i-1][j], D[i-1][j-1])
return D[m-1][n-1]
print(edit_distance("distance", "editing"))
# output = 5, which is correct
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].
One of the samples for the Google or-tools is a solver for the n-queens problem. At the bottom it says that the implementation can be improved by adding symmetry breaking constraints to the constraint solver.
Looking around the internet, I found the symmetry breaking constraints for the n-queens problem, but I cannot for the life of me figure out how to convert those to constraints to python code that implements them.
EDIT: this was a bad question, let's update...
What have I tried?
Here is the setup from the first link above:
from ortools.constraint_solver import pywrapcp
N = 8
solver = pywrapcp.Solver("n-queens")
# Creates the variables.
# The array index is the column, and the value is the row.
queens = [solver.IntVar(0, N - 1, "x%i" % i) for i in range(N)]
# Creates the constraints.
# All rows must be different.
solver.Add(solver.AllDifferent(queens))
# All columns must be different because the indices of queens are all different.
# No two queens can be on the same diagonal.
solver.Add(solver.AllDifferent([queens[i] + i for i in range(N)]))
solver.Add(solver.AllDifferent([queens[i] - i for i in range(N)]))
# TODO: add symmetry breaking constraints
db = solver.Phase(queens, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE)
solver.NewSearch(db)
num_solutions = 0
while solver.NextSolution():
num_solutions += 1
solver.EndSearch()
print()
print("Solutions found:", num_solutions)
print("Time:", solver.WallTime(), "ms")
I know I can implement simple constraints successfully. If I wanted to ensure the solution always has a queen in the first column on the first row, I could implement that like this:
solver.Add(queens[0] == 0)
The queens[0] variable represents the queens location in the first column and this constraint is only satisfied when the first column has a queen in the first row. This of course is not what I want to do however because it's possible that a solution does not include any corner cells.
The symmetry breaking constraints for the n-queens problem are found below. They are pulled directly from the link in the second paragraph.
I understand how these constraints work. The idea is that you can apply this function to each cell on the n-queens board in order to transform the state into an equivalent state. One of these states will be the canonical representation of that state. This is used as a method to prune future processing by eliminating duplicate evaluations.
If I were just implementing this in an after the fact way, I would do exactly what I describe above, convert the state using each possible symmetry breaking function, calculate some sort of state hash (e.g. a string of the selected row in each column) and select the one that's the lowest for each proposed solution. Skip future processing on ones we have seen before.
My problem is that I don't know how to convert these transformations into constraints for the google or-tools constraint programming solver.
Let's take a look at the simplest one, d1(r[i] = j) => r[j] = i, reflection about the main diagonal. What I know is that the transformation needs to be applied to all cells, then compared against the current state in order to prevent one from being expanded. I don't understand enough about python to understand what kind of expression works here for the transformation, and I just can't figure out how to create the constraint that compares the transformation to the current state for this particular solver.
state = [queens[i].Value() for i in range(N)]
symX = [state[N - (i + 1)] for i in range(N)]
symY = [N - (state[i] + 1) for i in range(N)]
symD1 = [state.index(i) for i in range(N)]
symD2 = [N - (state.index(N-(i+1)) + 1) for i in range(N)]
symR90 = [N - (state.index(i) + 1) for i in range(N)]
symR180 = [N - (state[N-(i+1)] + 1) for i in range(N)]
symR270 = [state.index(N-(i+1)) for i in range(N)]
I tried to use a custom DecisionBuilder to prune the search tree using the symmetries as new constraints, but I couldn't make it work.
Instead I had to use a SearchMonitor that captures the event of every solution and check if that solution is a symmetry of a previous one.
Here i add the code of the SearchMonitor, the capture of the solution overriding the "AcceptSolution" function, and the gen_symetries function to calculate and check all possible symmetries.
class SearchMonitor(pywrapcp.SearchMonitor):
def __init__(self, solver, q):
pywrapcp.SearchMonitor.__init__(self, solver)
self.q = q
self.all_solutions = []
self.unique_solutions = []
self.n = len(self.q)
def AcceptSolution(self):
qval = [self.q[i].Value() for i in range(self.n)]
self.all_solutions.append(qval)
symmetries = [vv in self.unique_solutions for vv in gen_symmetries(self.n, qval)]
if sum(symmetries) == 0:
self.unique_solutions.append(qval)
return False
def gen_symmetries(n, solution):
symmetries = []
#x(r[i]=j) → r[n−i+1]=j
x = list(range(n))
for index in range(n):
x[n - 1 - index] = solution[index]
symmetries.append(x)
#y(r[i]=j) → r[i]=n−j+1
y = list(range(n))
for index in range(n):
y[index] = (n - 1 - solution[index])
symmetries.append(y)
#d1(r[i]=j) → r[j]=i
d1 = list(range(n))
for index in range(n):
d1[solution[index]] = index
symmetries.append(d1)
# d2(r[i]=j) → r[n−j+1]=n−i+1
d2 = list(range(n))
for index in range(n):
d2[n - 1 - solution[index]] = (n - 1 - index)
symmetries.append(d2)
# r90(r[i]=j) → r[j] = n−i+1
r90 = list(range(n))
for index in range(n):
r90[solution[index]] = (n - 1 - index)
symmetries.append(r90)
# r180(r[i]=j) → r[n−i+1]=n−j+1
r180 = list(range(n))
for index in range(n):
r180[n - 1 - index] = (n - 1 - solution[index])
symmetries.append(r180)
# r270(r[i]=j) → r[n−j+1]=i
r270 = list(range(n))
for index in range(n):
r270[n - 1 - solution[index]] = index
symmetries.append(r270)
return symmetries
Later you just have to add the monitor to your solver like this.
monitor = SearchMonitor(solver, queens)
solver.Solve(db, monitor)
solver.NewSearch(db)
And finally just printing all the unique solutions
print("Unique Solutions:", len(monitor.unique_solutions), monitor.unique_solutions)
You can see the full working example in the gist.
https://gist.github.com/carlgira/7a4e6cf0f7b7412762171015917bccb4
You must use the known symmetry relations between the sought solutions to identify constraints that will eliminate equivalent solutions.
For every solution with queens[0] >= N/2, then there is another, vertically mirrored, solution with queens[0] <= N/2. Therefore, we can seek for the solution with the smaller value of queens[0] and add the following constraint:
solver.Add(queens[0] < (N+1)//2) # Handle both even and odd values of N
Then, a solution satisfying the condition queens[0] < queens[N-1] has an equivalent, horizontally-mirrored, solution satisfying queens[0] > queens[N-1]. You can tell the solver to look for only those solutions, where the queen in the leftmost column is below the queen in the rightmost column:
solver.Add(queens[0] < queens[N-1])
I couldn't easily formulate a constraint reflecting the rotational symmetry, but I believe that it is possible.
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
I have a 2D array in Python (version 3.2) that looks like this:
...AAA....
...AAABB..
..BBBBBCC.
.....CCCC.
.DDD..CC..
.DDD......
It represents a kind of map with areas painted different colors. The above example shows four distinct regions, A, B, C, and D.
Here's an example of indexing the array:
map[1][5] == 'A' would return True.
I'm trying to write a function that takes in an array like this, and a row/col index, and returns the number of adjoining spaces that are of the same "color". So using that example above, here are some return values (the arguments are the array, row, and column number respectively:
6 <-- countArea(map, 5, 2)
8 <-- countArea(map, 2, 8)
I'd like to implement this as a recursive function, but I can't figure it out. Here's what I have so far:
def countArea(map, row, col):
key = map[row][col]
if (map[row-1][col] == key):
return 1 + countArea(map, row-1, col)
elif (map[row+1][col] == key):
return 1 + countArea(map, row+1, col)
elif (map[row][col+1] == key):
return 1 + countArea(map, row, col+1)
elif (map[row][col-1] == key):
return 1 + countArea(map, row, col-1)
else:
return 1
I know I'm missing something basic here. I'm basically saying "here is the current character, now look in each direction to see if it has the same character."
My question is, what am I missing in this recursive definition?
Thanks for your help.
My question is, what am I missing in this recursive definition?
Once a grid square has been counted, it must not be counted again (this includes counting by recursive invocations of countArea()!)
Your current algorithm goes as far north as it can, and then keeps taking one step to the south followed by one step to the north. This two-step sequence repeats until you run out of stack space.
If you like, you could read up on algorithms for this problem in Wikipedia.
In your code the algorithm would look one field left of a given input field and in the recursive call would again call the function on the initial field. (What you obviously don't want since it would lead to an infinite recursion)
Approach 1
A method to overcome this problem while still using recursion would be to specify a direction where the recursion should look for more fields of the same type. For example the call to the field directly north (or above) of the initial one could look recursively farer to the north or east (or right), the one to the east go south (below) and east and so on.
By intelligently choosing the first step you can ensure, that there is no overlap in the scanned regions. However it needs some adaptions to specify the directions the recursive call should scan. BUT: Note that this algorithm would not work if the area is overhanging so if not every field northeast of the starting point can be reached by just moving right and up.
There exist more algorithms like this that are also capable to solve the the mentioned problem. Have a look at Flood Filling on wikipedia.
Approach 2
You can also save the already visited fields in some way and directly return from the recursive call if the field was already visited.
The following implementation should work:
def countArea(map, row, col, key=None, seen=None):
if key is None:
key = map[row][col]
if seen is None:
seen = set()
seen.add((row, col)) # mark this location as visited
n = 1
for dy, dx in [(0, 1), (1, 0), (-1, 0), (0, -1)]:
r, c = row + dy, col + dx
if r < 0 or r >= len(map) or c < 0 or c >= len(map[0]): # check boundaries
continue
# only increment and recurse if key matches and we haven't already visited
if map[r][c] == key and (r, c) not in seen:
n += countArea(map, r, c, key, seen)
return n
Example:
>>> print '\n'.join(''.join(row) for row in map)
...AAA....
...AAABB..
..BBBBBCC.
.....CCCC.
.DDD..CC..
.DDD......
>>> countArea(map, 5, 2)
6
>>> countArea(map, 2, 8)
8
Note that this assumes that areas with the same key that are only touching at a diagonal should be considered separate, for example for the following map countArea(map, 0, 0) and countArea(map, 1, 1) would both return 1:
A.
.A
As a side note, you should not use map as a variable name, as it will mask the builtin map() function.