Sudoku backtracking solver bug - python

Ok, I've been scratching my head over this for a few hours now..
My goal was to code a sudoku solver that uses the backtracking method and to show the progress of the algorithm using pygame. For this I have to keep track of the events, I did it by appending them to a list named registre as is shown in the code:
def solver(self):
self.t1 = time.time()
if self.t1-self.t0 > self.solve_time_max:
sys.exit(1)
for i in range(self.grid.shape[0]):
for j in range(self.grid.shape[1]):
if self.grid[i][j]==0:
for n in range(1,10):
self.registre.append([n,i,j])
if self.verify(n,i,j):
self.grid[i][j]=n
self.solver()
if 0 not in self.grid:
break
self.registre.append([0,i,j])
self.grid[i][j]=0
return self.grid
I actually succeeded and everything goes fine for most of the runs. But sometimes, for some reason I couldn't identify, this happens :
print(une_grille.grid0)
print(une_grille.grid)
print(une_grille.registre[:20])
[[0 8 0 7 0 0 0 0 2]
[5 0 0 0 0 9 0 0 0]
[0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0]
[0 1 0 0 6 0 0 0 0]
[4 0 0 9 0 0 0 0 0]
[0 0 9 0 8 0 0 0 4]
[2 0 0 0 0 0 0 8 0]
[0 0 0 0 0 0 0 0 0]]
[[1 8 3 7 4 5 6 9 2]
[5 2 4 6 1 9 3 7 8]
[6 9 7 2 3 8 1 4 5]
[3 5 2 8 7 1 4 6 9]
[9 1 8 3 6 4 2 5 7]
[4 7 6 9 5 2 8 1 3]
[7 3 9 1 8 6 5 2 4]
[2 4 1 5 9 3 7 8 6]
[8 6 5 4 2 7 9 3 1]]
[[1, 0, 0], [1, 0, 1], [0, 0, 1], [2, 0, 1], [0, 0, 1], [3, 0, 1], [1, 0,
2], [0, 0, 2], [2, 0, 2], [0, 0, 2], [3, 0, 2], [0, 0, 2], [4, 0, 2], [0,
0, 2], [5, 0, 2], [1, 0, 3], [0, 0, 3], [2, 0, 3], [0, 0, 3], [3, 0, 3]]
What is printed is simply the initialized grid, the solved grid and the first 20 events in self.registre. For this run the displaying on pygame didn't work, some numbers overlap themselves and others are left blank. I am almost sure it's not a displaying problem since the displaying function uses the list registre and it works just fine for most of the other runs. Also I don't understand these events.
Complete script :
import numpy as np
import random as rd
import time
import sys
class Grid():
"""
une Docstring
"""
def __init__(self, nval=15, dim=(9,9), tries_max=1000, init_time_max=5e-3, solve_time_max = 1):
self.nval = nval+1
self.dim = dim
self.t0 = 0
self.t1 = 0
self.tries_max = tries_max
self.k = 0
self.init_time_max = init_time_max
self.solve_time_max = solve_time_max
self.registre = []
self.grid = self.create_grid()
self.smthg = 0
def create_grid(self):
for tries in range(self.tries_max):
self.k = 0
if tries == self.tries_max -1:
print(f"Tried {self.tries_max} times, I have failed")
sys.exit(1)
self.grid0 = np.zeros([self.dim[0],self.dim[1]], dtype=int)
try:
self.grid0 = self.initialize_board()
except SystemExit:
print(f"TRY #{tries}: Spent too much time initializing board. Re-trying.")
continue
self.grid = np.copy(self.grid0)
try:
self.t0 = time.time()
self.grid = self.solver()
if 0 not in self.grid:
print(f"Found grid with solution after n = {tries+1} tries!")
return self.grid
else:
print(f"TRY #{tries} converged to null solution")
continue
except SystemExit:
print(f"TRY #{tries} too much time spent trying to solve current grid, continuing")
continue
print("Maximum tries reached")
def initialize_board(self):
for i in range(self.nval):
rx = rd.randint(0, self.grid0.shape[0]-1)
ry = rd.randint(0, self.grid0.shape[1]-1)
cx = int(rx/3)
cy = int(ry/3)
time0 = time.time()
while(self.grid0[rx][ry]==0):
if time.time()-time0 > self.init_time_max:
sys.exit(1)
r = rd.randint(1, 9)
if((r in self.grid0[rx,:]) or (r in self.grid0[:,ry]) or (r in self.grid0[3*cx:3*cx+3,3*cy:3*cy+3])):
continue
else:
self.grid0[rx][ry] = r
return self.grid0
def solver(self):
self.t1 = time.time()
if self.t1-self.t0 > self.solve_time_max:
sys.exit(1)
for i in range(self.grid.shape[0]):
for j in range(self.grid.shape[1]):
if self.grid[i][j]==0:
for n in range(1,10):
self.registre.append([n,i,j])
if self.verify(n,i,j):
self.grid[i][j]=n
self.solver()
if 0 not in self.grid:
break
self.registre.append([0,i,j])
self.grid[i][j]=0
return self.grid
def verify(self, number, x, y):
cx = int(x/3)
cy = int(y/3)
if((number in self.grid[x,:]) or (number in self.grid[:,y]) or (number in self.grid[3*cx:3*cx+3,3*cy:3*cy+3])):
return False
return True
game = Grid(nval = 35)
print(game.grid)
print(game.grid0)
print(game.registre[:20])
Another instance of the issue :
[[1 2 3 9 5 7 6 4 8]
[5 8 4 6 2 1 9 7 3]
[7 9 6 8 4 3 1 5 2]
[6 7 2 5 1 4 3 8 9]
[3 4 9 7 8 6 5 2 1]
[8 5 1 3 9 2 7 6 4]
[2 1 5 4 6 9 8 3 7]
[4 6 7 1 3 8 2 9 5]
[9 3 8 2 7 5 4 1 6]]
[[0 0 0 9 0 7 6 0 0]
[0 0 0 6 0 0 0 0 3]
[0 0 0 8 4 3 1 5 2]
[6 0 0 0 0 0 0 0 0]
[0 4 9 0 0 6 5 2 0]
[0 5 1 0 9 0 7 0 0]
[0 1 5 0 0 0 0 0 0]
[0 0 7 0 0 0 0 0 5]
[9 0 0 2 0 0 0 1 0]]
[[1, 0, 2], [0, 0, 2], [2, 0, 2], [0, 0, 2], [3, 0, 2], [1, 0, 3], [0, 0, 3], [2, 0, 3], [0, 0, 3], [3, 0, 3], [0, 0, 3], [4, 0, 3], [1, 0, 4], [0, 0, 4], [2, 0, 4], [0, 0, 4], [3, 0, 4], [0, 0, 4], [4, 0, 4], [0, 0, 4]]
I would really appreciate it if you could help me with this.

