1

I am trying to implement a simple multiplication of two 2*2 matrices. And I came across the following code.

def ikjMatrixProduct(A, B):
    n = len(A)
    C = [[0 for i in range(n)] for j in range(n)]
    for i in range(n):
        for k in range(n):
            for j in range(n):
                C[i][j] += A[i][k] * B[k][j]
    return C

Why do we use this assignment : C = [[0 for i in range(n)] for j in range(n)] in line 3

I've never seen that assignment before. Can please someone explain this.

My own code was throwing an error because i used c = [] and this code worked.

PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
pi2018
  • 347
  • 2
  • 11
  • 2
    The line `C = [[0 for i in range(n)] for j in range(n)]` creates an n by n matrix filled with zeros. This is needed, since in the line `C[i][j] += A[i][k] * B[k][j]` the elements of `C` are accessed. If the matrix would not have been initialized, you would get an error here. By the way: please have a look at `numpy` and please do look into your code again. It seems unnecessary complex. – Merlin1896 Jul 24 '18 at 19:29
  • 1
    Another way of doing that is `np.zeros((n,n))` – W Stokvis Jul 24 '18 at 19:30
  • 1
    Also note that you really need some kind of double loop in order to initialize a zero matrix using python lists. One might think that it's OK to use list multiplication to create a 2x2 nested list of lists, but if you do that you can end up with multiple references to the same list for the two rows, and then mutating an item in your matrix would change other items as well. See [this](https://stackoverflow.com/questions/240178/list-of-lists-changes-reflected-across-sublists-unexpectedly) for what I mean. – Andras Deak -- Слава Україні Jul 24 '18 at 19:40
  • 2
    @Merlin1896, W Stokvis: Our professor wanted us to write the code without using any libraries. Hence I didn't use numpy. Andras Deak, the example helped! – pi2018 Jul 24 '18 at 19:55
  • BTW, when creating multidimensional lists it's safe to use list multiplication on the innermost list when the items are immutable. So you could do, e.g. `C = [[0] * n for j in range(n)]`. That's a bit faster than a double Python loop because the `[0]*n` is done at C speed. – PM 2Ring Jul 25 '18 at 05:04

1 Answers1

1

As Merlin1896 said, C = [[0 for i in range(n)] for j in range(n)] creates a 2D list, that is, a list of n lists, with each inner list containing n zeros. A list is empty until you fill it with something. You can't do C[i][j] += A[i][k] * B[k][j] if C is an empty list, since the C[i][j] don't exist yet.

A quicker way to initialize your matrix is

C = [[0] * n for j in range(n)]

It's safe to use list multiplication on the innermost lists, since they contain integers, which are immutable. But it's not safe on the outer lists because lists are mutable. Doing C = [[0] * n] * n makes the outer lists references to the one list object. Eg,

n = 4
C = [[0] * n] * n
print(C)
C[0][0] = 1
print(C)

output

[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
[[1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]]

However, it's better if we reorganize the code so that we don't have to pre-fill the lists with zeros. We can do that by changing the loop order, making the k loop the innermost. Then we can use the built-in sum function with a generator expression.

def ikjMatrixProduct(A, B):
    n = len(A)
    C = []
    for i in range(n):
        row = []
        for j in range(n):
            row.append(sum(A[i][k] * B[k][j] for k in range(n)))
        C.append(row)
    return C

We could turn that into a list comprehension, although many would argue that a triply-nested list comprehension / generator expression is not very readable. ;)

def ikjMatrixProduct(A, B):
    nrange = range(len(A))
    return [[sum(A[i][k] * B[k][j] for k in nrange) for j in nrange] for i in nrange]
PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • Thanks for explanation. I particularly liked the very last example of the triply-nested list-comprehension! I had a question regarding this code. n = 4 C = [[0] * n] * n print(C). This outputs a 4*1 list of [0,0,0,0]. I don't get the following part we have said that C[0][0] = 1, but the second row doesn't yet exists. so why isn't the code throwing an error? Also how do we get a second row full of [1,0,0,0]?? C[0][0] = 1 print(C) – pi2018 Jul 25 '18 at 16:04
  • Please disregard the previous comment. I have reframed my question as below: I had a question regarding this code. n = 4; C = [[0] * n] * n; print(C);. This outputs four 4*1 lists of [0,0,0,0]. I don't get the following part we have said that C[0 ][0] = 1, how does the "1" get broadcasted to the entire row and we get an output of [1,0,0,0] for all the four 4*1 lists? Isn't C[0][0] mean that first element of the first list?? C[0][0] = 1; print(C). – pi2018 Jul 25 '18 at 16:13
  • @pi2018 In that code, `C` contains 4 references to a single list object. It does *not* contain 4 different lists. So `C[0]`, `C[1]`, `C[2]`, and `C[3]` are just different names for the same list. – PM 2Ring Jul 25 '18 at 21:13