How to stop yielding sudoku boards, once the puzzle is solved? - python

This is my recursive sudoku solver. Pretty self-explanatory:
def is_valid(board, num, row, col):
# check row
for i in range(9):
if board[i][col] == num:
return False
# check column
for i in range(9):
if board[row][i] == num:
return False
# check square
square_w = (row // 3) * 3
square_h = (col // 3) * 3
for sRow in range(square_w, square_w + 3):
for sCol in range(square_h, square_h + 3):
if board[sRow][sCol] == num:
return False
return True
def solve(board):
for row in range(9):
for col in range(9):
if board[row][col] == 0:
for num in range(1, 10):
if is_valid(board, num, row, col):
board[row][col] = num
yield board
yield from solve(board)
board[row][col] = 0
yield board
return
yield board
And this is the board I am running the solver on:
bo = [
[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]
]
The problem is that when I call solve and iterate the yielded boards, the boards continue to generate even after getting the result (at the bottom most yield board in the solve function).
How can I make sure that the last value in the generator is the solved board?

There are some indentation problems in your code. At the time of writing both the return and yield from statements should be indented one level more.
To stop the generation of more boards once you have it solved, you should check that the board is solved. The code does not do this anywhere.
As you correctly indicate, the board is solved when you reach the last statement in the solve function, and the function returns there. You could use this moment to mark the board so to make it easier for the caller to also know that the board was solved. As you already mutate the same board throughout the algorithm (instead of yielding copies), I suppose you could just mutate the board once more in order to indicate it was solved. For instance, you could store a 0 in the top-left corner. This can be checked by the caller, who can then immediately return:
def solve(board):
for row in range(9):
for col in range(9):
if board[row][col] == 0:
for num in range(1, 10):
if is_valid(board, num, row, col):
board[row][col] = num
yield board
yield from solve( board) # corrected wrong indentation
if board[0][0] == 0: # was the board already solved?
return # then get out of here!
board[row][col] = 0 # better placed at this indentation
yield board # better placed at this indentation
return # corrected wrong indentation
# the board is the solution: no need to yield it as it was already yielded
board[0][0] = 0 # mark that no more boards should be yielded

Related

How to store matrix list data in recursive python function?

I am trying to solve the N_Queen problem with recursive function. My codes are shown below. The problemm is that I cannot store the acceptable solution in a list. I can see that all the right answers appeared while debuging, but those answers cannot store in a list outside the recursive function. I hope the acceptable solution could be stored in a list and keep on recursion for the next right answer. But the appended solution are always changing.
By importing copy and deepcopy, I have solved the problem, yet it is still confusing. It seems that others can store pure list result without deepcopy, but my matrix list is unacceptable.
THX.
def NQueens(self, n: int) -> int:
res = []
board = [['.' for _ in range(n)] for _ in range(n)]
def backtrack(board,row):
if row == n:
res.append(board) # **this is the confusing part**
return
for col in range(n):
if not isvalid(board,row,col):
continue
board[row][col] = 'Q'
backtrack(board,row+1)
board[row][col] = '.'
def isvalid(board,row,col):
for i in range(row):
if board[i][col] == 'Q':
return False
for i in range(1,row+1):
if col - i >= 0 and board[row - i][col - i] == 'Q':
return False
for i in range(1,row+1):
if col + i < n and board[row - i][col + i] == 'Q':
return False
return True
backtrack(board,0)
return res
The problem is not that you can't save it, the problem is that after saving it you change the values of the board and they are linked so they both change so, when you finally return res, you end up returning the last state of board x times over, x beeing the times you appended it
I currently don't know hot to solve this but when i find out i'll edit or comment on this
i finally found the problem, sorry for the wait.
In the line board[row][col] = '.' there were no conditions for that, what i mean is that it will alwas happen so when you put a queen after backtracking you'll remove it without exceptions.
I changed it so it will make a return true statement if achived to place all the queens so when backtracking it will not delete them if it found a solution.
I'll leave the working code, commenting what i changed, here:
def NQueens(n: int) -> int:
board = [['.' for _ in range(n)] for _ in range(n)]
def backtrack(board,row):
#If all queens are placed then return true
if row >= n:
return True
for col in range(n):
#if it is valid place the queen and try to place the next queen on the next row
if isvalid(board,row,col):
board[row][col] = 'Q'
if backtrack(board,row+1) == True:
return True
#if placing the queen was not the way to the solution remove the queen
board[row][col] = '.'
#if the queen can't be placed in any column in this row return false
return False
def isvalid(board,row,col):
for i in range(row):
if board[i][col] == 'Q':
return False
for i in range(1,row+1):
if col - i >= 0 and board[row - i][col - i] == 'Q':
return False
for i in range(1,row+1):
if col + i < n and board[row - i][col + i] == 'Q':
return False
return True
backtrack(board,0)
return board #directly return board, res was innecesary

Python Sudoku: How can I implement a for loop subgrid?

I am trying to implement a function that gets a nested list of Sudoku and returns True if valid and False otherwise. I implemented row checking and column checking, but subgrid checking is not working.
GRID_SIZE = 9
SUBGRID_SIZE = 3
def check_grid_if_valid(grid):
gridlist = []
for i in range(GRID_SIZE):
# column
for j in range(GRID_SIZE):
if grid[j][i] in gridlist and grid[j][i] != None:
return False
gridlist.append(grid[j][i])
gridlist.clear()
# row
for j in range(GRID_SIZE):
if grid[i][j] in gridlist and grid[i][j] != None:
return False
gridlist.append(grid[i][j])
gridlist.clear()
# it is working now
# subgrid
for row in range(0, GRID_SIZE, SUBGRID_SIZE):
for column in range(0, GRID_SIZE, SUBGRID_SIZE):
for i in range(row, row + SUBGRID_SIZE):
for j in range(column, column + SUBGRID_SIZE):
if grid[i][j] in gridlist and grid [i][j] != None:
return False
gridlist.append(grid[i][j])
gridlist.clear()
return True
Thanks to #OneCricketeer for kind help!
Try the following
for subgrid_col in range(GRID_SIZE, step=SUBGRID_SIZE):
for subgrid_row in range(GRID_SIZE, step=SUBGRID_SIZE):
print(f"Checking subgrid ({subgrid_col}, {subgrid_row})")
for i in range(SUBGRID_SIZE):
for j in range(SUBGRID_SIZE):
num = grid[subgrid_col+i][subgrid_row+j]
Also, I'd suggest adding numbers simply into a set rather than a list, then checking the length of the set is 10 after adding all the numbers rather than checking containment while adding the numbers. You also shouldn't need to check in range(1,10) if you are limiting user-input elsewhere; this check would only be necessary if you're reading grids from external sources that don't limit your input

Backtracking 8 Queens Python problems

I've started solving the 8 queens problem with backtracking in Python. Everything is nice & fine. It even printed out the first answer. However, it stuck itself on its first backtracking try.
The task sounded in that way:
Implement a Python function that solves the 8 queens puzzle. The 8 queen puzzle consists of placing 8 queens on a chess board, so that, none of the queens could capture any other. Note that queens can move orthogonally or diagonally in any direction.
You should implement a function solve() that when called, it prints the first solution of the puzzle and then it awaits for input. Once the user presses ‘enter’, the next solution is printed, and so on.
- Your program should be able to find all the solutions for the puzzle and each solution only once. '
- It should be easy to modify your program, so that, it works for different board sizes. Hints:
- In any row, there is exactly one queen. Hence, all you need to compute is the column in which each of the 8 queens can be placed.
- You should implement a recursive function solve(n) that finds a place for nth+1 the queen and then calls itself recursively for the n+1 queen (unless all the queens have been placed). It should systematically explore all the possibilities using backtracking.
- You are allowed (and encouraged) to define extra functions (other than solve() ) to improve the quality of your code if necessary.
import numpy as np
grid = np.zeros((8, 8), dtype = int)
def possible(y, n):
global solved
global grid
for i in range(0, 8):
if grid[y][i] == n:
return False
try:
for item in solved[str(y)]:
if grid[y].all() == item.all():
return False
except KeyError:
return True
return True
max_y = 7
max_x = 7
def print_grid():
global grid
for line in grid:
for square in line:
if square == 0:
print(".", end = " ")
else :
print("Q", end = " ")
print()
solved = {}
def prefilled_solved():
global solved
for i in range(0, len(grid[0])):
solved[f"{str(i)}"] = []
def solve(y=0):
global grid
global solved
while y < 8:
for x in range(0, 8):
if grid[y][x] == 0:
if possible(x, 1):
grid[y][x] = 1
solved[f"{str(y)}"].append(grid[y])
y += 1
solve(y)
#y -= 1 or y = 0 or y -=2
# backtracking - bad choice
# grid[y][x] = 0
print_grid()
print(grid)
return
input("More?")
if __name__ == '__main__':
prefilled_solved()
solve()
I've followed the #mkam advice, Now I've got the random constellation of Queen but I've got rid of recursion altogether.
```import numpy as np
grid = np.zeros((8, 8), dtype = int)
from random import randint, shuffle, choice
from itertools import permutations
constellations_drawn = []
def print_grid():
global grid
for line in grid:
for square in line:
if square == 0:
print(".", end = " ")
else :
print("Q", end = " ")
print()
solved = []
def prefilled_solved():
global solved
new_board = ['1', '2', '3', '4', '5', '6', '7', '8']
new_board_i = ''.join(new_board)
solved = permutations(new_board_i, 8)
def solve(y=0):
global grid
global solved
global constellations_drawn
list_solved = list(solved)
len_solved = len(list_solved)
board_drawn = list_solved[randint(0, len_solved-1)]
board_drawn_str = ''.join(board_drawn)
while board_drawn_str in constellations_drawn:
board_drawn = list_solved[randint(0, len_solved - 1)]
new_board_list = [int(item) for item in board_drawn]
for i, x in enumerate(new_board_list):
if grid[i-1][x-1] == 0:
grid[i-1][x-1] = 1
#y += 1
#solve(y)
#y -= 1 or y = 0 or y -=2
# backtracking - bad choice
# grid[y][x] = 0
constellations_drawn.append(board_drawn_str)
print_grid()
print(grid)
return
input("More?")
if __name__ == '__main__':
prefilled_solved()
solve()
I've merged the code of #mkam and mine. And it works. I still use numpy ndarray.
import numpy as np
from numpy.core._multiarray_umath import ndarray
def print_grid(solutions_found, board) -> None:
line: ndarray
len_board = len(board)
grid: ndarray = np.zeros((len_board, len_board), dtype=int)
for i, number in enumerate(board):
grid[i - 1][number - 1] = 1
for line in grid:
for square in line:
if square == 0:
print(".", end=" ")
else:
print("Q", end=" ")
print()
print(f'Solution - {solutions_found}')
def solve(boardsize, board=[], solutions_found=0):
if len(board) == boardsize:
solutions_found += 1
print_grid(solutions_found, board)
else:
for q in [col for col in range(1, boardsize + 1) if col not in board]:
if is_safe(q, board):
solutions_found = solve(boardsize, board + [q], solutions_found)
return solutions_found
def is_safe(q, board, x=1):
if not board:
return True
if board[-1] in [q + x, q - x]:
return False
return is_safe(q, board[:-1], x + 1)
if __name__ == '__main__':
solve(8)
This is an example of how the 8-Queens problem can be solved recursively, using a simple list to represent the board. A list such as [8, 4, 1, 3, 6, 2, 7, 5] represents the 8 rows of a chessboard from top to bottom, with a Q in the 8th column of the top row, the 4th column of the 7th row, the 1st column of the 6th row ... and the 5th column of the bottom row.
A solution is built starting with an empty board [] by placing a Q in the next row in a column position where it cannot be taken. Possible positions are columns which have not already been taken earlier (this is the for loop in function solve). For each of these possible column positions, function issafe checks whether the position is safe from being taken diagonally by the Qs already on the board. If the position is safe, the solution board is extended by another row and the solution recurses until the board is filled (len(board) == boardsize), at which point the solution count is incremented and the board is displayed.
Note that the function solve works for any size of square chessboard - the desired size is passed as a parameter to solve, and the function returns the total number of solutions found.
Hope this helps explain how the 8-Queens problem can be solved recursively WITHOUT numpy.
def display(solution_number, board):
row = '| ' * len(board) + '|'
hr = '+---' * len(board) + '+'
for col in board:
print(hr)
print(row[:col*4-3],'Q',row[col*4:])
print(f'{hr}\n{board}\nSolution - {solution_number}\n')
def issafe(q, board, x=1):
if not board: return True
if board[-1] in [q+x,q-x]: return False
return issafe(q, board[:-1], x+1)
def solve(boardsize, board=[], solutions_found=0):
if len(board) == boardsize:
solutions_found += 1
display(solutions_found, board)
else:
for q in [col for col in range(1,boardsize+1) if col not in board]:
if issafe(q,board):
solutions_found = solve(boardsize, board + [q], solutions_found)
return solutions_found
if __name__ == '__main__':
solutions = solve(8)
print(f'{solutions} solutions found')
You mention using yield - this is also possible, and will transform solve into a generator, producing one solution at a time. Your program can then use a for loop to receive each solution in turn and process it as required. The following yield solution works with Python v.3.3 onwards because it uses yield from:
def display(solution_number, board):
row = '| ' * len(board) + '|'
hr = '+---' * len(board) + '+'
for col in board:
print(hr)
print(row[:col*4-3],'Q',row[col*4:])
print(f'{hr}\n{board}\nSolution - {solution_number}\n')
def issafe(q, board, x=1):
if not board: return True
if board[-1] in [q+x,q-x]: return False
return issafe(q, board[:-1], x+1)
def solve(boardsize, board=[]):
if len(board) == boardsize:
yield board
else:
for q in [col for col in range(1,boardsize+1) if col not in board]:
if issafe(q,board):
yield from solve(boardsize, board + [q])
if __name__ == '__main__':
for solutionnumber, solution in enumerate(solve(8)):
display(solutionnumber+1, solution)
If the recursive function issafe appears confusing, here is a non-recursive version:
def issafe(q, board):
x = len(board)
for col in board:
if col in [q+x,q-x]: return False
x -= 1
return True

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 2s. 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.

Sudoku Checker in Python

I am trying to create a sudoku checker in python:
ill_formed = [[5,3,4,6,7,8,9,1,2],
[6,7,2,1,9,5,3,4,8],
[1,9,8,3,4,2,5,6,7],
[8,5,9,7,6,1,4,2,3],
[4,2,6,8,5,3,7,9], # <---
[7,1,3,9,2,4,8,5,6],
[9,6,1,5,3,7,2,8,4],
[2,8,7,4,1,9,6,3,5],
[3,4,5,2,8,6,1,7,9]]
easy = [[2,9,0,0,0,0,0,7,0],
[3,0,6,0,0,8,4,0,0],
[8,0,0,0,4,0,0,0,2],
[0,2,0,0,3,1,0,0,7],
[0,0,0,0,8,0,0,0,0],
[1,0,0,9,5,0,0,6,0],
[7,0,0,0,9,0,0,0,1],
[0,0,1,2,0,0,3,0,6],
[0,3,0,0,0,0,0,5,9]]
I am expecting input like that- a list of 9 lists. The zeros represent number that have not been filled in by the user. They can appear multiple times in a row, column or 3x3.
def check_sudoku(grid):
if len(grid) == 9:
numsinrow = 0
for i in range(9):
if len(grid[i]) == 9:
numsinrow += 1
if numsinrow == 9:
for i in range(9):
rowoccurence = [0,0,0,0,0,0,0,0,0,0]
for j in range(9):
rowoccurence[grid[i][j]] += 1
temprow = rowoccurence[1:10]
if temprow == [1,1,1,1,1,1,1,1,1]:
return True
else:
return False
else:
return False
else:
return False
I obviously need to check that there is a 9x9 list of lists (grid), and that there are no duplicates in each row, column and 3x3 small square. In the code, I first check to see if there are a proper number of rows (There should be 9). Then I check that each row has 9 elements in it (with the ill_formed example you see that this is not the case). I then attempt to check duplicates in each row but I am having some trouble doing so. I thought that I could loop over each row and loop over each element in that row, and add 1 to a list of ints (rowoccurence). For example, if the first number is a 2, then rowoccurence[2] should be equal to 1. The zeros are in rowoccurence[0] and are not checked(I have a temporary list which should take everything except that first element- the zeros- because there could be more than 1 zero in a row and the grid could still be legit). I try to check the temp list (basically rowoccurence) against a reference list of correct values but it does not seem to be working. Could you help me check the rows for duplicates in this sudoku checker? Thank you so much in advance!
Remember, you're not searching for duplicates -- merely nonzero duplicates. Summing a set works for this. You can also check the legality of the row/column at the same time:
def sudoku_ok(line):
return (len(line) == 9 and sum(line) == sum(set(line)))
def check_sudoku(grid):
bad_rows = [row for row in grid if not sudoku_ok(row)]
grid = list(zip(*grid))
bad_cols = [col for col in grid if not sudoku_ok(col)]
squares = []
for i in range(9, step=3):
for j in range(9, step=3):
square = list(itertools.chain(row[j:j+3] for row in grid[i:i+3]))
squares.append(square)
bad_squares = [square for square in squares if not sudoku_ok(square)]
return not (bad_rows or bad_cols or bad_squares)
You return True too early, so you never make it to the test you hope to see fail:
if temprow == [1,1,1,1,1,1,1,1,1]:
return True # <-- this is the culprit
else:
return False
Misc other notes: one easy way to make sure that all elements of some vector are equal to some constant is:
all(i == const for i in vector)
Another, even easier: if vec[1:10] are all 1, then sum(vec[1:10]) must be 9. (bad idea, see comment below.)
I am only posting this because most other solutions are hardly readable though they might be really efficient. For someone who is new and just trying to learn I believe that the code below is helpful and very readable. Hope this helps anyone looking to learn how to create a sudoku checker.
def check_sudoku(grid):
for row in range(len(grid)):
for col in range(len(grid)):
# check value is an int
if grid[row][col] < 1 or type(grid[row][col]) is not type(1):
return False
# check value is within 1 through n.
# for example a 2x2 grid should not have the value 8 in it
elif grid[row][col] > len(grid):
return False
# check the rows
for row in grid:
if sorted(list(set(row))) != sorted(row):
return False
# check the cols
cols = []
for col in range(len(grid)):
for row in grid:
cols += [row[col]]
# set will get unique values, its converted to list so you can compare
# it's sorted so the comparison is done correctly.
if sorted(list(set(cols))) != sorted(cols):
return False
cols = []
# if you get past all the false checks return True
return True
Took reference from #llb 's answer, which did not check for missing values or zeros, here's my solution which would work for negative values, zeros and missing values
def line_ok(e):
if len(set(e)) != 9: return False
for i in range(len(e)):
if e[i] not in range(1,10): return False
return True
def checker(grid):
bad_rows = [False for row in grid if not line_ok(row)]
grid = list(zip(*grid))
bad_cols = [False for col in grid if not line_ok(col)]
squares = []
for i in range(0,9,3):
for j in range(0,9,3):
square = list(itertools.chain.from_iterable(row[j:j+3] for row in grid[i:i+3]))
squares.append(square)
bad_squares = [False for sq in squares if not line_ok(sq)]
return not any([bad_rows, bad_cols, bad_squares])
print(checker(sudoku_correct))
PS: Due to less reps, couldn't comment. Hope whoever needs it, finds it :)
Define a function to verify that there are no duplicates, then you can use it to check rows, columns, and 3x3 grids. You can reduce the nested blocks by returning early if some condition is not met, for example, number of rows are larger than 9. And only return true at the very end of the function if none of the checks fail.
from collections import Counter
def check_dups(l):
counts = Counter()
for cell in l:
if cell != 0: counts[cell] += 1
if cell > 9 or counts[cell] > 1: return False
return True
def check_sudoku(grid):
if len(grid) != 9: return False
if sum(len(row) == 9 for row in grid) != 9: return False
for row in grid:
if not check_dups(row): return False
return True
I think the reason your code collapse is because your indent. You should do:
for j in range(9):
rowoccurence[grid[i][j]] += 1
temprow = rowoccurence[1:10]
if temprow == [1,1,1,1,1,1,1,1,1]:
return True
else:
return False
Rather than:
for j in range(9):
rowoccurence[grid[i][j]] += 1
temprow = rowoccurence[1:10]
if temprow == [1,1,1,1,1,1,1,1,1]:
return True
else:
return False
Or use Counter:
from collections import Counter
...
if numsinrow == 9:
for i in range(9):
count = Counter(grid[i])
return False if max(count.values()) > 1 else True
valid_solution= lambda board: not any([sorted(row)!=list(range(1,10)) for row in board]) and not any([sorted(list(col))!=list(range(1,10)) for col in zip(*board)]) and not any([sorted(board[i][j:j+3]+board[i+1][j:j+3]+board[i+2][j:j+3]) !=list(range(1,10)) for i in range(0,9,3) for j in range(0,9,3)])
import numpy as np
def is_valid(row):
# checks whether a given set of values forms a valid row in sudoku
return len(list(filter(lambda val: type(val) == int and 0 < val < 10, set(row))) == 9
def check_sudoku(grid):
""" Check a sudoku board is correctly completed or not. """
# checks whether the grid has 9 rows
if len(grid) != 9:
return False
# checks whether the grid has 9 columns
for i in range(9):
if len(grid[i]) != 9:
return False
# turns grid from list to numpy array
grid = np.array(grid)
# checks whether the grid is filled with integers
if grid.dtype != np.int:
return False
for i in range(9):
# checks for repetition in rows
if not is_valid(grid[i, :]):
return False
# checks for repetition in columns
if not is_valid(grid[:, i]):
return False
# checks for repetition in squares
if not is_valid(grid[i//3*3:i//3*3+3, j%3*3:j%3*3+3]):
return False
# returns true if none of the conditions reached
return True
How about just checking each row/column with:
sorted(row) == range(1,10)
or for python 3
sorted(row) == list(range(1,10))
I imagine the running time is dominated by creating a new list (whether you do the histogram approach or the sorting approach) so the extra factor of log(n) shouldn't be noticeable
In order to check each row,column, and subsquare, I suggest having extractor methods that get the nth row, column, and subsquare from your matrix (ie don't try and put it all in one method).
So for instance:
getSubSquare(m, i):
subrow = (i // 3) * 3
subcol = (i % 3) * 3
v = [0] * 9
for j in range(9):
subrj = j // 3
subcj = j % 3
v[j] = m[subrow + subrj][subcol + subcj]
return v
getRow(m,i):
return m[i]
getCol(m,i):
return [m[j][i] for j in range(9)]
def check_sudoku(grid):
if len(grid) == 9:
numsinrow = 0
for i in range(9):
if len(grid[i]) == 9:
numsinrow += 1
if numsinrow == 9:
if checkrow(grid):
if checkcol(grid):
return True
else:
return False
else:
return False
else:
return False
else:
return False
def checkrow(grid):
for i in range(9):
rowoccurence = [0,0,0,0,0,0,0,0,0,0]
for j in range(9):
rowoccurence[grid[i][j]] += 1
temprow = rowoccurence[1:10]
for q in range(9):
if temprow[q] == 1 or temprow[q] == 0:
continue
else:
return False
return True
def checkcol(grid):
for num in range(9):
coloccurence = [0,0,0,0,0,0,0,0,0,0]
for i in range(9):
coloccurence[grid[i][num]] += 1
tempcol = coloccurence[1:10]
for q in range(9):
if tempcol[q] == 1 or tempcol[q] == 0:
continue
else:
return False
return True
Ok guys I am back with a function to check the rows which works. Thank you so much for all the extensive help. I am sort of a noob at this so I did not understand some answers but I realized that I was returning true way too early. I also realized that if there were multiple zeros in a row, then some numbers would not come up in the rowoccurence/temp list. This is why I had to check for both 1's and 0's in the rowoccurence/temp list. I have also written a similar function to check the columns. Thanks again!
If you want to check a row for duplicates, instead of
rowoccurence = [0,0,0,0,0,0,0,0,0,0]
for j in range(9):
rowoccurence[grid[i][j]] += 1
temprow = rowoccurence[1:10]
if temprow == [1,1,1,1,1,1,1,1,1]:
return True
else:
return False
count:
b = True
for i in range(9):
grid[i].count(grid[i][j]) > 1:
b = False
return b
Your approach does a bit more than just duplicate checking, it also takes care that only one digit number are present otherwise an out of bound exception will be raised
Wrote up a simple class to model a (completed) Sudoku board. Nothing tricky but a simple solution for a 9x9 board.
class SudokuBoard(object):
DIGITS = set(range(1, 10))
def __init__(self, values):
self.values = values
def row(self, n):
return self.values[n]
def rows(self):
return self.values
def cols(self):
return [self.col(i) for i in xrange(9)]
def col(self, n):
return [self.values[i][n] for i in xrange(len(self.values))]
def groups(self):
return [self.group(i) for i in xrange(9)]
def group(self, n):
start_r = (n / 3) * 3
start_c = n * 3 % 9
values = []
for row in xrange(start_r, start_r + 3):
for col in xrange(start_c, start_c + 3):
values.append(self.values[row][col])
return values
def is_correct(self):
for row in self.rows():
if self.DIGITS - set(row):
return False
for col in self.cols():
if self.DIGITS - set(col):
return False
for group in self.groups():
if self.DIGITS - set(group):
return False
return True
I wrote the code on https://github.com/loghmanb/daily-coding-problem
from collections import defaultdict
def solution_valid(board)
#check row and column is distict no or not?!
rows = defaultdict(int)
columns = defaultdict(int)
squares = defaultdict(int)
for i in range(9):
rows.clear()
columns.clear()
squares.clear()
for j in range(9):
if board[i][j] is not None:
columns[board[i][j]] += 1
if columns[board[i][j]]>1:
return False
if board[j][i] is not None:
rows[board[j][i]] += 1
if rows[board[j][i]]>1:
return False
new_j = (i*3 + j%3)%9
new_i = (i//3)*3 + j//3
if squares[board[new_i][new_j]] is not None:
squares[board[new_i][new_j]] += 1
if squares[board[new_i][new_j]]>1:
return False
return True
The question is old, but I leave a new contribution for others who come here, like me.
Correct me if I'm wrong, but I think the solution is quite simple. No need to check for duplicates in row, column and grid. Just check the row. Because if there are duplicates in the column or in the grid, there will also be duplicates in the row.
So I think it's enough to check for 0 and duplicates on the row:
from collections import Counter
solved = True
for row in board:
if max(Counter(row).values()) > 1: solved = False
elif 0 in row: solved = False

Categories

Resources