Sudoku Solver in Python - python

So, I'm trying to create a function that will take in a list of lists, each consisting of 9 integers numbered 1 through 9, and return a Boolean if it is a valid solution to for a Sudoku. Right now, I've managed to solve part of the problem with checking for rows and columns, but I am stuck on the implementation of checking the three by three boxes. Here's my code so far.
alist = [1,2,3,4,5,6,7,8,9]
def checkSudoku(grid):
a = grid
b = grid
c = grid
d = False
e = False
f = False
newlist = []
for i in a:
i.sort()
if i == alist:
d = True
else:
d = False
break
for j in range(9):
for k in b:
newlist.append(k)
newlist.sort()
if i == alist:
e = True
newlist = []
else:
e = False
break
if d == True and e == True:
return True
else:
return False
Basically, My was to test all three factors necessary for it to be true, then return True if all three are True, else return false. Any help?

Not sure if this is your problem, but there is a pretty obvious problem with this code:
a = grid
b = grid
c = grid
It looks like you think this creates 3 copies of the grid, but it doesn't. It creates three different references to the same object. This means that your i.sort() on a will affect the logic on b later on.
What you should do is actually copy the object. That it's a nested list makes this a little bit tricky, but an easy way to do this is with the library function deepcopy:
a = copy.deepcopy(grid)
b = copy.deepcopy(grid)
c = copy.deepcopy(grid)

One major problem with how your code works is that you're using list.sort, meaning that grid itself changes. Consider using sorted, which works with all iterables and returns a copy instead:
for row in grid:
if sorted(row) == alist:
# now you know that the row contains all ints 1-9.
This also means you don't need to try to manually duplicate grid. If you need help duplicating a list (especially a multidimensional list) check out this question.
As for checking each 3x3 box: iterate through each of the 9 "top left" corners first like so:
for x in (0,3,6):
for y in (0,3,6):
subgrid = grid[y][x:x+3] + grid[y+1][x:x+3] + grid[y+2][x:x+3]
if sorted(subgrid) == alist:
# do your thing
For help with list slicing check this out.

Related

Failing hidden tests in codesignal. What seems to be the problem in my code?

https://app.codesignal.com/arcade/intro/level-7/PTWhv2oWqd6p4AHB9
Here, the problem is that "Given an array of equal-length strings, you'd like to know if it's possible to rearrange the order of the elements in such a way that each consecutive pair of strings differ by exactly one character. Return true if it's possible, and false if not."
and my code is
import itertools
def only_one_element_different(x, y):
list1 = []
for i in x:
list1.append(i)
list2 = []
for j in y:
list2.append(j)
list3 = []
for k in range(0, len(list1)):
if list1[k] == list2[k]:
list3.append(True)
else:
list3.append(False)
if list3.count(False) == 1:
return True
else:
return False
def if_list_is_linear(list):
list4 = []
for m in range(0, len(list)-1):
if only_one_element_different(list[m], list[m+1]):
list4.append(True)
else:
list4.append(False)
if False in list4:
return False
else:
return True
list5 = ['aba', 'bbb','bab','bba']
list6 = list(itertools.permutations(list5))
list7 = []
for n in list6:
if if_list_is_linear(n) == True:
list7.append(True)
else:
list7.append(False)
if True in list7:
print("Real True")
else:
print("False")
​
(In list5, The array is given)
this passed all the tests but
failed several hidden tests.
I don't know if it is because of the timeout or flaws in my code.
Please help
(sorry for the bad english)
As I don't know what the "hidden tests" are and am not willing to create an account on this site, read the specs (which are probably incomplete and ambiguous as usual) and take the whole test, I'll only address the perfs issues.
Your first function is about as inefficient as possible:
def only_one_element_different(x, y):
list1 = []
for i in x:
list1.append(i)
This can be rewritten as list1 = list(x), which is faster - but it's not even needed: strings are sequences, and sequences are iterables, so you can just use the strings directly.
list2 = []
for j in y:
list2.append(j)
idem
list3 = []
for k in range(0, len(list1)):
if list1[k] == list2[k]:
list3.append(True)
else:
list3.append(False)
First simplification: use zip() to iterate on both strings at once:
>>> print(list(zip("aba", "bbb")))
[('a', 'b'), ('b', 'b'), ('a', 'b')]
which would give:
for c1, c2 in zip(x, y):
if c1 == c2:
list3.append(True)
else:
list3.append(False)
Now c1 == c2 is an expression, which means it's evaluates to an object (a boolean in this case), so you can simplify this as
for c1, c2 in zip(x, y):
list3.append(c1 == c2)
Now this is a rather inefficient way to build a list - first because of the method calls overhead, and also because growing the list might lead to memory allocation, which is costly. A much faster solution is a "list comprehesion":
list3 = [c1 == c2 for c1, c2 in zip(x, y)]
and while we're at it, this:
if list3.count(False) == 1:
return True
else:
return False
is a convoluted way to write
return list3.count(False) == 1
IOW you can rewrite the whole thing as
return [c1 == c2 for c1, c2 in zip(x, y)].count(False) == 1
Now if the x and y strings were long, this would still be inefficient - you are inspecting the whole string when you could detect "non-matching" string way sooner:
def only_one_diff(x, y):
diff = False
for c1, c2 in zip(x, y):
if c1 != c2:
if diff:
# we already found another diff in a previous iteration,
# so there's more than one diff, so we are done
return False
# else it was the first diff, let's flag it:
diff = True
# if we didn't return from within the for loop,
# we have either one single diff or no diff at all:
return diff
Which of those two implementations will actually be faster depends on the strings length. For long strings, this second implemention should be faster, for 3 letters strings it's not garanteed, you'd have to benchmark both using the timeit module. In both cases, the second implementation will eat less memory...
The rest of your code suffers from similar issues:
def if_list_is_linear(list):
list4 = []
for m in range(0, len(list)-1):
if only_one_element_different(list[m], list[m+1]):
list4.append(True)
else:
list4.append(False)
if False in list4:
return False
else:
return True
here again, you'd save some time by returning as soon as you know one of the strings doesn't pass the only_one_diff predicate:
# NB : don't use `list` as a var name, it shadows the builtin `list` type
def if_list_is_linear(lst):
for x, y in zip(lst, lst[1:]):
if not only_one_diff(x, y):
# no need to test further
return False
# ok, we're good
return True
I think you get the point by now....
I am also working on CodeSignal's practice problems using python. Sometimes, but not always, if a print statement is included in the submitted solution - even if the line is never read - this can cause a hidden test to fail.
After removing or commenting out print statements double check for edge cases that you may have overlooked and were not tested in the visible tests. (i.e. 'abc','abc' is a discontinuity while 'abc','aba' is okay because exactly one letter changed)
For your reference, I outlined and annotated my solution below.
First: Make a dictionary that keeps a list of all strings that are only one letter different. i.e. for [ 'aba' , 'bbb' , 'bab' , 'bba' ]
dictionary={ 'aba' : ['bba'] , 'bbb' : ['bab','bba'] , ... }
string_dict={}
#iterate through strings comparing each key string to all other strings
for idx in range(len(inputArray)):
key=inputArray[idx]
string_dict[key]=[]
for string in np.concatenate([inputArray[:idx],inputArray[idx+1:]]):
#check to see if key is within one letter of the string
count=np.sum([k!=s for (k,s) in zip(key,string)])
#do not add value to key if value==key or 2 or more letters are different
if count>1 or count==0:
continue
else:
#Add string to dictionary as a value if it is within 1 letter from the key
string_dict[key]+=[string]
From now on you do not need to compute whether string1 is within 1 letter change from string2 since it is stored in your dictionary
Second: check all permutations of inputArray to see if any are a solution
Given that there will be 10 strings or less in the given inputArray it is not too expensive to check all possible permutations
arr_len=len(inputArray)
for permutation in list(permutations(range(arr_len))):
#order inputArray values according to each permutation
arr=inputArray[np.array(permutation)]
#Count how many adjacent strings are more than 1 letter different
discontinuities=np.sum([arr[idx+1] not in string_dict[arr[idx]] for idx in range(arr_len-1)])
if discontinuities==0:
#Made it to the end of the permutation and found no discontinuities. A solution exists!
return True
#Went through all permutations and all of them had at least one discontinuity. No solution exists.
return False

Trying create a matrix for sudoku with random numbers in Python

I need to create a sudoku template, with it all filled. I need to do with random numbers, so I check if the number already exists in the row and in the column. The problem is that when a number is repeated in the column, it does not try another one, getting an infinite loop. Can Someone help me? Remembering that I am required to do with random numbers.
import random
matrix = [[None for i in range(9)] for j in range(9)]
def criarSdk():
for l in range(9):
for c in range(9):
if matrix[l][c] is None:
tmp3 = False
print("linha l={}, coluna c={}".format(l,c))
while tmp3 is False:
ale = random.randint(1, 9)
tmp1 = veriLine(matrix,ale, l)
tmp2 = veriCol(matrix,ale, c)
tmp3 = tmp1 and tmp2
if tmp3 is True:
matrix[l][c] = ale
def veriLine(vetor, value, line):
tmp = True
for c in range(9):
if value == vetor[line][c]:
tmp = False
return tmp
def veriCol(vetor, value, col):
tmp = True
for l in range(9):
if value == vetor[l][col]:
tmp = False
return tmp
criarSdk()
for i in range(9):
print(matrix[i])
Your problem is not with the code, but with your algorithm.
If you fill up a sudoku like this, you can end up in a situation where no digit can be filled in for some space.
Consider this:
1 2 3 4 5 6 7 8 9
2 1 5 3 4 7 8 6 x
You can imagine the first line getting filled with random values (which happen to be in order) and then the values on the second line also getting filled in with random values, but what value to put in x? You can't put in 9, because it would conflict on the column. But you can't put in anything else either, since everything else is already on the line.
In this case, your code will loop endlessly, trying random numbers, but none of them will ever work.
You need to look into backtracking and come up with a better solution. Note that your requirement for randomly picking a number doesn't have to mean you keep calling random.randint() for a new option, you could also randomly pick from a set of numbers you haven't tried yet.

how do I check if a 2 dimensional python list contains only number 1