After you added the code that repeats the generation of grids, it becomes clear that you don't create a new instance of Grid, but mutate the existing one. In that process you should then take care to reset the state completely. You only do this partly, e.g. by resetting t0. But you don't reset registre, and so when you print the first 20 items, you are actually looking at the log of the first attempt, not the successful one.

Related

Sum n values of n-lists with the same index in Python

For example, I have 5 lists with 10 elements each one generated with random values simulating a coin toss.
I get my 5 lists with 10 elements in the following way:
result = [0,1] #0 is tail #1 is head
probability = [1/2,1/2]
N = 10
list = []
def list_generator(number): #this number would be 5 in this case
for i in range(number):
n_round = np.array(rnd.choices(result, probability, k=N))
print(n_round)
list_generator(5)
And for example I would get this
[1 1 0 0 0 1 0 1 1 0]
[0 1 0 0 0 1 1 1 0 1]
[1 1 0 0 1 1 1 0 1 1]
[0 0 0 1 0 0 0 1 0 0]
[0 0 1 1 0 0 0 0 1 1]
How can I sum only the numbers of the same column, I mean, I would like to get a list that appends the value of 1+0+1+0+0 (the first column), then, that list appends the sum of each second coin toss of each round i.e. 1+1+1+0+0 (the second column), and so on with the ten coin tosses
(I need it in a list because I will use this to plot a graph)
I have thought about making a matrix with each array and summing only the nth column and append that value in the list but I do not know how to do that, I do not have much knowledge about using arrays.
Have your function return a 2d numpy array and then sum along the required axis. Separately, you don't need to pass probability to random.choices as equal probabilities are the default.
import random
import numpy as np
def list_generator(number):
return np.array([np.array(random.choices([0,1], k=10)) for i in range(number)])
a = list_generator(5)
>>> a
array([[0, 1, 1, 1, 0, 1, 1, 0, 0, 0],
[1, 0, 1, 0, 1, 1, 1, 1, 1, 0],
[1, 1, 0, 1, 1, 1, 0, 0, 1, 1],
[1, 1, 0, 0, 1, 1, 1, 1, 0, 0],
[0, 1, 1, 0, 0, 1, 1, 1, 0, 0]])
>>> a.sum(axis=0)
array([3, 4, 3, 2, 3, 5, 4, 3, 2, 1])
You can use numpy.random.randint to generate your randomized data. Then use sum to get the sum of the columns:
import numpy as np
N = 10
data = np.random.randint(2, size=(N, N))
print(data)
print(data.sum(axis=0))
[[1 0 1 1 1 1 0 0 1 1]
[0 0 1 1 0 0 1 1 1 0]
[1 1 0 1 1 1 0 0 1 1]
[1 1 0 0 0 0 1 1 1 1]
[1 0 0 1 1 1 0 1 1 1]
[1 0 1 1 0 1 0 1 1 1]
[0 0 0 1 0 1 0 1 1 0]
[0 0 0 1 0 1 0 1 0 1]
[1 0 0 0 1 0 1 0 1 1]
[1 0 1 1 0 1 0 0 0 1]]
[7 2 4 8 4 7 3 6 8 8]

