简体   繁体   中英

How to optimize function run speed involving backtracking for a sudoku solver

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM