This is a program that solves a sudoku board using python and the back-tracking algorithm, but I don't seem to understand the recursion in solve(bo). Like it seems like if the condition is not met, then the index is reset to 0 and it continues to try numbers at the same position.
Can someone help explain in simpler terms perhaps, how the function back tracks and rechecks conditions?
board = [
[7,8,0,4,0,0,1,2,0],
[6,0,0,0,7,5,0,0,9],
[0,0,0,6,0,1,0,7,8],
[0,0,7,0,4,0,2,6,0],
[0,0,1,0,5,0,9,3,0],
[9,0,4,0,6,0,0,0,5],
[0,7,0,3,0,0,0,1,2],
[1,2,0,0,0,7,4,0,0],
[0,4,9,2,0,6,0,0,7]
]
def solve(bo):
find = find_empty(bo)
if not find:
return True
else:
row, col = find
for i in range(1,10):
if valid(bo, i, (row, col)):
bo[row][col] = i
if solve(bo):
return True
bo[row][col] = 0
return False
def valid(bo, num, pos):
# Check row
for i in range(len(bo[0])):
if bo[pos[0]][i] == num and pos[1] != i:
return False
# Check column
for i in range(len(bo)):
if bo[i][pos[1]] == num and pos[0] != i:
return False
# Check box
box_x = pos[1] // 3
box_y = pos[0] // 3
for i in range(box_y*3, box_y*3 + 3):
for j in range(box_x * 3, box_x*3 + 3):
if bo[i][j] == num and (i,j) != pos:
return False
return True
def print_board(bo):
for i in range(len(bo)):
if i % 3 == 0 and i != 0:
print("- - - - - - - - - - - - - ")
for j in range(len(bo[0])):
if j % 3 == 0 and j != 0:
print(" | ", end="")
if j == 8:
print(bo[i][j])
else:
print(str(bo[i][j]) + " ", end="")
def find_empty(bo):
for i in range(len(bo)):
for j in range(len(bo[0])):
if bo[i][j] == 0:
return (i, j) # row, col
return None
print_board(board)
solve(board)
print("___________________")
print_board(board)
Right! So the way this solution operates is it finds the next empty field on the board, attempts to insert number from range 1..9 there and then check the validity. The validity check is a tentative answer to whether a given number is correct, i.e. whether it does not produce a violation of the rules. If so, the number gets inserted into the array and solve is called recursively. Now, here the implicit backtracking is taking place. If this recursive call to solve fails to produce a fully consistent solution to the puzzle, then the whole "branch" starting with our tentative guess gets removed from the call stack, the failed guess is reset on the board (removed), and we continue to the next guess.
Related
im learning python, I wanted the sudoku numbers to be read from a csv file and to be reconstructed. that part works. but it does not solve the sudoku. it just prints the same input again.
Can somebody please help me? Any idea why?
Question number 2: Any Tipps how i could modify this code to give the number of possible sudoku solutions? In case, there are multiple solutions possible.
Thank you so much in Advanced! :)
import csv
with open('sudoko.csv') as f:
board = list(csv.reader(f,delimiter=';'))
def solve(bo):
find = find_empty(bo)
if not find:
return True
else:
row, col = find
for num in range(1,10):
if valid(bo, num, (row, col)):
bo[row][col] = num
if solve(bo):
return True
bo[row][col] = 0
return False
def valid(bo, num, pos):
# Check row
for field in range(len(bo[0])):
if bo[pos[0]][field] == num and pos[1] != field:
return False
# Check column
for line in range(len(bo)):
if bo[line][pos[1]] == num and pos[0] != line:
return False
# Check box
box_x = pos[1] // 3
box_y = pos[0] // 3
for i in range(box_y*3, box_y*3 + 3):
for j in range(box_x * 3, box_x*3 + 3):
if bo[i][j] == num and (i,j) != pos:
return False
return True
def print_board(bo):
for i in range(len(bo)):
if i % 3 == 0 and i != 0:
print("- - - - - - - - - - - - - ")
for j in range(len(bo[0])):
if j % 3 == 0 and j != 0:
print(" | ", end="")
if j == 8:
print(bo[i][j])
else:
print(str(bo[i][j]) + " ", end="")
def find_empty(bo):
for i in range(len(bo)):
for j in range(len(bo[0])):
if bo[i][j] == 0:
return (i, j) # row, col
return None
if __name__ == "__main__":
print_board(board)
solve(board)
print("___________________")
print("")
print_board(board)
For school, we should program a sudoku solver and I just followed a tutorial to program one and it works:
board = [
[7,8,0,4,0,0,1,2,0],
[6,0,0,0,7,5,0,0,9],
[0,0,0,6,0,1,0,7,8],
[0,0,7,0,4,0,2,6,0],
[0,0,1,0,5,0,9,3,0],
[9,0,4,0,6,0,0,0,5],
[0,7,0,3,0,0,0,1,2],
[1,2,0,0,0,7,4,0,0],
[0,4,9,2,0,6,0,0,7]
]
def solve(bo):
find = find_empty(bo)
if not find:
return True
else:
row, col = find
for i in range(1,10):
if valid(bo, i, (row, col)):
bo[row][col] = i
if solve(bo):
return True
bo[row][col] = 0
return False
def valid(bo, num, pos):
# Check row
for i in range(len(bo[0])):
if bo[pos[0]][i] == num and pos[1] != i:
return False
# Check column
for i in range(len(bo)):
if bo[i][pos[1]] == num and pos[0] != i:
return False
# Check box
box_x = pos[1] // 3
box_y = pos[0] // 3
for i in range(box_y*3, box_y*3 + 3):
for j in range(box_x * 3, box_x*3 + 3):
if bo[i][j] == num and (i,j) != pos:
return False
return True
def print_board(bo):
for i in range(len(bo)):
if i % 3 == 0 and i != 0:
print("- - - - - - - - - - - - - ")
for j in range(len(bo[0])):
if j % 3 == 0 and j != 0:
print(" | ", end="")
if j == 8:
print(bo[i][j])
else:
print(str(bo[i][j]) + " ", end="")
def find_empty(bo):
for i in range(len(bo)):
for j in range(len(bo[0])):
if bo[i][j] == 0:
return (i, j) # row, col
return None
print_board(board)
solve(board)
print("___________________")
print_board(board)
But my problem is we have to handle strings like:
003020600
900305000
001806400
008102900
700000008
006708200
002609500
800203009
005010300
and couldn't separate them with a comma. Do you have an idea how to fix the problem?
You can transform a multiline string to a 2d-list of integers like:
s = """003020600
900305000
001806400
008102900
700000008
006708200
002609500
800203009
005010300"""
board = [[*map(int, line)] for line in s.splitlines()]
Or, if you are reading these lines as input from stdin:
board = []
for _ in range(9):
board.append([*map(int, input())])
I was working on a Sudoku solver, and when I ran it, it just didn't work- the function f returned a none type. After debugging, I found out the code didn't solve some of the squares. I don't know why this happened, but I suspect the loop checking all the numbers possible in a square stops after finding one possible number and doesn't check the rest too. Can you please pinpoint me the problem in my code? Thanks.
Note: for empty spaces in the Sudoku, enter 0.
# enter the Sudoku as a two-dimensional array (a[i][j])
def square_check(array, loc_x, loc_y, val):
added_x = loc_x - (loc_x % 3)
added_y = loc_y - (loc_y % 3)
for i in range(3):
for j in range(3):
if i+added_y != loc_y or j+added_x != loc_x:
if array[i+added_y][j+added_x] == val:
return False
return True
def line_check(array, loc_x, loc_y, val):
for i in range(9):
if i != loc_x:
if array[loc_y][i] == val:
return False
return True
def col_check(array, loc_x, loc_y, val):
for i in range(9):
if i != loc_y:
if array[i][loc_x] == val:
return False
return True
def f(array):
flag = True
mark = True
for i in range(0, 9):
for j in range(0, 9):
if array[i][j] == 0:
flag = False
if flag:
return array
for i in range(0, 9):
for j in range(0, 9):
if array[i][j] == 0:
for num in range(1, 10):
if square_check(array, j, i, num):
if col_check(array, j, i, num):
if line_check(array, j, i, num):
new_array = array.copy()
new_array[i][j] = num
mark = False
f(new_array)
if mark:
break
def to_str(array):
for i in range(len(array)):
print(array[i])
print("")
to_str(f(sudoku))
The N Queen is the problem of placing N chess queens on an N×N chessboard so that no two queens attack each other. I have solved this program earlier, but am trying to rework my code to mirror that which I used to fashion a sudoku solver. I cannot seem to find the logical error but when I run the code, nothing prints. My program is attached below and if anyone could find my error that would be very helpful!
import numpy as np
def main():
global n
n = input("Enter N")
n = int(n)
global board
board = np.zeros((n,n), dtype=int)
solve_board()
def solve_board():
for row in range(n):
for col in range(n):
if board[row][col] == 0: #no queen
if (is_valid (board,row,col,n)):
board[row][col] = 1 #Assigning 1 for queen
solve_board()
board[row][col] = 0
return False
print('-'*n)
for row in board:
for col in row:
if col == 1:
print ("Q", end = " ")
else:
print (".", end = " ")
def is_valid(board,i,j,n):
if 1 in board[i]: #Checking row
return False
for row in range(0,i): #Checking column
if (board[row][j]==1):
return False
x,y = i,j
while (x>=0 and y>=0): #left diagonal
if (board[x][y]==1):
return False
x-=1
y-=1
x,y = i,j
while (x>=0 and y<n): #right diagonal
if (board[x][y]==1):
return False
x-=1
y+=1
return True
if __name__ == "__main__":
main()
This is how I had solved this code earlier, with solve_board being altered as followed.
def solve_board(row):
if(row == n):
print('-'*n)
for row in board:
for col in row:
if col == 1:
print ("Q", end = " ")
else:
print (".", end = " ")
print("")
else:
for col in range(n):
if (is_valid (board,row,col,n)):
board[row][col]=1
solve_board(row+1)
board[row][col] = 0
return False
Here is where the inspiration for my current code came from, a sudoku solver that I designed where I used 2 nested for loops; one for rows and one for columns. Based on this I had altered my solve_board(row) in my original n-queens code to the current function without a parameter. This sudoku code works perfectly.
def solve_board():
global board
for rowno in range(9):
#print ("row",rowno)
for colno in range(9):
#print("col",colno)
if board[rowno][colno] == 0:
for i in range(1,10):
#print(i)
if (is_valid(board,rowno,colno,i)):
board[rowno][colno]=i
solve_board()
board[rowno][colno]=0
return False
print (np.matrix(board))
I think the issue might lie in the fact that in the N-Queens problem the board does not fill up, i.e there are still 0s while for sudoku the entire board fills up and therefore when the ''if board[row][col] == 0'' is proven false exits the loop and prints. In the N-Queens problem since zero's are always present, it becomes an issue.
Try this
import numpy as np
def main():
global n
n = input("Enter N: ")
n = int(n)
global board
board = np.zeros((n,n), dtype=int)
solve_board()
def print_board():
print('-'*n)
for row in board:
for col in row:
if col == 1:
print ("Q", end = " ")
else:
print (".", end = " ")
print()
def is_valid(board,i,j,n):
if 1 in board[i]: #Checking row
return False
for row in range(0, n): #Checking column
if (board[row][j]==1):
return False
for k in range(0,n):
for l in range(0,n):
if (k+l==i+j) or (k-l==i-j):
if board[k][l]==1:
return False
return True
def solve_board():
for row in range(n):
for col in range(n):
if board[row][col] == 0: #no queen
if (is_valid (board,row,col,n)):
board[row][col] = 1 #Assigning 1 for queen
if np.count_nonzero(board) == n:
print_board()
return True
solve_board()
board[row][col] = 0
else:
return False
if __name__ == "__main__":
main()
I have a practice question that requires me to generate x number of alternating substrings, namely "#-" & "#--" using both recursion as well as iteration. Eg.string_iteration(3) generates "#-#--#-".
I have successfully implemented the solution for the iterative method,
but I'm having trouble getting started on the recursive method. How can I proceed?
Iterative method
def string_iteration(x):
odd_block = '#-'
even_block = '#--'
current_block = ''
if x == 0:
return ''
else:
for i in range(1,x+1):
if i % 2 != 0:
current_block += odd_block
elif i % 2 == 0:
current_block += even_block
i += 1
return current_block
For recursion, you almost always just need a base case and everything else. Here, your base case it pretty simple — when x < 1, you can return an empty string:
if x < 1:
return ''
After than you just need to return the block + the result of string_iteration(x-1). After than it's just a matter of deciding which block to choose. For example:
def string_iteration(x):
# base case
if x < 1:
return ''
blocks = ('#--', '#-')
# recursion
return string_iteration(x-1) + blocks[x % 2]
string_iteration(5)
# '#-#--#-#--#-'
This boils down to
string_iteration(1) + string_iteration(2) ... string_iteration(x)
The other answer doesn't give the same result as your iterative method. If you always want it to start with the odd block, you should add the block on the right of the recursive call instead of the left:
def string_recursion(x):
odd_block = '#-'
even_block = '#--'
if x == 0:
return ''
if x % 2 != 0:
return string_recursion(x - 1) + odd_block
elif x % 2 == 0:
return string_recursion(x - 1) + even_block
For recursive solution, you need a base case and calling the function again with some other value so that at the end you will have the desired output. Here, we can break this problem recursively like - string_recursive(x) = string_recursive(x-1) + string_recursive(x-2) + ... + string_recursive(1).
def string_recursion(x, parity):
final_str = ''
if x == 0:
return ''
if parity == -1: # when parity -1 we will add odd block
final_str += odd_block
elif parity == 1:
final_str += even_block
parity *= -1 # flip the parity every time
final_str += string_recursion(x-1, parity)
return final_str
odd_block = '#-'
even_block = '#--'
print(string_recursion(3, -1)) # for x=1 case we have odd parity, hence -1
# Output: #-#--#-