An efficient way to concatenate rows of a 2-dim array according to a given list of pairs of indexes

Suppose I have a 2 dimensional array with a very large number of rows, and a list of pairs of indexes of that array. I want to create a new 2 dim array, whose rows are concatenations of the rows of the original array, made according to the list of pairs of indexes. For example:
a =
1 2 3
4 5 6
7 8 9
0 0 0
indexes = [[0,0], [0,1], [2,3]]
the returned array should be:
1 2 3 1 2 3
1 2 3 4 5 6
7 8 9 0 0 0
Obviously I can iterate the list of indexes, but my question is whether there is a more efficient way of doing this. I should say that the list of indexes is also very large.
First convert indexes to a Numpy array:
ind = np.array(indexes)
Then generate your result as:
result = np.concatenate([a[ind[:,0]], a[ind[:,1]]], axis=1)
The result is:
array([[1, 2, 3, 1, 2, 3],
[1, 2, 3, 4, 5, 6],
[7, 8, 9, 0, 0, 0]])
Another possible formula (with the same result):
result = np.concatenate([ a[ind[:,i]] for i in range(ind.shape[1]) ], axis=1)
You can do this in one line using NumPy as:
a = np.arange(12).reshape(4, 3)
print(a)
b = [[0, 0], [1, 1], [2, 3]]
b = np.array(b)
print(b)
c = a[b.reshape(-1)].reshape(-1, a.shape[1]*b.shape[1])
print(c)
'''
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]]
[[0 0]
[1 1]
[2 3]]
[[ 0 1 2 0 1 2]
[ 3 4 5 3 4 5]
[ 6 7 8 9 10 11]]
'''
You can use horizontal stacking np.hstack:
c = np.array(indexes)
np.hstack((a[c[:,0]],a[c[:,1]]))
output:
[[1 2 3 1 2 3]
[1 2 3 4 5 6]
[7 8 9 0 0 0]]

How to get indexes of top 2 values of each row in a 2-D numpy array, but with a specific area is excluded?

