How can I decrease the time complexity and increase efficiency, without writing a new algorithm. My solution solves the majority of puzzles in a fast time, but for some difficult ones it can take over a minute! I have added some of the difficult puzzles below the code.
import numpy as np
from skimage.util import view_as_blocks # pip install scikit-image
#input: 9x9 numpy array, empty cell = 0
#output: 9x9 numpy array: if not solution all array entries should be -1
#backtracking depth-first search with constraint propagation
#searches for 0 value in board
def zero_search(sudoku):
for i in range(len(sudoku)):
for j in range(len(sudoku[0])):
if sudoku[i][j] == 0:
return (i, j) # row, col
return False
#parameters: board, num = inserted value, pos = board position(vector/tuple)
def valid(sudoku, num, pos):
# Check row
for i in range(len(sudoku[0])):
if sudoku[pos[0]][i] == num and pos[1] != i:
return False
# Check column
for i in range(len(sudoku)):
if sudoku[i][pos[1]] == num and pos[0] != i:
return False
# Check box
box_x = pos[1] // 3
box_y = pos[0] // 3
#y-axis/columns
for i in range(box_y*3, box_y*3 + 3):
#x-axis/rows
for j in range(box_x * 3, box_x*3 + 3):
if sudoku[i][j] == num and (i,j) != pos:
return False
return True
def solver(sudoku):
find = zero_search(sudoku)
if not find:
return True
else:
row, col = find
#check insertion values 1-9
for i in range(1,10):
if valid(sudoku, i, (row, col)):
sudoku[row][col] = i
if solver(sudoku):
return True
sudoku[row][col] = 0
return False
def initial_invalid(sudoku):
# Check row
for i in range(9):
dup_lst=[]
for j in range(9):
if sudoku[j][i]!=0:
if sudoku[j][i] in dup_lst:
return True
else:
dup_lst.append(sudoku[j][i])
#check column
for i in range(9):
dup_lst=[]
for j in range(9):
if sudoku[i][j]!=0:
if sudoku[i][j] in dup_lst:
return True
else:
dup_lst.append(sudoku[i][j])
#not needed
def final_valid (sudoku):
subgrids = view_as_blocks(sudoku, (3, 3))
sums = [np.sum(subgrids[i][j]) for j in range(3) for i in range(3)]
if sum(sums) !=405:
return False
else:
return True
#row_sum = sudoku.sum(axis=1)
#if sum(row_sum) != 405:
# return False
#col_sum = sudoku.sum(axis=0)
#if sum(col_sum) != 405:
# return False
def sudoku_solver(sudoku):
if initial_invalid(sudoku):
return np.full((9,9),-1)
if solver(sudoku):
return sudoku
else:
return np.full((9,9),-1)
[[0 8 0 4 3 0 0 0 0]
[0 0 5 0 0 9 0 0 0]
[6 0 0 0 8 0 0 7 0]
[0 0 0 0 9 0 0 0 3]
[0 0 0 8 0 7 0 0 0]
[9 0 0 0 0 0 0 5 4]
[0 6 0 0 0 0 0 0 5]
[0 0 8 0 0 0 4 0 0]
[0 4 0 0 0 6 0 1 0]]
[[0 0 2 0 0 0 0 0 4]
[0 5 0 0 1 3 7 0 0]
[7 9 0 0 0 0 0 5 0]
[0 0 9 0 0 0 0 6 0]
[0 0 0 0 3 0 5 0 8]
[5 0 7 0 0 0 4 0 0]
[0 0 0 0 6 0 8 0 0]
[0 6 0 0 2 7 0 4 0]
[8 0 0 0 0 0 0 2 0]]
[[0 0 0 6 0 0 2 0 0]
[0 0 0 0 0 9 0 6 0]
[0 8 0 0 0 5 0 0 3]
[1 0 0 4 0 0 9 0 0]
[8 3 0 0 0 0 0 0 0]
[0 2 0 0 0 6 0 0 0]
[0 0 0 0 6 0 0 0 0]
[2 5 0 3 0 7 0 9 0]
[0 0 1 0 0 0 0 8 4]]