I have been trying to implement Sudoku in Python, but the backtracking is not working at all. When I input a 4x4 grid of 0's, I get output, but most of the time it fails to provide the result for a 3x3. This test case progresses correctly until it reaches the last element of the second row.
import math
solution=[[3,0,6,5,0,8,4,0,0],
[5,2,0,0,0,0,0,0,0],
[0,8,7,0,0,0,0,3,1],
[0,0,3,0,1,0,0,8,0],
[9,0,0,8,6,3,0,0,5],
[0,5,0,0,9,0,6,0,0],
[1,3,0,0,0,0,2,5,0],
[0,0,0,0,0,0,0,7,4],
[0,0,5,2,0,6,3,0,0]]
#solution=[[0 for x in range(4)] for y in range(4)]
N=9
row=0
col=0
def positionFound():
global row,col
for x in range(N):
for y in range(N):
if solution[x][y] is 0:
row,col=x,y
return row,col
return False
def isSafe(row,col,num):
global N
for c in range(N):
if solution[row][c] is num:
return False
for r in range(N):
if solution[r][col] is num:
return False
r=row-row%int(math.sqrt(N))
c=col-col%int(math.sqrt(N))
for x in range(r,r+int(math.sqrt(N))):
for y in range(c,c+int(math.sqrt(N))):
if solution[x][y] is num:
return False
return True
back=1
def sudoku(solution):
global row,col
if positionFound() is False:
print('SUCCESS')
for x in solution:
print(x)
return True
for number in range(1,N+1):
if isSafe(row,col,number):
solution[row][col]=number
if sudoku(solution) is True:
return True
solution[row][col]=0
return False
sudoku(solution)
for x in solution:
print(x)
OUTPUT:
[3, 1, 6, 5, 2, 8, 4, 9, 7]
[5, 2, 4, 1, 3, 7, 8, 6, 0]
[0, 8, 7, 0, 0, 0, 0, 3, 1]
[0, 0, 3, 0, 1, 0, 0, 8, 0]
[9, 0, 0, 8, 6, 3, 0, 0, 5]
[0, 5, 0, 0, 9, 0, 6, 0, 0]
[1, 3, 0, 0, 0, 0, 2, 5, 0]
[0, 0, 0, 0, 0, 0, 0, 7, 4]
[0, 0, 5, 2, 0, 6, 3, 0, 0]
The reason your backtracking isn't working is that you haven't implemented backtracking. Once you fail to place a number in a given location, you have no provision to return your [row, col] cursor to the previous position. You need to involve a way to know what the previous filled position was and resume with the next legal number for that position. Your recursion holds previous board positions in the stack, but you've lost the cursor position -- and your re-try loop assumes that it gets reset.
One strong possibility is to make row and col local variables, keeping them coordinated with the solution grid they describe. Make them part of the parameter passing, so the stack maintains those values for you.
Related
This is a sudoku solver, in the second function when I try to return the solved board it appears as none, but if you print the board you can see the solution. here is the code:
board = [[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 solution(row, col, number):
for i in range(9):
if board[row][i] == number:
return False
if board[i][col] == number:
return False
row0 = (row // 3) *3
col0 = (col // 3) * 3
for i in range(3):
for j in range(3):
if board[row0+i][col0+j] == number:
return False
return True
def solve():
global board
for row in range(9):
for col in range(9):
if board[row][col] == 0:
for number in range(1,10):
if solution(row, col, number):
board[row][col] = number
solve()
board[row][col] = 0
return
[print(x) for x in board]
return board
print(solve())
How can I make the solve function return the new board?
The issue with your code is that you have two exiting points, one that returns the board:
return board
and one that returns nothing (i.e. None)
return
If you follow how the recursion calls are invoked, you realize that the parent call is actually returning from the None exit point.
However, just passing board to that exit point does not fix the issue as you have recursive calls to solve() which are followed by setting back to 0 specific cells.
So essentially, you first solve the sudoku and then unwind the whole solution back to where you started.
To solve this you need to keep track of when you actually solve the sudoku and skip the setting back to 0 if the sudoku is solved.
One way of doing this (slightly rewritten to avoid the perilous use of global) is:
board = [[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 solution(board, row, col, number):
for i in range(9):
if board[row][i] == number:
return False
if board[i][col] == number:
return False
row0 = (row // 3) *3
col0 = (col // 3) * 3
for i in range(3):
for j in range(3):
if board[row0+i][col0+j] == number:
return False
return True
def solve(board):
for row in range(9):
for col in range(9):
if board[row][col] == 0:
is_solved = False
for number in range(1, 10):
if solution(board, row, col, number):
board[row][col] = number
is_solved = solve(board)
if not is_solved:
board[row][col] = 0
return is_solved
return True
def print_board(board):
for row in board:
print(row)
print()
print_board(board)
# [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]
solve(board)
print_board(board)
# [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, 1]
# [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]
Where essentially the solve() function is changed so that:
it accepts a board parameter which is modified in-place
it returns if the board was solved or not
if the board is already solved it does not unwind the solution
(a print_board() helper function is included for simpler visualization)
(also the solution() has been changed to include a board parameter, to avoid relying on the global board definition, and the final block is outside of the main loop because it is looped there pointlessly).
The function does not need to return board because it gets modified in-place.
If one really needs the board to be returned, one can simply include it in the return.
Note that if you just replace the lone return inside the loops with a break, this does not seem to work.
If it does work, it will take a huge amount of time.
I suspect this will eventually lead to infinite looping because you just find some partial solution and unroll it back and forth forever (this assumption is based on replacing the return in my code with a break which yields a partial solution).
Im trying to create a function that will transform a regular Matrix into CSR form (I don't want to use the scipy.sparse one).
To do this, I'm using a nested for-loop to run through a given matrix to create a new matrix with three rows.
The first row ('Values') should contain all non-zero values. The second ('Cols') should contain the column index for each number in 'Values'. The third row should contain the index value in 'Values' for the first non-zero value on each row.
My question regards the second and third rows:
Is there a way of getting the column ID for the element 'i' in the for-loop?
M=array([[4,0,39],
[0,5,0],
[0,0,7]])
def Convert(x):
CSRMatrix = []
Values = []
Cols = []
Rows = []
for k in x:
for i in k:
if i != 0:
Values.append(i)
Cols.append({#the column index value of 'i'})
Rows.append[#theindex in 'Values' of the first non-zero element on each row]
CSRMatrix.append(Values)
CSRMatrix.append(Cols)
CSRMatrix.append(Rows)
return(CSRMatrix)
Convert(M)
I'm not sure of what you want exactly for Cols.append() because of the way you commented it in the code between curly braces.
Is it a dict containing the index:value of all non 0 value? Or a list of sets containing the indexes of all non 0 values (which would be weird), or is it all the indexes of each row in your array?
Anyway I put the 2 most likely candidates (dict and list of indexes for each row) test each one and delete the unwanted one and if none are right please add some more specifics:
import numpy as np
m = np.array([[4,0,39],
[0,5,0],
[0,0,7]])
def Convert(x):
CSRMatrix = []
Values = []
Cols = []
Rows = []
for num in x:
for i in range(len(num)):
if num[i] != 0:
Values.append(num[i])
Cols.append({i:num[i]}) # <- if dict. Remove if not what you wanted
Rows.append(i)
Cols.append(i) # <- list of all indexes in the array for each row. Remove if not what you wanted
CSRMatrix.append(Values)
CSRMatrix.append(Cols)
CSRMatrix.append(Rows)
return(CSRMatrix)
x = Convert(m)
print(x)
enumerate() passes an index for every iteration.
Thereby the second row can be easily created by appending num2.
For the third row you have to check again if you have already added a value in that row. If not append num2 and set the non_zero check to False. For the next row non_zero check is set to True again.
def Convert(x):
CSRMatrix = []
Values = []
Cols = []
Rows = []
for num, k in enumerate(x):
non_zero = True
for num2, i in enumerate(k):
if i != 0:
Values.append(i)
Cols.append(num2)
if non_zero:
Rows.append(num2)
non_zero = False
CSRMatrix.append(Values)
CSRMatrix.append(Cols)
CSRMatrix.append(Rows)
return (CSRMatrix)
Here is a numpythonic implementation, use the nonzero method to directly obtain the row and column index of non-zero elements, and then use a comparison to generate a mask. Finally, use nonzero for the mask to get the row indices:
>>> M = np.array([[ 4, 0, 39],
... [ 0, 5, 0],
... [ 0, 0, 7]])
>>> r, c = M.nonzero()
>>> mask = np.concatenate(([True], r[1:] != r[:-1]))
>>> [M[r, c], c, *mask.nonzero()]
[array([ 4, 39, 5, 7]), array([0, 2, 1, 2]), array([0, 2, 3])]
Test of a larger array:
>>> a = np.random.choice(10, size=(8, 8), p=[0.73] + [0.03] * 9)
>>> a
array([[0, 0, 0, 0, 8, 0, 0, 1],
[1, 0, 5, 4, 0, 0, 9, 0],
[0, 0, 9, 0, 0, 0, 0, 1],
[0, 0, 0, 8, 9, 0, 0, 4],
[0, 0, 5, 0, 0, 6, 0, 0],
[0, 8, 0, 0, 0, 0, 0, 9],
[0, 0, 0, 0, 0, 0, 0, 9],
[0, 9, 0, 0, 0, 4, 0, 0]])
>>> r, c = a.nonzero()
>>> mask = np.concatenate(([True], r[1:] != r[:-1]))
>>> pp([a[r, c], c, *mask.nonzero()])
[array([8, 1, 1, 5, 4, 9, 9, 1, 8, 9, 4, 5, 6, 8, 9, 9, 9, 4]),
array([4, 7, 0, 2, 3, 6, 2, 7, 3, 4, 7, 2, 5, 1, 7, 7, 1, 5], dtype=int64),
array([ 0, 2, 6, 8, 11, 13, 15, 16], dtype=int64)]
I have a list of integers which I want to separate according to a certain condition. I want to get the sum and the count of the list elements, stopping when three or more consecutive elements are equal to 0; then the sum and count orders restart again from where they stopped.
For example, part of the list is:
[8, 2, 1, 1, 2, 0, 0, 0, 0, 0, 6, 0, 2, 0, 0, 0, 8, 0, 0, 2, 0, 0, 0, 6, 0, 0]
The process would be:
8, 2, 1, 1, 2 -> sum: 14, length: 5
0, 0, 0, 0, 0
6, 0, 2 -> sum: 8, length: 3
0, 0, 0
8, 0, 0, 2 -> sum: 10, length: 4
0, 0, 0
6, 0, 0 -> sum: 6, length: 3
So the output I want is:
[[14, 5], [8, 3], [10, 4], [6, 3]]
What I've written so far computes the sum okay, but my problem is that zeros within sections aren't counted in the lengths.
Current (incorrect) output:
[[14, 5], [8, 2], [10, 2], [6, 2]]
Code:
arr = [8, 2, 1, 1, 2, 0, 0, 0, 0, 0, 6, 0, 2, 0, 0, 0, 8, 0, 0, 2, 0, 0, 0, 6, 0, 0]
result = []
summed, count = 0, 0
for i in range(0, len(arr) - 2):
el, el1, el2 = arr[i], arr[i + 1], arr[i + 2]
if el != 0:
summed = summed + el
count = count + 1
if el == 0 and el1 == 0 and el2 == 0:
if summed != 0:
result.append([summed, count])
summed = 0
count = 0
elif i == len(arr) - 3:
summed = el + el1 + el2
count = count + 1
result.append([summed, count])
break
print(result)
It is quite hard to understand what your code does. Working with Strings seems more straightforward and readable, your output can be achieved in just two lines (thanks to #CrazyChucky for the improvement):
import re
arr = [8, 2, 1, 1, 2, 0, 0, 0, 0, 0, 6, 0, 2, 0, 0, 0, 8, 0, 0, 2, 0, 0, 0, 6, 0, 0]
# Convert to String by joining integers, and split into substrings, the separator being three zeros or more
strings = re.split(r'0{3,}', ''.join(str(i) for i in arr))
# Sums and counts using list comprehensions
output = [[sum(int(x) for x in substring), len(substring)] for substring in strings]
Output:
>>>output
>>>[[14, 5], [8, 3], [10, 4], [6, 3]]
Remember that readability is always the most important factor in any code. One should read your code for the first time and understand how it works.
If the full list contains numbers with more than one digit, you can do the following:
# Convert to String by joining integers, seperating them by a commade, and split into substrings, the separator being three zeros or more
strings = re.split(r',?(?:0,){3,}', ','.join(str(i) for i in arr))
# Make a list of numbers from those strings
num_lists = [string.split(',') for string in strings]
# # Sums and counts using list comprehensions
output = [[sum(int(x) for x in num_list), len(num_list)] for num_list in num_lists]
This answer is not so much to suggest a way I'd recommend doing it, as to highlight how clever Paul Lemarchand's idea of using a regular expression is. Without Python's re module doing the heavy lifting for you, you have to either look ahead to see how many zeros are coming (as in Prakash Dahal's answer), or keep track of how many zeros you've seen as you go. I think this implementation of the latter is about the simplest and shortest way you could solve this problem "from scratch":
input_list = [8, 2, 1, 1, 2, 0, 0, 0, 0, 0, 6, 0, 2, 0, 0, 0, 8, 0, 0, 2, 0, 0,
0, 6, 0, 0]
output_list = []
current_run = []
pending_zeros = 0
for num in input_list:
# If the number is 0, increment the number of "pending" zeros. (We
# don't know yet if they're part of a separating chunk or not.)
if num == 0:
pending_zeros += 1
# If this is the first nonzero after three or more zeros, process
# the existing run and start over from the current number.
elif pending_zeros >= 3:
output_list.append((sum(current_run), len(current_run)))
current_run = [num]
pending_zeros = 0
# Otherwise, the pending zeros (if any) should be included in the
# current run. Add them, and then the current number.
else:
current_run += [0] * pending_zeros
current_run.append(num)
pending_zeros = 0
# Once we're done looping, there will still be a run of numbers in the
# buffer (assuming the list had any nonzeros at all). It may have
# pending zeros at the end, too. Include the zeros if there are 2 or
# fewer, then process.
if current_run:
if pending_zeros <= 2:
current_run += [0] * pending_zeros
output_list.append((sum(current_run), len(current_run)))
print(output_list)
[(14, 5), (8, 3), (10, 4), (6, 3)]
One note: I made each entry in the list a tuple rather than a list. Tuples and lists have a lot of overlap, and in this case either would probably work perfectly well... but a tuple is a more idiomatic choice for an immutable data structure that will always be the same length, in which each position refers to something different. (In other words, it's not a list of equivalent items, but rather a well-defined combination of (sum, length).)
Use this:
a = [8, 2, 1, 1, 2, 0, 0, 0, 0, 0, 6, 0, 2, 0, 0, 0, 8, 0, 0, 2, 0, 0, 0, 6, 0,0]
total_list = []
i = 0
sum_int = 0
count_int = 0
for ind, ele in enumerate(a):
if ind < (len(a) - 2):
sum_int += ele
if sum_int != 0:
count_int += 1
if (a[ind] == 0) and (a[ind+1] == 0) and (a[ind+2] == 0):
if sum_int != 0:
count_int -= 1
total_list.append([sum_int, count_int])
sum_int = 0
count_int = 0
else:
sum_int += ele
count_int += 1
if sum_int != 0:
total_list.append([sum_int, count_int+1])
sum_int = 0
count_int = 0
print(total_list)
Say I have two lists (always the same length):
l0 = [0, 4, 4, 4, 0, 0, 0, 8, 8, 0]
l1 = [0, 1, 1, 1, 0, 0, 0, 8, 8, 8]
I have the following rules for intersections and unions I need to apply when comparing these lists element-wise:
# union and intersect
uni = [0]*len(l0)
intersec = [0]*len(l0)
for i in range(len(l0)):
if l0[i] == l1[i]:
uni[i] = l0[i]
intersec[i] = l0[i]
else:
intersec[i] = 0
if l0[i] == 0:
uni[i] = l1[i]
elif l1[i] == 0:
uni[i] = l0[i]
else:
uni[i] = [l0[i], l1[i]]
Thus, the desired output is:
uni: [0, [4, 1], [4, 1], [4, 1], 0, 0, 0, 8, 8, 8]
intersec: [0, 0, 0, 0, 0, 0, 0, 8, 8, 0]
While this works, I need to do this with several hundred very large lists (each, with thousands of elements), so I am looking for a way to vectorize this. I tried using np.where and various masking strategies, but that went nowhere fast. Any suggestions would be most welcome.
* EDIT *
Regarding
uni: [0, [4, 1], [4, 1], [4, 1], 0, 0, 0, 8, 8, 8]
versus
uni: [0, [4, 1], [4, 1], [4, 1], 0, 0, 0, 8, 8, [0, 8]]
I'm still fighting the 8 versus [0, 8] in my mind. The lists are derived from BIO tags in system annotations (see IOB labeling of text chunks), where each list element is a character index in a document and the vakue is an assigned enumerated label. 0 represents a label representing no annotation (i.e., used for determining negatives in a confusion matrix); while non zero elements represent assigned enumerated labels for that character. Since I am ignoring true negatives, I think I can say 8 is equivalent to [0, 8]. As to whether this simplifies things, I am not yet sure.
* EDIT 2 *
I'm using [0, 8] to keep things simple and to keep the definitions of intersection and union consistent with set theory.
I would stay away from calling them 'intersection' and 'union', since those operations have well-defined meanings on sets and the operation you're looking to perform is neither of them.
However, to do what you want:
l0 = [0, 4, 4, 4, 0, 0, 0, 8, 8, 0]
l1 = [0, 1, 1, 1, 0, 0, 0, 8, 8, 8]
values = [
(x
if x == y else 0,
0
if x == y == 0
else x if y == 0
else y if x == 0
else [x, y])
for x, y in zip(l0, l1)
]
result_a, result_b = map(list, zip(*values))
print(result_a)
print(result_b)
This is more than enough for thousands, or even millions of elements since the operation is so basic. Of course, if we're talking billions, you may want to look at numpy anyway.
Semi vectorized solution for union and full for intersection:
import numpy as np
l0 = np.array(l0)
l1 = np.array(l1)
intersec = np.zeros(l0.shape[0])
intersec_idx = np.where(l0==l1)
intersec[intersec_idx] = l0[intersec_idx]
intersec = intersec.astype(int).tolist()
union = np.zeros(l0.shape[0])
union_idx = np.where(l0==l1)
union[union_idx] = l0[union_idx]
no_union_idx = np.where(l0!=l1)
union = union.astype(int).tolist()
for idx in no_union_idx[0]:
union[idx] = [l0[idx], l1[idx]]
and the output:
>>> intersection
[0, 0, 0, 0, 0, 0, 0, 8, 8, 0]
>>> union
[0, [4, 1], [4, 1], [4, 1], 0, 0, 0, 8, 8, [0, 8]]
NB: I think your original union solution is incorrect. See the last output 8 vs [0,8]
Let's say we have a 1d numpy array filled with some int values. And let's say that some of them are 0.
Is there any way, using numpy array's power, to fill all the 0 values with the last non-zero values found?
for example:
arr = np.array([1, 0, 0, 2, 0, 4, 6, 8, 0, 0, 0, 0, 2])
fill_zeros_with_last(arr)
print arr
[1 1 1 2 2 4 6 8 8 8 8 8 2]
A way to do it would be with this function:
def fill_zeros_with_last(arr):
last_val = None # I don't really care about the initial value
for i in range(arr.size):
if arr[i]:
last_val = arr[i]
elif last_val is not None:
arr[i] = last_val
However, this is using a raw python for loop instead of taking advantage of the numpy and scipy power.
If we knew that a reasonably small number of consecutive zeros are possible, we could use something based on numpy.roll. The problem is that the number of consecutive zeros is potentially large...
Any ideas? or should we go straight to Cython?
Disclaimer:
I would say long ago I found a question in stackoverflow asking something like this or very similar. I wasn't able to find it. :-(
Maybe I missed the right search terms, sorry for the duplicate then. Maybe it was just my imagination...
Here's a solution using np.maximum.accumulate:
def fill_zeros_with_last(arr):
prev = np.arange(len(arr))
prev[arr == 0] = 0
prev = np.maximum.accumulate(prev)
return arr[prev]
We construct an array prev which has the same length as arr, and such that prev[i] is the index of the last non-zero entry before the i-th entry of arr. For example, if:
>>> arr = np.array([1, 0, 0, 2, 0, 4, 6, 8, 0, 0, 0, 0, 2])
Then prev looks like:
array([ 0, 0, 0, 3, 3, 5, 6, 7, 7, 7, 7, 7, 12])
Then we just index into arr with prev and we obtain our result. A test:
>>> arr = np.array([1, 0, 0, 2, 0, 4, 6, 8, 0, 0, 0, 0, 2])
>>> fill_zeros_with_last(arr)
array([1, 1, 1, 2, 2, 4, 6, 8, 8, 8, 8, 8, 2])
Note: Be careful to understand what this does when the first entry of your array is zero:
>>> fill_zeros_with_last(np.array([0,0,1,0,0]))
array([0, 0, 1, 1, 1])
Inspired by jme's answer here and by Bas Swinckels' (in the linked question) I came up with a different combination of numpy functions:
def fill_zeros_with_last(arr, initial=0):
ind = np.nonzero(arr)[0]
cnt = np.cumsum(np.array(arr, dtype=bool))
return np.where(cnt, arr[ind[cnt-1]], initial)
I think it's succinct and also works, so I'm posting it here for the record. Still, jme's is also succinct and easy to follow and seems to be faster, so I'm accepting it :-)
If the 0s only come in strings of 1, this use of nonzero might work:
In [266]: arr=np.array([1,0,2,3,0,4,0,5])
In [267]: I=np.nonzero(arr==0)[0]
In [268]: arr[I] = arr[I-1]
In [269]: arr
Out[269]: array([1, 1, 2, 3, 3, 4, 4, 5])
I can handle your arr by applying this repeatedly until I is empty.
In [286]: arr = np.array([1, 0, 0, 2, 0, 4, 6, 8, 0, 0, 0, 0, 2])
In [287]: while True:
.....: I=np.nonzero(arr==0)[0]
.....: if len(I)==0: break
.....: arr[I] = arr[I-1]
.....:
In [288]: arr
Out[288]: array([1, 1, 1, 2, 2, 4, 6, 8, 8, 8, 8, 8, 2])
If the strings of 0s are long it might be better to look for those strings and handle them as a block. But if most strings are short, this repeated application may be the fastest route.