Made typo, Python function still works. Why? - python

I've been learning programming and Python for about a month now using Udacity. For one of the questions we are supposed to write a function that checks if a sudoku list passed in is valid.
In the for loop below, I wanted to iterate through row and col at the same time with both the original and transposed list using zip() but mistakenly left in row in the second half of my or statement. I ran it and to my surprise, it still continued to return a correct answer.
def check_sudoku(array):
is_sudoku = True
reference = range(1, len(array) + 1)
transposed = zip(array)
for row, col in zip(array, transposed):
if sorted(row) != reference or sorted(row) != reference:
is_sudoku = False
break
return is_sudoku
My guess is it's because I defined is_sudoku = True by default, and I'm comparing rows with a reference list so it catches invalid values even if my transpose didn't work. When I replaced the second row with col though, it broke.
My question is, is my guess right? If not, why does this code work and how could I better write this?
Thanks! (Pasted on codepad as well if you want to see what lists I passed in - http://codepad.org/IXDlZuUu)

If the correct value was False, then your function works because at some point sorted(row) != reference. As for rewriting it, I'd think something like this would be more clear:
def check_sudoku(array):
reference = range(1, len(array) + 1)
transposed = zip(array)
for row, col in zip(array, transposed):
if sorted(row) != reference or sorted(row) != reference:
return False
return True
Additionally, it's pretty hard for me to see why you do transposed = zip(array) and then zip(array, transposed). As far as I can tell that just takes a list like [1, 2, 3] and turns it into [(1, (1,)), (2, (2,)), (3, (3,))].
If you are looking to iterate through the rows and columns, here's one method that works.
>>> rows = incorrect
>>> cols = [[row[i] for i in range(len(rows[0]))] for row in rows]
>>> cols = [[row[i] for row in rows] for i in range(len(row))]
>>> cols
[[4, 6, 3, 9, 7, 8, 1, 5, 2], [1, 5, 9, 6, 3, 2, 4, 8, 7], [2, 8, 7, 4, 5, 1, 9, 3, 6], [3, 9, 5, 2, 1, 7, 6, 4, 8], [6, 4, 2, 3, 8, 9, 5, 7, 1], [7, 1, 8, 5, 4, 6, 3, 2, 9], [8, 3,
1, 7, 6, 4, 2, 9, 5], [5, 2, 6, 8, 9, 3, 7, 1, 4], [1, 7, 4, 1, 2, 5, 8, 6, 3]]
>>> rows
[[4, 1, 2, 3, 6, 7, 8, 5, 1], [6, 5, 8, 9, 4, 1, 3, 2, 7], [3, 9, 7, 5, 2, 8, 1, 6, 4], [9, 6, 4, 2, 3, 5, 7, 8, 1], [7, 3, 5, 1, 8, 4, 6, 9, 2], [8, 2, 1, 7, 9, 6, 4, 3, 5], [1, 4,
9, 6, 5, 3, 2, 7, 8], [5, 8, 3, 4, 7, 2, 9, 1, 6], [2, 7, 6, 8, 1, 9, 5, 4, 3]]

Related

(Sudoku board validator) loop through every n item in couple lists

I'm trying to make a sudoku board validator and I'm stuck at validating columns.
Grid looks like this:
[1,2,3,4,5,6,7,8,9]
[5,5,5,5,5,5,5,5,8]
[5,5,5,5,5,5,5,5,7]
[5,5,5,5,5,5,5,5,6]
[5,5,5,5,5,5,5,5,5]
[5,5,5,5,5,5,5,5,4]
[5,5,5,5,5,5,5,5,3]
[5,5,5,5,5,5,5,5,2]
[5,5,5,5,5,5,5,5,1]
I've came up with an idea
to loop through every n item in rows, put them in list and by using set() check if all numbers are unique (9 times).
My code looks like this:
def grid(*rows):
#validate columns
cut = []
x = range(0,8)
counter = 0
while counter != 9:
for column in rows:
cut.append(column[x])
print(len(cut) == len(set(cut)))
counter += 1
I suspect there are better and more efficient ways to solve this problem, but I'm new to Python and want to learn. If I want to make it my way, what am I doing wrong?
If I want to make it my way, what am I doing wrong?
you initialize cut = [] in front of both loops instead of initializing it every time a new column is validated. You have to place cut = [] within the while and before the for loop going over the rows to prevent the code of collecting in cut all of the extracted values and not only these from the just validated column.
x = range(0,8) makes x being a range not a single value required as index, so the code column[x] gives an TypeError: list indices must be integers or slices, not range. You don't need x in your code at all. The value of counter is what you need in place of x.
print and counter+1 indentation is wrong. In your code the counter will be increased not only with each new processed column, but also with each row from which the single column value is extracted. This will give counter values from 0 up to 80 and not as expected and should be from 0 to 8.
a better approach to sudoku validation will be one validation procedure applied to the three different views on the sudoku grid. For a proposal of code providing functions for generating a column and 3x3 cell view on the sudoku grid generated from the list of rows see the code provided later on along with a proposal of another principle for use in the validation function.
Below your code adjusted in a way that it works as it should:
def grid(rows):
#validate columns
counter = 0
while counter != 9:
cut = []
for row in rows:
cut.append(row[counter])
counter += 1
print(len(cut) == len(set(cut)))
To get you started using another approach for the logic of a validation procedure, below a suggestion how to get the column and the 3x3 cell views on the sudoku grid list from the list of the grid rows. Having this views the function for validating the columns and the 3x3 cells will be exactly the same as for rows.
The naming of variables in the code should be sufficient as documentation what the code does:
lstSudokuGridRows = [
[6, 2, 5, 8, 4, 3, 7, 9, 1],
[7, 9, 1, 2, 6, 5, 4, 8, 3],
[4, 8, 3, 9, 7, 1, 6, 2, 5],
[8, 1, 4, 5, 9, 7, 2, 3, 6],
[2, 3, 6, 1, 8, 4, 9, 5, 7],
[9, 5, 7, 3, 2, 6, 8, 1, 4],
[5, 6, 9, 4, 3, 2, 1, 7, 8],
[3, 4, 2, 7, 1, 8, 5, 6, 9],
[1, 7, 8, 6, 5, 9, 3, 4, 2]]
print(lstSudokuGridRows)
def getSudokuGridColumns(lstSudokuGridRows):
return list(zip(*lstSudokuGridRows))
lstSudokuGridColumns = getSudokuGridColumns(lstSudokuGridRows)
print(lstSudokuGridColumns)
def getSudokuGridCells(lstSudokuGridRows):
lstCells = [[],[],[],[],[],[],[],[],[]]
#print(lstCells)
for i_row, row in enumerate(lstSudokuGridRows):
for i_column, col_row_item in enumerate(row):
#print(3*(i_row//3)+(1+i_column//3)-1, end='|')
lstCells[3*(i_row//3)+(1+i_column//3)-1].append(col_row_item)
#print()
return lstCells
lstSudokuGridCells = getSudokuGridCells(lstSudokuGridRows)
print(lstSudokuGridCells)
and the printed output can be checked against the sudoku grid defined as a list of rows if the correct values are in the 9 sublists of the list:
[[6, 2, 5, 8, 4, 3, 7, 9, 1], [7, 9, 1, 2, 6, 5, 4, 8, 3], [4, 8, 3, 9, 7, 1, 6, 2, 5], [8, 1, 4, 5, 9, 7, 2, 3, 6], [2, 3, 6, 1, 8, 4, 9, 5, 7], [9, 5, 7, 3, 2, 6, 8, 1, 4], [5, 6, 9, 4, 3, 2, 1, 7, 8], [3, 4, 2, 7, 1, 8, 5, 6, 9], [1, 7, 8, 6, 5, 9, 3, 4, 2]]
[(6, 7, 4, 8, 2, 9, 5, 3, 1), (2, 9, 8, 1, 3, 5, 6, 4, 7), (5, 1, 3, 4, 6, 7, 9, 2, 8), (8, 2, 9, 5, 1, 3, 4, 7, 6), (4, 6, 7, 9, 8, 2, 3, 1, 5), (3, 5, 1, 7, 4, 6, 2, 8, 9), (7, 4, 6, 2, 9, 8, 1, 5, 3), (9, 8, 2, 3, 5, 1, 7, 6, 4), (1, 3, 5, 6, 7, 4, 8, 9, 2)]
[[6, 2, 5, 7, 9, 1, 4, 8, 3], [8, 4, 3, 2, 6, 5, 9, 7, 1], [7, 9, 1, 4, 8, 3, 6, 2, 5], [8, 1, 4, 2, 3, 6, 9, 5, 7], [5, 9, 7, 1, 8, 4, 3, 2, 6], [2, 3, 6, 9, 5, 7, 8, 1, 4], [5, 6, 9, 3, 4, 2, 1, 7, 8], [4, 3, 2, 7, 1, 8, 6, 5, 9], [1, 7, 8, 5, 6, 9, 3, 4, 2]]
For validation of the board I would suggest not to implement the idea of the number of unique elements as the not yet solved sudoku board can have also the zero ( 0 ) as value or just wrong values beyond the span of 1 to 9 what will then be not detected by the validation and therefore making it much harder to debug the code in eventual case of problems. Below a proposal of validation function based on comparing a sorted line of the sudoku grid view (which can be one of row view, column view and 3x3 cell view) with [1,2,3,4,5,6,7,8,9]:
def validateSudokuView(lstSudokuGridView):
gridViewOK = True
for line in lstSudokuGridView:
if sorted(line) != [1,2,3,4,5,6,7,8,9]:
gridViewOK = False
break
return gridViewOK
which can be also coded as:
def validateSudokuView(lstSudokuGridView):
return all(sorted(line)==[1,2,3,4,5,6,7,8,9] for line in lstSudokuGridView)
using the Python all() function.
You can use zip to transpose the matrix, then check the size of the sets while iterating over the columns.
for col in zip(*rows):
if len({*col}) != len(col):
print('not unique')
else:
print('good')

Creating a list of lists via iteration - Cannot understand why variable.append() gives me the same list many times, rather than each list being unique

I would like to create a list of lists, with each sub-list increasing by 1.
In other words
[[1],
[1, 2],
[1, 2, 3],
[1, 2, 3, 4],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5, 6],
[1, 2, 3, 4, 5, 6, 7],
[1, 2, 3, 4, 5, 6, 7, 8],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
I wrote the following code, but I don't understand why list_of_lists returns the same thing 10 times in a row.
I have found examples of how to do it correctly, but I would like to understand why the code below appends the same version of the temporary list to list_of_lists, rather than appending each new version once.
list_of_lists = []
temporary = []
for i in range(1,11):
temporary.append(i)
print(temporary)
# the temporary list iterates as expected when I print it. When i is 1, it prints [1]. When i is 2, it prints [1,2]
list_of_lists.append(temporary)
print(list_of_lists)
# "list of lists" simply appends the LATEST version of "temporary" list i number of times.
My reasoning is that list_of_lists.append(temporary) should simply append 1 version of the temporary list to the end. And I don't understand why instead it overwrites the previous appended versions of the temporary list, and doesn't simply append the latest one to the already existing entries in list_of_lists
I have seen examples of how to do it correctly, so this issue is functionally solved. But I would greatly appreciate it if anyone could please take the time to explain why my logic was incorrect.
Thank you.
I was expecting:
[[1],
[1, 2],
[1, 2, 3],
[1, 2, 3, 4],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5, 6],
[1, 2, 3, 4, 5, 6, 7],
[1, 2, 3, 4, 5, 6, 7, 8],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
I got:
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
If you use list_of_lists.append(temporary), you only add a reference to the temporary list. What this means is when you print list_of_lists, it checks to see the latest version of the temporary list, and prints that. Therefore, any changes made in temporary will automatically be made in list_of_lists.
To get aroudn this, instead of appending the temporary list, you can append a copy of the list instead with temporary.copy().
Try this:
list_of_lists = []
temporary = []
for i in range(1,11):
temporary.append(i)
print(temporary)
list_of_lists.append(temporary.copy()) #this should fix it
print(list_of_lists)
This then outputs:
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6, 7], [1, 2, 3, 4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
instead of:
list_of_lists.append(temporary)
you can use this:
list_of_lists.append(temporary[:])
The difference ist, that the first line only appends a reference to the list, where the second line creates a copy through [:] of the list.

How to change an array to boolean values making the array output True/False

Make a new array where the elements are all Boolean values, representing whether or not the corresponding values in the solved board are odd. (That is to say, if a value is odd, the new array should have True in its corresponding location.)
Currently my code just outputs whether the values are true or false, but are not in the 9x9 array I need it to be in.
Array
solvedBoard = np.array([
[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]])
INPUT:
for item in np.nditer(solvedBoard1):
if item%2 ==0:
print("False")
else:
print("True")
OUTPUT:
True
True
False
False
True
False
True etc...
Try a simple comparison answer = solvedBoard % 2 == 0

How to swap items in a list within a list python

I am trying to randomly swap 2 items in each list within a list, where those to be swapped are not in another list.
Here is my code
import random
def swap(mylist):
remain = [[1, 2], [4], [], [8, 2], [1, 4], [5, 2, 1], [], [9, 5], [7]]
for x in range(0, 9):
remaining = set(mylist[x]) - set(remain[x])
to_swap = random.sample(remaining, 2)
mylist[x][mylist[x].index(to_swap[0])], mylist[x][mylist[x].index(to_swap[1])] = mylist[x][mylist[x].index(to_swap[1])], mylist[x][mylist[x].index(to_swap[0])]
return mylist
print(swap([[8, 5, 4, 1, 3, 9, 7, 6, 2], [9, 3, 5, 6, 4, 7, 1, 2, 8], [7, 3, 2, 5, 4, 1, 9, 6, 8], [2, 1, 3, 8, 6, 9, 5, 7, 4], [1, 2, 3, 5, 7, 4, 9, 8, 6], [6, 9, 3, 1, 7, 4, 2, 8, 5], [1, 2, 7, 4, 3, 8, 5, 9, 6], [3, 7, 8, 4, 1, 5, 9, 6, 2], [4, 2, 6, 5, 7, 1, 9, 3, 8]]))
Whenever I run this and print out the result, it just prints out my input again.
Does anyone know what is wrong with my code?
Thanks.
Your code performs the swaps with about one half of the sublists. I wonder what the reason of this behavior is *(see below).
If you rewrite the swapping part like this:
i = mylist[x].index(to_swap[0])
j = mylist[x].index(to_swap[1])
mylist[x][i], mylist[x][j] = mylist[x][j], mylist[x][i]
then it works.
UPDATE:
There is no need to access the lists on the right-hand side of the assignment, since we already know the values, so the updated answer would be:
i = mylist[x].index(to_swap[0])
j = mylist[x].index(to_swap[1])
mylist[x][i], mylist[x][j] = to_swap[1], to_swap[0]
*UPDATE 2:
The above mentioned behavior is caused by the fact that in multiple assignments, expressions on the left-hand side are evaluated one by one from left to right. That means the OP's code didn't work in cases where index(to_swap[0]) < index(to_swap[1]).
Example: values 5 and 6 in the first sublist [8, 5, 4, 1, 3, 9, 7, 6, 2]. First, the program will do
mylist[x][mylist[x].index(5)] = 6
modifying the list to [8, 6, 4, 1, 3, 9, 7, 6, 2]. Second, the program will do
mylist[x][mylist[x].index(6)] = 5
modifying it back to [8, 5, 4, 1, 3, 9, 7, 6, 2].

AssertEqual returning False when, from Observation is True

I'm currently revising for an exam, for which there is a test script given by the professor.
The question concerns the game Sodoku; in this section, I have to return all non-zero values of a row of values to the Sudoku table (represented by a 2D array) as a set.
def get_values_from_row(puzzle, row):
rowVal = []
try:
for i in puzzle[row]:
if i != 0:
rowVal.append(i)
except IndexError:
print('Invalid Row')
if len(rowVal) == 0:
return rowVal
else:
rowVal = set(rowVal)
print(rowVal)
return(rowVal)
here is the Sudoku board
sudoku1 = [[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]]
When I run the function for row 0 I get {1,2,3,4,5,6,7,8,9} as expected. However, when I run the test script returns a fail.
This is the relevant code in the test script:
import unittest
class Test(unittest.TestCase):
def test_get_values_from_row(self):
sudoku1 = [
[5, 3, 4, 0, 7, 8, 9, 1, 2],
[6, 7, 0, 0, 9, 5, 0, 4, 8],
[1, 9, 8, 0, 4, 0, 5, 6, 7],
[8, 5, 9, 7, 6, 1, 4, 2, 3],
[4, 2, 6, 8, 5, 3, 7, 9, 1],
[7, 1, 3, 0, 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]]
self.assertEqual(sg.get_values_from_row(sudoku1, 0), set([6]))
sg is just what the script I'm editing is imported as. When I look at the log apparently 6 is not in the set, when I changed it to 3 the same thing happened. It looked like whatever the test value was it would be deleted from my returned list
Traceback (most recent call last):'
File "question_1_iii_test.py", line 23, in test_get_values_from_row'
self.assertEqual(sg.get_values_from_row(sudoku1, 0), set([6]))'
AssertionError: Items in the first set but not the second:'
1
2
3
4
5
7
8
9
Items in the second set but not the first:
6
My question is: Why does AssertEqual return false when it's clearly true?
The return value of the function is not same as [6], so AssertEqual return false
sg.get_values_from_row(sudoku1, 0) = [5, 3, 4, 7, 8, 9, 1, 2]
which is not equal to [6]
Okay so I contacted my professor and apparently, the test code was incorrect it was meant to be inverting the list so that the only number in the list was the one that was omitted in the row so in a way; we were all right
This is probably due to a datatype error. set([6]) will give a type() set while sudoku1 is a list and contains lists
[6]==set([6])
will return False

Categories

Resources