We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF or read online on Scribd
You are on page 1/ 100
Backtracking
* Systematically search for a solution
* Build the solution one step at a time
* If we hit a dead-end
* Undo the last step
* Try the next optionEight queens
* Place 8 queens on a chess
board so that none of them
attack each other
* In chess, a queen can
move any number of
squares along a row
column or diagonalN queens
e Place N queens on an
N x N chess board so that
none attack each other
° N= 2, 3 impossible
* N= 4is possible
° And all bigger N as well
Q
Q
Q8 queens
* Clearly, exactly one queen
in each row, column
« Place queens row by row
* In each row, place a queen
in the first available column
° Can't place a queen in the
8th row!Backtracking
* Keep trying to extend the next solution
* If we cannot, undo previous move and try again
* Exhaustively search through all possibilities
« ... but systematically!Coding the solution
* How do we represent the board?
* nxn grid, number rows and columns from @ to n-1
* board[i][}] = 1 indicates queen at (i,j)
* board[i][j] == @ indicates no queen
* We know there is only one queen per row
* Single list board of length n with entries @ to n-1
* board[i] == j : queen in row 1, column j, i.e. (i, })Overall structure
def placequeen(i,board): # Trying row i
for each c such that Ci,c) ts available:
place queen at (1,c) and update board
if i == n-1:
return(True) # Last queen has been placed
else:
extendsoln = placequeen(i+1, board)
if extendsoln:
return(True) # This solution extends fully
else:
undo this move and update board
else:
return(False) # Row i failedBacktracking
* Systematically search for a solution
* Build the solution one step at a time
« If we hit a dead-end
* Undo the last step
* Try the next optionCoding the solution
* How do we represent the board?
* nx ngnd, number rows and columns from @ to n-1
* board[i][}] = 1 indicates queen at (i,j)
* board[i][j] — @ indicates no queen
* We know there is only one queen per row
* Single list board of length n with entnes 0 to n-1
* board[i] == j : queen in row 1, column j, ie. (i,j)Overall structure
def placequeen(i,board): # Trying row i
for each c such that Ci,c) ts available:
place queen at (1,c) and update board
if i == n-1:
return(True) # Last queen has been placed
else:
extendsoln = placequeen(i+1, board)
if extendsoln:
return(True) # This solution extends fully
else:
undo this move and update board
else:
return(False) # Row i failedUpdating the board
* Our 1-D and 2-D representations keep track of the
queens
* Need an efficient way to compute which squares are
free to place the next queen
* nxnattack grid
* attack(iJ[)] == lif (i,)}) is attacked by a queen
* attack{.J[j] == @if (1,)) is currently available
* How do we undo the effect of placing a queen?Updating the board
* Our 1-D and 2-D representations keep track of the
queens
* Need an efficient way to compute which squares are
free to place the next queen
* nxnattack grid
* attack(t][j] == Lif (i,}) is attacked by a queen
* attack[.][}] == Oif (1,3) ts currently available
* How do we undo the effect of placing a queen?
* Which attack[i1][j}] should be reset to 0?Updating the board
* Queens are added row by row
* Number the queens 0 to n-1
* Record earliest queen that attacks each square
* attack[1)[j] == kif (.,)) was first attacked by
queen k
* attack[i}[j] == -1if (i,j) is free
* Remove queen k — reset attack(iJ][{j] == kto-1
* All other squares still attacked by earlier queensUpdating the board
* Queens are added row by row
* Number the queens 0 to n-1
« Record earliest queen that attacks each square
* attack[ij[j] == kif (1, )) was first attacked by
queen k
* attack[iJ[j] = -1if (i,j) is free
* Remove queen k — reset attack({i][j] == kto-1
* All other squares still attacked by earlier queensA better representation
* How many queens attack row i?
* How many queens attack row j?
* An individual square (i,j) is attacked by upto 4
queens |
* Queen on row i and on column j
* One queen on each diagonal through (i,j)Numbering diagonals
Decreasing diagonal:
column - row is invariant
Increasing diagonal:
column + row is invariant
(ij) is attacked if
* row iis attacked
* column j is attacked
* diagonal j-i is attacked
* diagonal j+i is attacked
NOWhe Wn =o
01234567
c+er=12Numbering diagonals
Decreasing diagonal:
column - row is invariant
Increasing diagonal:
column + row is invanant
(i,j) is attacked if
* row jis attacked
* column | is attacked
* diagonal j-i is attacked
* diagonal j+i is attacked
NOOewn—o
t-0
01234567
c+r=12O(n) representation
* row{i] «= 1 if row’ is attacked, @. .N-1
* col{i] == 1 if column iis attacked, 0. .N-1
* NWtoSE[i] == 1 if NW to SE diagonal i is \
attacked, -(N-1) to (N-1) yUpdating the board
* Ci,)) is free if
row[t J==col[j]==NitoSE[j-i]==SWtoNE[j+i]=-0
* Add queen at (i,j)
board[i} = 3
Crow[t),collj] ,NWtoSE[j-1],SWtoNE[j+i]) =
(1,1,1,1)
* Remove queen at (i,j)
board[i] = -1
Crow[i],col[j] ,NWtoSE[}-1],SWtoNE[j+i]) =
(8,0,0,0)Overall structure
def placequeen(i,board: # Trying row i
for each ¢ such that Ci,c) is available:
place queen ot (i,¢) and update board
if i == n-1:
return(True) # Last queen has been placed
else:
extendsoln = placequeen(i+1,board)
if extendsoln:
return(True) # This solution extends fully
else:
undo this move and update board
else:
return(False) # Row i failedImplementation details
* Maintain board as nested dictionary
board[ ‘queen’ ][i] = ) : Queen located at (i,j)
board["row'){i] = 1: Row i attacked
board[‘col'][i} = 1: Column: attacked
board['nwtose'}[i] = 1: NWtoSW diagonal 1
attacked
board['swtone'J[i] = 1: SWtoNE diagonal i
attackedUpdating the board
* Ci, )) Is free if
row[i ]==col [j] ==NitoSE[j-i]--SWtoNE[j+i]--0
* Add queen at (i, ))
board[i} = j
CrowLi],col[j] ,NWtoSE[}-1],SWtoNE[}j+i]) =
d1,2,1,1)
+ Remove queen at (i, 3)
board[i] = -1
Crow[i],col(j] ,NWtoSE(j-i],SWtoNE(j+1]) =
(@,0,0,0)ag
return
ee)
ane -PeL eo a Ea [remPe a a) [osmYAll solutions?
def placequeen(i,board): # Try row i
for each c such that (i,c) is available:
place queen at (i,c) and update board
if i s= n-1;
record solution # Last queen placed
else:
extendsoln = placequeen(i+1,board)
undo this move and update boardplacequeen( 0, box
tatoo)ee eee pier TE |Recall 8 queens
def placequeen(i,board: # Trying row i
for each ¢ such that (i,c) is available:
place queen at (i,c) and update board
if i == n-1:
return(True) # Last queen has been placed
else:
extendsoln = placequeen(1+1,board)
if extendsoln:
return(True) # This solution extends fully
else:
undo this move and update board
else:
return(False) # Row i failedGlobal variables
* Can we avoid passing board explicitly to each
function?
* Can we have a single global copy of board that all
functions can update?Scope of name
* Scope of name is the portion of code where it is
available to read and update
* By default, in Python, scope is local to functions
* But actually, only if we update the name inside
the functionTwo examples
def FQ:
y = (x)
vnt(y)
we. ?
OoGlobal variables
* Actually, this applies
only to immutable
valuesGlobal variables
* Actually, this applies def f():
only to immutable y « x{@]
values print(y)
x[{@] = 22
x = [7]
FORecall 8 queens
def placequeen(i , board} # Trying row i
for each c such that (i,c) is availeble:
place queen at (i,c) and update board
if i == n-1:
return(True) # Last queen has been placed
else:
extendsoln = placequeen(1+1,board)
if extendsoln:
return(True) # This solution extends fully
else:
undo this move and update board
else:
return(False) # Row i failedGlobal variables
+ Can we avoid passing board explicitly to each
function?
* Can we have a single global copy of board that all
functions can update?Two examples
def FO:
y = &)
oo
Xa?
FOroe eo hot mez) |Two examples
def fC): def F():
y=x vee
print(y) print(y)
x = 22
x=7
se) x=?
Fine! FOTwo examples
def f(): def f():
yer y=x
print(y) print(y)
x = 22
x= 7
fO x=7
Fine! fC) Error!
* If x is not found in f(), Python looks at enclosing
function for global xTwo examples
def f(): def f():
ye y=x
print(y) print(y)
x = 22
xa 7
FQ) x= 7
FO Error!
* If x is not found in f(), Python looks at enclosing
function for global x
* If x is updated in fC), it becomes a local name!