Recently I was tidying up and debugging my function that is a backtracking sudoku solver and realized that it wouldn't finish running for much larger sudoku boards:
My code:
def areLegalValues(values: list):
if len(values) in [1,4,9,16,25]:
for value in values:
if value <= len(values):
if value != 0 and values.count(value)>1:
return False
else:
return False
return True
return False
def isLegalRow(board: list, row: list):
compareList = [ ]
for r in range(len(board)):
if r == row:
for c in range(len(board[0])):
#print(board[r][c])
compareList.append(board[r][c])
return areLegalValues(compareList)
def isLegalCol(board: list, col: list):
compareList = [ ]
for r in range(len(board)):
for c in range(len(board[0])):
if c == col:
compareList.append(board[r][c])
#print(compareList)
return areLegalValues(compareList)
def isLegalBlock(board: list, block: list):
compareList = [ ]
N = int((len(board))**(1/2))
blockRowNumber = int(block//N)
blockColNumber = int(block%N)
for row in range(blockRowNumber*N, blockRowNumber*N+N):
#print(row)
for col in range(len(board[0])):
if col in range(blockColNumber*N, blockColNumber*N+N):
#print(board[row][col])
compareList.append(board[row][col])
# print(compareList)
return areLegalValues(compareList)
def isLegalSudoku(board: list):
boardLength = len(board)
for row in range(len(board)):
if isLegalRow(board,row) != True:
return False
for col in range(len(board[0])):
if isLegalCol(board,col) != True:
return False
for block in range(boardLength):
if isLegalBlock(board, block) != True:
return False
return True
def solveSudoku(board: list):
"""takes in a sudoku board and solves the board through use of backtracking
and returns the dectructively changed board"""
checkZeroes = True
for row in range(len(board)):
for col in range(len(board[0])):
if board[row][col] == 0:
checkZeroes = False
if checkZeroes == True:
return board
else:
for row in range(len(board)):
for col in range(len(board[0])):
if board[row][col] == 0:
for number in range(1,len(board)+1):
board[row][col] = number
if isLegalSudoku(board) == True:
solution = solveSudoku(board)
if solution != None:
return solution
board[row][col] = 0
return None
I was wondering how might I optimize/streamline this so that it runs faster and can handle larger input sizes without taking a extremely long time?
Thanks
As others mentioned in the comments, you may want to rethink the algorithm because backtracking is very slow.
That being said, there are technically some ways to slightly optimize this. For example, rather than trying every number for each zero, you could pre-calculate what numbers are missing (You except to have a certain number of each value in the grid based on the size of the grid) and keep a count of how many of each are missing. Then, once you've used up the allotted amount of a number, if would no longer attempt to place it in the grid.
For example, the grid
[
[0,2,3,4],
[0,4,1,0],
[2,3,4,1],
[4,1,0,3],
[
is missing 1 1
, 1 3
, and 2 2
s. You would not have to try to place any fours, and you would only have 3 choices to pick from for the first 1 or 2 numbers, and then decreasing afterwards. This would be a major improvement for few missing values, and a minor one for larger grids.
One other improvement you could make is in the legal board checking. Rather than checking the actual values of the rows, columns, and regions, you can simply sum them and check that they all equal the correct value (sum from 1 to the size of the board). For example, on a board size 9, all rows, columns and regions should sum to 45. On board size 4, they should sum to 10. The one downside to this method is that it doesn't distinguish if the board has any illegal moves vs. if it's simply missing an entry. Therefore this could only be used to check boards that have no remaining zeroes.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.