I have a 2-D array for example:
p = np.array([[21,2,3,1,12,13],
[4,5,6,14,15,16],
[7,8,9,17,18,19]])
b = np.argpartition(p, np.argmin(p, axis=1))[:, -2:]
com = np.ones([3,6],dtype=np.int)
com[np.arange(com.shape[0])[:,None],b] = 0
print(com)
b is the indices of top 2 values of each row in p:
b = [[0 5]
[4 5]
[4 5]]
com is np.ones matrix, the same size as p, the element whose index is same as b will change to 0.
So the result is :
com = [[0 1 1 1 1 0]
[1 1 1 1 0 0]
[1 1 1 1 0 0]]
Now I have one more constraint :
p[0:2,0:2] = [[21 2]
[4 5]]
The numbers in these area [0:2,0:2] should not be considered, so the result should be:
b = [[4 5]
[4 5]
[4 5]]
com = [[1 1 1 1 0 0]
[1 1 1 1 0 0]
[1 1 1 1 0 0]]
How can I do this ? Should I use a mask or something similarly?
Thanks in advance !
Just set the values in those slices to a low value, ensuring that they won't be among the two largest, an then use argpartition:
out = np.copy(p)
out[0:2,0:2] = -np.inf
np.argpartition(out, [-2,-1])[:, -2:]
array([[4, 5],
[4, 5],
[4, 5]])

How to get the same output example in Python?

I'm a beginner trying to learn about lists and arrays, especially with dimensional lists. I'm really struggling on how to do the code for the list shown in the output example. I would appreciate some help putting me in the right direction.
Here's how I want the list to look like as a example
# If I put in 4 as a example, the result will be this:
[[0, 0, 0, 0, 0], [0, 1, 2, 3, 4], [0, 2, 4, 6, 8], [0, 3, 6, 9, 12], [0, 4, 8, 12, 16]]
0 0 0 0 0
0 1 2 3 4
0 2 4 6 8
0 3 6 9 12
0 4 8 12 16
my code down below:
def myNumber():
chooseNumber=int(input("\nPlease enter a number between [1, 12]: "))
while not (chooseNumber >= 1 and chooseNumber <= 12):
print("That number is not between [1, 12], try again.")
chooseNumber=int(input("Please enter a number between [1, 12]: "))
print("Your number is:", chooseNumber)
myNumber()
#---- the code below I am struggling with here ----#
n = 4
[[i*j for i in range(n+2)] for j in range (n+2)]
# x = [ [1,3],[2,4] ]
print([[i*j for i in range(n+2)] for j in range (n+2)])
here is how to produce your desired output if I understand correctly:
n = 4
lists = [[i*j for i in range(n+1)] for j in range(n+1)]
outstr = ''
for i in lists:
for n in i:
outstr += str(n) + " "
outstr += "\n"
print(outstr)
Output:
0 0 0 0 0
0 1 2 3 4
0 2 4 6 8
0 3 6 9 12
0 4 8 12 16
Let me know if this helps!

python, scikit-learn - Weird behaviour using LabelShuffleSplit

Following the scikit-learn documentation for LabelShuffleSplit, I wish to randomise my train/validation batches to ensure I'm training on all possible data (e.g. for an ensemble).
According to the doc, I should see something like (indeed, notice that train/validation sets are evenly split via test_size=0.5):
>>> from sklearn.cross_validation import LabelShuffleSplit
>>> labels = [1, 1, 2, 2, 3, 3, 4, 4]
>>> slo = LabelShuffleSplit(labels, n_iter=4, test_size=0.5, random_state=0)
>>> for train, test in slo:
>>> print("%s %s" % (train, test))
...
[0 1 2 3] [4 5 6 7]
[2 3 6 7] [0 1 4 5]
[2 3 4 5] [0 1 6 7]
[4 5 6 7] [0 1 2 3]
But then I tried using labels = [0, 0, 0, 0, 0, 0, 0, 0] which returned:
...
[] [0 1 2 3 4 5 6 7]
[] [0 1 2 3 4 5 6 7]
[] [0 1 2 3 4 5 6 7]
[] [0 1 2 3 4 5 6 7]
(i.e not evenly split - all data has simply been put into the validation set?) I understand in this case that is doesn't really matter which indices are put into the train/validation sets, but I was hoping it would still be a 50%:50% split???

Categories