Assume I have a two dimensional list and not using numpy,not using two for loop in this case.
a = [[0,0,0],[1,0,0]]
b = [[0,0,0],[0,0,0]]
is there any function to determine that a list contain only 1 or 0.
any() and all() won't work on the 2D list I think.
a = [[0,0,0][1,0,0]]
b = [[0,0,0][0,0,0]]
I want
if allListItemsAreZero(b):
print(‘this works’)
There are two things in your question.
your lists have to be corrected
a = [[0,0,0][1,0,0]] ==> a = [[0,0,0],[1,0,0]]
and then below kind of code should help you.
a = [[0,0,0],[1,0,0]]
for x in a:
print(x.count(1))
If you're allowed to use itertools from the standard library, this is a way to do it:
from itertools import chain
def allListItemsAreZero(input_list):
for n in chain.from_iterable(input_list):
if n != 0:
return False
return True
To check all 0s we can use any to check if we encounter a 1 in the sublist (Thanks #JonClements for the idea)
def check_all_zeros(a):
return not any(c != 0 for r in a for c in r)
print(check_all_zeros([[0,0,0],[0,0,0]]))
print(check_all_zeros([[0,1,0],[0,0,0]]))
The output will be
True
False
A similar idea for checking all 1s, we check if we encounter a 0 in the sublist
def check_all_ones(a):
return not any(c != 1 for r in a for c in r)
print(check_all_ones([[1,1,1],[1,1,1]]))
print(check_all_ones([[0,1,0],[0,0,0]]))
The output will be
True
False

AND and OR are switched?

I want to create two random sets and compare them to two other sets.
The programm should stop when both sets are the same as the other two sets.
This is a boiled down version of the code i'm having trouble with:
import random
random.seed()
a = set()
b = set()
c = set()
d = set()
for i in range(3):
a.add(random.randint(1,10))
b.add(random.randint(1,10))
while a!=c and b!=d:
c.clear()
d.clear()
for i in range(3):
c.add(random.randint(1,10))
d.add(random.randint(1,10))
print(a,b)
print(c,d)
The exact line with my problem:
while a!=c and b!=d:
With "and" the loop already stops when just one pair of sets are the same.
When i switch the "and" for "or" it does exacly what i want, i just don't understand why?
Can somebody please explane.
The condition holds when both a is not c and b is not d. As soon as one pair is equal then the and fails and the loop is broken.
As you have identified you could use an or which will work, but it may be more clear to use:
while not (a == c and b == d):
...
which works exactly how you would say it!
Think about it. You have the condition:
a != c and b != d
What exactly is this condition telling Python? Well, we can see it uses the and operator. The and operator will only return True, if both of its conditions are true. From this, we can see that the above condition will only return true if both the a set is not equal to the c set and the b set is not equal to the d set.
So what would happen when one pair of sets becomes equal? The above condition will fail. Since one of the comparisons between two sets would return False, rather than True, and will also return false, since both of its conditions are not true.
What you actually want is this:
not (a == c and b == d)
I'll leave it as an exercise up to you to figure why the above condition works, whereas a != c and b != d does not.
While loops repeat when the statement is True, in this case when either a != c orb != devaluates to False, so will the while loop argument.
You want to alter the logic, change:
a != c and b !=d
to:
not (a == c and b == d)
or:
a != c or b != d
This will achieve what you are after.
You might try:
while not (a == c and b == d):
#code

Memoryerror with too big list

I'm writing script in python, and now I have to create pretty big list exactly containing 248956422 integers. The point is, that some of this "0" in this table will be changed for 1,2 or 3, cause I have 8 lists, 4 with beginning positions of genes, and 4 with endings of them.
The point is i have to iterate "anno" several time cause numbers replacing 0 can change with other iteration.
"Anno" has to be written to the file to create annotation file.
Here's my question, how can I divide, or do it on-the-fly , not to get memoryerror including replacing "0" for others, and 1,2,3s for others.
Mabye rewriting the file? I'm waitin for your advice, please ask me if it is not so clear what i wrote :P .
whole_st_gen = [] #to make these lists more clear for example
whole_end_gen = [] # whole_st_gen has element "177"
whole_st_ex = [] # and whole_end_gen has "200" so from position 177to200
whole_end_ex = [] # i need to put "1"
whole_st_mr = [] # of course these list can have even 1kk+ elements
whole_end_mr = [] # note that every st/end of same kind have equal length
whole_st_nc = []
whole_end_nc = [] #these lists are including some values of course
length = 248956422
anno = ['0' for i in range(0,length)] # here i get the memoryerror
#then i wanted to do something like..
for j in range(0, len(whole_st_gen)):
for y in range(whole_st_gen[j],whole_end_gen[j]):
anno[y]='1'
You might be better of by determine the value of each element in anno on the fly:
def anno():
for idx in xrange(248956422):
elm = "0"
for j in range(0, len(whole_st_gen)):
if whole_st_gen[j] <= idx < whole_end_gen[j]:
elm = "1"
for j in range(0, len(whole_st_ex)):
if whole_st_ex[j] <= idx < whole_end_ex[j]:
elm = "2"
for j in range(0, len(whole_st_mr)):
if whole_st_mr[j] <= idx < whole_end_mr[j]:
elm = "3"
for j in range(0, len(whole_st_nc)):
if whole_st_nc[j] <= idx < whole_end_nc[j]:
elm = "4"
yield elm
Then you just iterate using for elm in anno().
I got an edit proposal from the OP suggesting one function for each of whole_*_gen, whole_st_ex and so on, something like this:
def anno_st():
for idx in xrange(248956422):
elm = "0"
for j in range(0, len(whole_st_gen)):
if whole_st_ex[j] <= idx <= whole_end_ex[j]:
elm = "2"
yield elm
That's of course doable, but it will only result in the changes from whole_*_ex applied and one would need to combine them afterwards when writing to file which may be a bit awkward:
for a, b, c, d in zip(anno_st(), anno_ex(), anno_mr(), anno_nc()):
if d != "0":
write_to_file(d)
elif c != "0":
write_to_file(c)
elif b != "0":
write_to_file(b)
else:
write_to_file(a)
However if you only want to apply some of the change sets you could write a function that takes them as parameters:
def anno(*args):
for idx in xrange(248956422):
elm = "0"
for st, end, tag in args:
for j in range(0, len(st)):
if st <= idx < end[j]:
elm = tag
yield tag
And then call by supplying the lists (for example with only the two first changes):
for tag in anno((whole_st_gen, whole_end_gen, "1"),
(whole_st_ex, whole_end_ex, "2")):
write_to_file(tag)
You could use a bytearray object to have a much more compact memory representation than a list of integers:
anno = bytearray(b'\0' * 248956422)
print(anno[0]) # → 0
anno[0] = 2
print(anno[0]) # → 2
print(anno.__sizeof__()) # → 248956447 (on my computer)
Instead of creating a list using list comprehension I suggest to create an iterator using a generator-expression which produce the numbers on demand instead of saving all of them in memory.Also you don't need to use the i in your loop since it's just a throw away variable which you don't use it.
anno = ('0' for _ in range(0,length)) # In python 2.X use xrange() instead of range()
But note that and iterator is a one shot iterable and you can not use it after iterating over it one time.If you want to use it for multiple times you can create N independent iterators from it using itertools.tee().
Also note that you can not change it in-place if you want to change some elements based on a condition you can create a new iterator by iterating over your iterator and applying the condition using a generator expression.
For example :
new_anno =("""do something with i""" for i in anno if #some condition)

Categories