1

Currently I have a file with 6 rows of numbers and each row containing 9 numbers. The point is to test each row of numbers in the file if it completes a magic square. So for example, say a row of numbers from the file is 4 3 8 9 5 1 2 7 6. The first three numbers need to be the first row in a matrix. The next three numbers need to be the second row, and same for the third. Therefore you would need to end up with a matrix of: [['4','3','8'],['9','5','1'],['2','7','6']]

I need to test the matrix to see if it is a valid magic square (Rows add up to 15, columns add to 15, and diagonals add to 15).

My code is currently:

def readfile(fname):
    """Return a list of lines from the file"""
    f = open(fname, 'r')
    lines = f.read()
    lines = lines.split()
    f.close()
    return lines

def assignValues(lines):
    magicSquare = []
    rows = 3
    columns = 3
    for row in range(rows):
        magicSquare.append([0] * columns)
    for row in range(len(magicSquare)):
        for column in range(len(magicSquare[row])):
            magicSquare[row][column] = lines[column]
    return magicSquare

def main():
    lines = readfile(input_fname)
    matrix = assignValues(lines)
    print(matrix)

Whenever I run my code to test it, I'm getting:

[['4', '3', '8'], ['4', '3', '8'], ['4', '3', '8']]

So as you can see I am only getting the first 3 numbers into my matrix.
Finally, my question is how would I go by continuing my matrix with the following 6 numbers of the line of numbers? I'm not sure if it is something I can do in my loop, or if I am splitting my lines wrong, or am I completely on the wrong track?

Thanks.

2 Answers2

0

it only gets the first 3 column always because

magicSquare[row][column] = lines[column]

thus

def assignValues(lines):
    magicSquare = []
    rows = 3
    columns = 3
    for row in range(rows):
        magicSquare.append([0] * columns)
    for line in range((sizeof(lines)/9)) #since the input is already split this means that the size of 'lines' divided by 9 is equal to the number of rows of numbers
        for row in range(len(magicSquare)):
            for column in range(len(magicSquare[row])):
                magicSquare[row][column] = lines[(9*line)+(3*row)+column]
    return magicSquare

note that (3*row)+column will move to it 3 columns to the right every iteration and that (9*line)+(3*row)+column will move to it 9 columns (a whole row) to the right every iteration

once you get this you are now ready to process in finding out for the magic square

def testMagicSquare(matrix):
    rows = 3
    columns = 3
    for a in len(matrix)
        test1 = 0
        test2 = 0
        test3 = 0
        for b in range(3)
            if(sum(matrix[a][b])==15) test1=1 #flag true if whole row is 15 but turns false if a row is not 15
            else test1=0

            if((matrix[a][0][b]+matrix[a][1][b]+matrix[a][2][b])==15) test2=1 #flag true if column is 15 but turns false if a column is not 15
            else test2=0

            if(((matrix[a][0][0]+matrix[a][1][1]+matrix[a][2][2])==15) and 
            ((matrix[a][0][2]+matrix[a][1][1]+matrix[a][2][0])==15)) test3=1 #flag true if diagonal  is 15 but turns false if diagonal is not 15
            else test3=0

        if(test1>0 and test2>0 and test3>0) println('line ' + a + ' is a magic square')
        else println('line ' + a + ' is not a magic square')
Francis Fuerte
  • 254
  • 1
  • 9
0

To test if each row in your input file contains magic square data you need to re-organize the code slightly. I've used a different technique to Francis to fill the matrix. It might be a bit harder to understand how zip(*[iter(seq)] * size) works, but it's a very useful pattern. Please let me know if you need an explanation for it.

My code uses a list of tuples for the matrix, rather than a list of lists, but tuples are more suitable here anyway, since the data in the matrix doesn't need to be modified. Also, I convert the input data from str into int, since you need to do arithmetic on the numbers to test if matrix is a magic square.

#! /usr/bin/env python

def make_square(seq, size):
    return zip(*[iter(seq)] * size)


def main():
    fname = 'mydata'
    size = 3
    with open(fname, 'r') as f:
        for line in f:
            nums = [int(s) for s in line.split()]
            matrix = make_square(nums, size)
            print matrix
            #Now call the function to test if the data in matrix 
            #really is a magic square.
            #test_square(matrix)


if __name__ == '__main__':
    main()

Here's a modified version of make_square() that returns a list of lists instead of a list of tuples, but please bear in mind that a list of tuples is actually better than a list of lists if you don't need the mutability that lists give you.

def make_square(seq, size):
    square = zip(*[iter(seq)] * size)
    return [list(t) for t in square]

I suppose I should mention that there's actually only one possible 3 x 3 magic square that uses all the numbers from 1 to 9, not counting rotations and reflections. But I guess there's no harm in doing a brute-force demonstration of that fact. :)

Also, I have Python code that I wrote years ago (when I was first learning Python) which generates magic squares of size n x n for odd n >= 5. Let me know if you'd like to see it.


zip and iterator objects

Here's some code that briefly illustrates what the zip() and iter() functions do.

''' Fun with zip '''

numbers = [1, 2, 3, 4, 5, 6]
letters = ['a', 'b', 'c', 'd', 'e', 'f']

#Using zip to create a list of tuples containing pairs of elements of numbers & letters 
print zip(numbers, letters)

#zip works on other iterable objects, including strings
print zip(range(1, 7), 'abcdef')

#zip can handle more than 2 iterables
print zip('abc', 'def', 'ghi', 'jkl')

