Trouble understanding this sudoku solver recursion function (python) - python

So basically I decided to make a sudoku solver in python and recursive functions is most efficient, but I've been trying to understand this code from a youtuber and don't understand why each space (bo[row][col]) doesn't immediately reset to 0. The resetting of one space to 0 only occurs if solve(bo) is True, but if I look through the code, the board will only return True if the board is completely solved, so why doesn't this function just lead nowhere since solve(bo) will never be True?
def solve(bo):
find = find_empty(bo)
if not find:
return True # This is the only time that solve(bo) == 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 # Yet here it resets the space to 0 if solve(bo) is False
return False
def valid(bo, num, pos):
for i in range(9):
if bo[pos[0]][i] == num and pos[1] != i:
return False
for i in range(9):
if bo[i][pos[1]] == num and pos[0] != i:
return False
box_x = (pos[1] // 3) * 3
box_y = (pos[0] // 3) * 3
for i in range(box_y, box_y + 3):
for j in range(box_x, box_x + 3):
if bo[i][j] == num and (i, j) != pos:
return False
return True
def find_empty(bo):
for y in range(9):
for x in range(9):
if bo[y][x] == 0:
return (y, x)
return False

There is some discrepancy between your explanation and comments in the code, but I'll do my best to explain the code anyway.
First solve() will try to find the first empty spot (i.e. one that is 0). If it can't find any, then the board is solved so it returns true.
If it does find a spot, then it will try to place numbers 1-9 there and check if it works with the previously entered numbers. Say 7 works (we don't know if 8 or 9 work as well because we haven't checked that yet). So we set the empty space to 7 and then pass this updated board in a recursive call. Essentially, this is like saying "if I force this spot to have the number 7, can you find a solution?"
If the recursive call returns true, it means there is a solution with 7 in that spot and hence the board has a solution, so we return true. If the recursive call to solve() returns false, then we know that there is no solution to the board with 7 in this spot, so we reset this spot to 0 and then try 8 (and then 9 if needed).
The thing to remember is that there is only one board (bo) in all the recursive calls - in other words, all the function calls are operating on the same variable bo. It doesn't create a copy of the board every time you make a recursive call. Lookup 'Pass by reference' and shallow vs. deep copies if you want to learn more about why.

Related

Leetcode jumpgame recursive approach

Given the following problem:
You are given an integer array nums. You are initially positioned at the array's first index, and each element in the array represents your maximum jump length at that position.
Return true if you can reach the last index, or false otherwise.
Example 1:
Input: nums = [2,3,1,1,4]
Output: True
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
Example 2:
Input: nums = [3,2,1,0,4]
Output: False
Explanation: You will always arrive at index 3 no matter what. Its maximum jump length is 0, which makes it impossible to reach the last index.
I am trying to come up with a recursive solution. This is what I have so far. I am not looking for the optimal solution. I am just trying to solve using recursion for now. If n[i] is 0 I want the loop to go back to the previous loop and continue recursing, but I can't figure out how to do it.
def jumpGame(self, n: []) -> bool:
if len(n) < 2:
return True
for i in range(len(n)):
for j in range(1, n[i]+1):
next = i + j
return self.jumpGame(n[next:])
return False
If you want to do recursively and you said no need to be optimal ( so not memoized ), you could go with the below method. You don't need nested loops.
Also no need to explore all paths, you could optimize by looking at the step that you are going by checking i + (jump) < n
def jumpGame(a, i):
if i > len(a) - 1:
return False
if i == len(a) - 1:
return True
reached = False
for j in range(1, a[i] + 1):
if i + j < len(a):
reached = jumpGame(a, i + j)
if reached:
return True
return reached
print(jumpGame([2, 3, 1, 1, 4], 0))
print(jumpGame([3,2,1,0,4], 0))
True
False
When considering recursive solutions, the first thing you should consider is the 'base case', followed by the 'recursive case'. The base case is just 'what is the smallest form of this problem for which I can determine an answer', and the recursive is 'can I get from some form n of this problem to some form n - 1'.
That's a bit pedantic, but lets apply it to your situation. What is the base case? That case is if you have a list of length 1. If you have a list of length 0, there is no last index and you can return false. That would simply be:
if len(ls) == 0:
return False
if len(ls) == 1:
return True
Since we don't care what is in the last index, only at arriving at the last index, we know these if statements handle our base case.
Now for the recursive step. Assuming you have a list of length n, we must consider how to reduce the size of the problem. This is by making a 'jump', and we know that we can make a jump equal to a length up to the value of the current index. Then we just need to test each of these lengths. If any of them return True, we succeed.
any(jump_game(n[jump:] for jump in range(1, n[0] + 1)))
There are two mechanisms we are using here to make this easy. any takes in a sequence and quits as soon as one value is True, returning True. If none of them are true, it will return False. The second is a list slice, n[jump:] which takes a slice of a list from the index jump to the end. This might result in an empty list.
Putting this together we get:
def jump_game(n: list) -> bool:
# Base cases
if len(n) == 0:
return False
if len(n) == 1:
return True
# Recursive case
return any(jump_game(n[jump:]) for jump in range(1, n[0] + 1))
The results:
>>> jump_game([2,3,1,1,4])
True
>>> jump_game([3,2,1,0,1])
False
>>> jump_game([])
False
>>> jump_game([1])
True
I'm trying to lay out the rigorous approach here, because I think it helps to clarify where recursion goes wrong. In your recursive case you do need to iterate through your options - but that is only one loop, not the two you have. In your solution, in each recursion, you're iterating (for i in range(len(n))) through the entire list. So, you're really hiding an iterative solution inside a recursive one. Further, your base case is wrong, because a list of length 0 is considered a valid solution - but in fact, only a list of length 1 should return a True result.
What you should focus on for recursion is, again, solving the smallest possible form(s) of the problem. Here, it is if the list is one or zero length long. Then, you need to step each other possible size of the problem (length of the list) to a base case. We know we can do that by examining the first element, and choosing to jump anywhere up to that value. This gives us our options. We try each in turn until a solution is found - or until the space is exhausted and we can confidently say there is no solution.

How to make this greedy function faster?

I am trying to solve a problem and my code fails at one test case where the list is of length 25000. Is there any way I can make this faster. I tried using functools.lru_cache and I still can not run within the time required to complete.
This is the problem from the site
Given an array of non-negative integers nums, you are initially
positioned at the first index of the array.
Each element in the array represents your maximum jump length at that
position.
Determine if you are able to reach the last index.
This is what I have tried
def can_jump(input_list):
#lru_cache
def helper(idx = 0):
if idx == len(input_list) - 1:
return True
return any(helper(new_idx) for x in range(input_list[idx]) \
if (new_idx := idx + x + 1) < len(input_list)) # increasing order of jumps
return helper()
Sample test cases work
input_list = [2,3,1,1,4]
print(can_jump(input_list)) # True
input_list = [3,2,1,0,4]
print(can_jump(input_list)) # False
I have also tried going from the other direction,
return any(helper(new_idx) for x in range(input_list[idx], 0, -1) \
if (new_idx := idx + x) < len(input_list)) # decreasing order of jumps
But I still can not make this run fast enough to clear the last test case of 25000 element list, what is it that I am doing wrong here?
Ok, I think I get it. Can you try this? Please note, this is taken straight from: https://codereview.stackexchange.com/questions/223612/jump-game-leetcode
def canjump(nums):
maximum_reachable_index = 0
for index, max_jump in enumerate(nums):
if index > maximum_reachable_index:
return False
maximum_reachable_index = max(index + max_jump, maximum_reachable_index)
return True

How does the following recursive Sudoku solving function work?

I recently watched this video showing a recursive function to solve sudoku and this seems irrational, since at the end we always change the value back to zero in the end.
How come the function worked in the video but doesnt work for me? Should the grid remain the same for both of us since at the end we use grid[y][x] = 0 which resets the grid discarding all changes made?
The idea of this code is to iterate over each number cell and each number (1-9) and check if it is possible to put the number in that cell. if we reach a dead end we backtrack.
Here's the code I manually copied:
import numpy as np
global grid
grid = [[5,3,0,0,7,0,0,0,0],
[6,0,0,1,9,5,0,0,0],
[0,9,8,0,0,0,0,6,0],
[8,0,0,0,6,0,0,0,3],
[4,0,0,8,0,3,0,0,1],
[7,0,0,0,2,0,0,0,6],
[0,6,0,0,0,0,2,8,0],
[0,0,0,4,1,9,0,0,5],
[0,0,0,0,8,0,0,7,9]]
def possible(y,x,n):
global grid
for i in range(0, 9):
if grid[y][i] == n:
return False
for i in range(0, 9):
if grid[i][x] == n:
return False
x0 = (x//3)*3
y0 = (y//3)*3
for i in range(0, 3):
for j in range(0, 3):
if grid[y0+i][x0+j] == n:
return False
return True
def solve():
global grid
for y in range(9):
for x in range(9):
if grid[y][x] == 0:
for n in range(1, 10):
if possible(y,x,n):
grid[y][x] = n
solve()
grid[y][x] = 0
return
print(np.matrix(grid))
print("")
solve()
print(np.matrix(grid))
The problem is that the solve function does indeed reset the grid back to its initial state after has finished executing, but it also solves the sudoku.
In the video, note that the grid is printed inside the solve function, not after it:
def solve():
global grid
for y in range(9):
for x in range(9):
if grid[y][x] == 0:
for n in range(1, 10):
if possible(y,x,n):
grid[y][x] = n
solve()
grid[y][x] = 0
return
print(np.matrix(grid))
print(np.matrix(grid))
print("")
solve()
This works because it loops through every cell and only recurses if the cell does not yet have a value filled in, and then after it has tried every value, it returns.
The only way it will complete the for y in range(9): loop is if it never finds a cell that is not 0, i.e. if the sudoku is solved, so by printing the matrix after the loop completes it will ensure that it prints the solved sudoku.
Your algorithm is working fine but you don't do anything with the grid when you find a solution. Once a solution is discovered, the backtracking zeroes out all of the cells it's filled in as it unwinds the call stack. By the time you get back to the main scope and print the result, it's back to its original state.
Your algorithm ends when you've iterated all of the rows and columns in a call to solve and find that there are no zeroes left to fill in. After your loops end, print (or better yet, return) the result and you'll see the completed puzzle.
Also, better to pass grid as a parameter to solve rather than use the global keyword. Sharing state like this makes it far too easy to introduce bugs.

Recursion function cooperating with another function python

I am trying to make a game solver. It should be able to solve Sudoku i.e.
find each possible number useable in each square.
This is what I have:
def next_possible_moves(board,row,col):
possible_moves = set(range(1,len(board)+1))
rows = set(board[row])
cols = []
for j in range(0,len(board)):
cols.append(board[j][col])
cols = set(cols)
possible_moves = possible_moves - rows.union(cols)
return list(possible_moves)
def solve(board):
for i in range(0,len(board)):
if len(board) == 8 and len(board[i]):
print "Found a solution:", board
return True
for next_move in next_possible_moves(board):
if is_valid(board, next_move):
if solve(board + [next_move]):
return True
return False
Both are working, but I am not able to get them to work together. I am using recursion to make it use one number, and while going through the code, it will backtrack if it fails.
What I want is for next_possible_moves to use only one parameter, so I can remove "row" and "col".
Using a list with X lists inside, so it represent an "X times X" board.

Why two while loops one after the other (not inside the other) in Python don't work?

I wrote the code below and I was expecting that, when the first loop ends and doesn't return False, the flow would follow to the second while loop. However, the flow skips the second while loop and simply returns True. Why is that? How can I fix this problem, making the flow after the first while loop go to the second while loop?
square = [[1,2,3,4],[4,3,1,4],[3,1,2,4],[2,4,4,3]]
# this is an auxiliary function
def getSum(lis):
sum = 0
for e in lis:
sum = sum + e
return sum
# here is where the problem is
def check_game(square):
standardSum = getSum(range(1, len(square)+1))
while square: #this is the first while loop
row = square.pop()
print row, 'row', 'sum of row=', getSum(row)
if standardSum != getSum(row):
return False
m = 0
while m < len(square): # the second while loop, which the flow skips
n = 0
col = []
while n < len(square):
col.append(square[n][m])
n = n + 1
print col, 'column'
if standardSum != getSum(col):
print standardSum, ' and sum of col =', getSum(col)
return False
m = m + 1
return True
The first loop only terminates when there are no more items left in square. After the first loop, len(square) will be 0, so the entry condition for the second loop m < len(square) will be False.
FYI your code is very (very very very) un-idiomatic Python -- it's written much more like C.
Here's a rewrite which is much more like Python is normally written.
square = [[1,2,3,4],[4,3,1,4],[3,1,2,4],[2,4,4,3]]
transpose = lambda i: zip(*i)
def is_magic(square):
n = len(square)
s = n*(n+1)/2
return all(sum(row) == s for row in square) and \
all(sum(col) == s for col in transpose(square))
You may wish to look into numpy, which is a Python module for handling matrices. With it:
def is_magic(square):
n = len(square)
s = n*(n+1)/2
return all(m.sum(0) == s) and all(m.sum(1) == s)
while square: will terminate when square is empty; it follows that len(square) == 0, and thus m < len(square) evaluates to false when m=0.
square.pop() returns a row from square and removes the row, therefore len(square) is zero in the second loop.
There is also a built-in function sum that does the same thing as your getSum function.
You know how many times you plan to iterate because you check a length and an increment variable. Use a for loop instead, as it will allow you to initialize the increment and adjust it each loop on the same line. This will avoid issues resulting in infinite loops in the future (even though thisn't the problem here, I consider it relevant to point out).
You can avoid your error by replacing your first while with this:
for row in square:
print row, 'row', 'sum of row=', getSum(row)
if standardSum != getSum(row):
return False

Categories