#zip can be used in a for loop to process two (or more) iterables simultaneously
for n, l in zip(numbers, letters):
    print n, l

#Using zip in a list comprehension to make a list of lists
print [[l, n] for n, l in zip(numbers, letters)]

#zip stops if one of the iterables runs out of elements
print [[n, l] for n, l in zip((1, 2), letters)]
print [(n, l) for n, l in zip((3, 4), letters)]

#Turning an iterable into an iterator object using the iter function
iletters = iter(letters)

#When we take some elements from an iterator object it remembers where it's up to
#so when we take more elements from it, it continues from where it left off.
print [[n, l] for n, l in zip((1, 2, 3), iletters)]
print [(n, l) for n, l in zip((4, 5), iletters)]

#This list will just contain a single tuple because there's only 1 element left in iletters
print [(n, l) for n, l in zip((6, 7), iletters)]

#Rebuild the iletters iterator object 
iletters = iter('abcdefghijkl')

#See what happens when we zip multiple copies of the same iterator object. 
print zip(iletters, iletters, iletters)

#It can be convenient to put multiple copies of an iterator object into a list
iletters = iter('abcdefghijkl')
gang = [iletters] * 3

#The gang consists of 3 references to the same iterator object 
print gang

#We can pass each iterator in the gang to zip as a separate argument 
#by using the "splat" syntax
print zip(*gang)

#A more compact way of doing the same thing: 
print zip(* [iter('abcdefghijkl')]*3)

Here's the same code running in the interactive interpreter so you can easily see the output of each statement.

>>> numbers = [1, 2, 3, 4, 5, 6]
>>> letters = ['a', 'b', 'c', 'd', 'e', 'f']
>>> 
>>> #Using zip to create a list of tuples containing pairs of elements of numbers & letters 
... print zip(numbers, letters)
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'), (6, 'f')]
>>> 
>>> #zip works on other iterable objects, including strings
... print zip(range(1, 7), 'abcdef')
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'), (6, 'f')]
>>> 
>>> #zip can handle more than 2 iterables
... print zip('abc', 'def', 'ghi', 'jkl')
[('a', 'd', 'g', 'j'), ('b', 'e', 'h', 'k'), ('c', 'f', 'i', 'l')]
>>> 
>>> #zip can be used in a for loop to process two (or more) iterables simultaneously
... for n, l in zip(numbers, letters):
...     print n, l
... 
1 a
2 b
3 c
4 d
5 e
6 f
>>> #Using zip in a list comprehension to make a list of lists
... print [[l, n] for n, l in zip(numbers, letters)]
[['a', 1], ['b', 2], ['c', 3], ['d', 4], ['e', 5], ['f', 6]]
>>> 
>>> #zip stops if one of the iterables runs out of elements
... print [[n, l] for n, l in zip((1, 2), letters)]
[[1, 'a'], [2, 'b']]
>>> print [(n, l) for n, l in zip((3, 4), letters)]
[(3, 'a'), (4, 'b')]
>>> 
>>> #Turning an iterable into an iterator object using using the iter function
... iletters = iter(letters)
>>> 
>>> #When we take some elements from an iterator object it remembers where it's up to
... #so when we take more elements from it, it continues from where it left off.
... print [[n, l] for n, l in zip((1, 2, 3), iletters)]
[[1, 'a'], [2, 'b'], [3, 'c']]
>>> print [(n, l) for n, l in zip((4, 5), iletters)]
[(4, 'd'), (5, 'e')]
>>> 
>>> #This list will just contain a single tuple because there's only 1 element left in iletters
... print [(n, l) for n, l in zip((6, 7), iletters)]
[(6, 'f')]
>>> 
>>> #Rebuild the iletters iterator object 
... iletters = iter('abcdefghijkl')
>>> 
>>> #See what happens when we zip multiple copies of the same iterator object. 
... print zip(iletters, iletters, iletters)
[('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i'), ('j', 'k', 'l')]
>>> 
>>> #It can be convenient to put multiple copies of an iterator object into a list
... iletters = iter('abcdefghijkl')
>>> gang = [iletters] * 3
>>> 
>>> #The gang consists of 3 references to the same iterator object 
... print gang
[<iterator object at 0xb737eb8c>, <iterator object at 0xb737eb8c>, <iterator object at 0xb737eb8c>]
>>> 
>>> #We can pass each iterator in the gang to zip as a separate argument 
... #by using the "splat" syntax
... print zip(*gang)
[('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i'), ('j', 'k', 'l')]
>>> 
>>> #A more compact way of doing the same thing: 
... print zip(* [iter('abcdefghijkl')]*3)
[('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i'), ('j', 'k', 'l')]
>>> 
PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • I actually need to use two-dimensional lists to solve it. No tuples. – Bobby Tango Jan 30 '15 at 15:40
  • Why no tuples? They're actually more efficient than lists, and should be used when you don't need the mutability of lists. But it's pretty easy to convert a list of tuples to a list of lists; I'll add some code to my answer. – PM 2Ring Jan 31 '15 at 04:38
  • Thank you so much. And I think I will take you up on that explanation for the making square function. How does that return zip work? Also why the if statement to call main? – Bobby Tango Feb 02 '15 at 15:29
  • I'll add a little zip & iterator object tutorial to my answer. As for the `if` statement to call `main()`, that's fairly standard Python practice. It's not really needed here, but it's a good habit to get into, as it makes it easier to turn your scripts into modules. See http://stackoverflow.com/questions/4041238/why-use-def-main for details. – PM 2Ring Feb 03 '15 at 01:35