I am trying to fill up my matrix with zeros. Unfortunatelly, in the following example, variables x and y are redundand:
self.matrix = [[0 for x in range(0, self.N)] for y in range(0, self.N)]
Multiplying list, copy only references what of course is not what I am expecting:
>>> matrix = [[0] * 5] * 5
>>> matrix
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
>>> matrix[1][1] = "X"
>>> matrix
[[0, 'X', 0, 0, 0], [0, 'X', 0, 0, 0], [0, 'X', 0, 0, 0], [0, 'X', 0, 0, 0], [0, 'X', 0, 0, 0]]
So, is there any solution using a list comprehension to avoid redundand variables (x & y)?
The common idiom is to assign the result to the variable _, which signals a possible reader of your code that the value will not be used:
[[0]*self.N for _ in range(self.N)]
As you see, we can use the [0]*size idiom for the inner list because 0 is an immutable value. Also, you can call range with only one argument, in which case it is treated as the upper bound, with 0 being the lower bound).
If you want, you can build yourself a list build helper that supports creating lists of arbitrary nesting depth:
def make_multi_list(dim, func):
if not dim: return func()
return [make_multi_list(dim[1:], func) for _ in range(dim[0])]
Usage:
>>> make_multi_list((2, 2), lambda: 0)
[[0, 0], [0, 0]]
>>> make_multi_list((3, 2, 1), lambda: 0)
[[[0], [0]], [[0], [0]], [[0], [0]]]
Alternatively, you could do:
from itertools import repeat
self.matrix = [list(repeat(0, self.N)) for _ in range(0, self.N)]
Related
I want for a number n for there to be a nested list containing sublists that start off at length n and decrease to length one. If n was 4, the list would be:
[[0, 0, 0, 0], [0, 0, 0], [0, 0,], [0]]
Here's what I've tried:
triangle = []
for i in range(n):
for k in range(i):
triangle.append(0)
That just gave out for input n = 5:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
If you want to mantain your code form, you just use this function:
def get_triangle_list(n):
triangle_list = []
for i in range(n, 0, -1):
tmp_list = []
for k in range(i):
tmp_list.append(0)
triangle_list.append(tmp_list)
return triangle_list
print(get_triangle_list(4))
and output will be:
[[0, 0, 0, 0], [0, 0, 0], [0, 0], [0]]
But, as suggested by #Abhijit Sarkar, you can obtain the same output in smarter and more efficent way using list comprehension like
triangles = [[0] * x for x in range(n, 0, -1)]
n = 4
triangles = [[0] * x for x in range(n, 0, -1)]
print(triangles)
The follow codes didn't behave like I expected. Is this an incorrect way of initializing a 2D list filled with 0?
matrix = [[0] * 4] * 4
for row in matrix:
print(row)
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
matrix[0][0] = 1
for row in matrix:
print(row)
[1, 0, 0, 0]
[1, 0, 0, 0]
[1, 0, 0, 0]
[1, 0, 0, 0]
Use matrix = [[0 for i in range(4)] for j in range(4)] instead of matrix = [[0] * 4] * 4.
matrix = [[0 for i in range(4)] for j in range(4)]
matrix[0][0] = 1
for row in matrix:
print(row)
Output:
[1, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
I actually ran into this problem a while ago! No, this isn't the correct way, at least for what you're expecting to happen.
The problem is that when you initialise this list, you create a list of references back to the first item, so when you modify it, you modify all of them, because in reality they all point to the same object in memory.
Instead of that you can do something like this:
x = 4
y = 4
matrix = [[0]*x for _ in range(y)]
With a result of:
[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]
Then matrix[0][0] = 1 only sets the first element of the first list to 1.
You can extend this into 3D and beyond by simply adding a new layer of for __ in range(z) on the end and and wrapping it in more square brackets.
That is because the matrix is composed of a copy of the list [0,0,0,0].
This should work:
matrix = [[0 for i in range(4)] for j in range(4)]
matrix[0][0] = 1
In the general case you can make something like numpy.full (but for lists instead of arrays):
def full(shape, fill_value=0):
if len(shape) > 1:
return [full(shape[1:], fill_value) for _ in range(shape[0])]
else:
return [fill_value] * shape[0]
matrix = full((4, 4), 0)
I want to create an adjacency matrix for a graph without using any libraries. The problem is the size of the graph is not static and it increases over time. So I can not use a command like:
adj = [ [None for i in range(5)] for j in range(4) ]
and for example, assign 4 to element (1,1):
adj[1][1]=4
I have created an empty list called adj:
adj = []
now each time a node is added to the graph I add a list to adj:
adj.append([])
How can I assign a value to a specific element of the matrix, as I did with q[1][1]=4 when the size was fixed?
You can start with any default size and then use extend to add elements to a sublist, or add a new sublist.
adj = [ [0 for i in range(2)] for j in range(2) ]
print(adj)
#[[0, 0], [0, 0]]
adj[0].extend([1, 1])
#[[0, 0, 1, 1], [0, 0]]
print(adj)
adj.extend([[0, 0, 0, 0, 0]])
print(adj)
#[[0, 0, 1, 1], [0, 0], [0, 0, 0, 0, 0]]
Once you resize your list based on requirement, then you can assign your elements.
adj[0][0] = 2
adj[1][1] = 3
adj[2][2] = 4
print(adj)
#[[2, 0, 1, 1], [0, 3], [0, 0, 4, 0, 0]]
I've been given the pseudo-code:
for i= 1 to 3
for j = 1 to 3
board [i] [j] = 0
next j
next i
How would I create this in python?
(The idea is to create a 3 by 3 array with all of the elements set to 0 using a for loop).
If you really want to use for-loops:
>>> board = []
>>> for i in range(3):
... board.append([])
... for j in range(3):
... board[i].append(0)
...
>>> board
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
But Python makes this easier for you:
>>> board = [[0]*3 for _ in range(3)]
>>> board
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
arr=[[0,0,0] for i in range(3)] # create a list with 3 sublists containing [0,0,0]
arr
Out[1]: [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
If you want an list with 5 sublists containing 4 0's:
In [29]: arr=[[0,0,0,0] for i in range(5)]
In [30]: arr
Out[30]:
[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]
The range specifies how many sublists you want, ranges start at 0, so ranges 4 is 0,1,2,3,4.
gives you five [0,0,0,0]
Using the list comprehension is the same as:
arr=[]
for i in range(5):
arr.append([0,0,0,0])
arr
[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]]
If you want something closer to your pseudocode:
board = []
for i in range(3):
board.append([])
for j in range(3):
board[i].append(0)
numpy has something for this:
numpy.zeros((3,3))
You can use the style of pseudocode given or simply just use a python one liner
chess_board = [[x]*3 for _ in range(y)] --> list comprehension
or you can use the plain loop style of other languages like java. I prefer the one liner as it looks much nicer and cleaner.
What is wrong with the following program code, attempting to initialize a 4 x 4 matrix of integers? How should the initialization be done?
line = [0] * 4
matrix = [line, line, line, line]
Use a list comprehension:
>>> line = [[0]*4 for _ in xrange(4)]
>>> line
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
Don't do this though:
>>> line = [[0]*4]*4
>>> line
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
The output looks same, but the problem here is all inner lists are actually the same object repeated 4 times:
>>> [id(x) for x in line]
[156931756, 156931756, 156931756, 156931756]
So, changing one of them is going to affect all:
>>> line[2][0] = 10
>>> line
[[10, 0, 0, 0], [10, 0, 0, 0], [10, 0, 0, 0], [10, 0, 0, 0]]
Same thing is applicable to your code:
>>> line = [0] * 4
>>> matrix = [line, line, line, line]
>>> [id(x) for x in matrix]
[158521804, 158521804, 158521804, 158521804]
If line contains only immutable object then you can change your code do:
>>> matrix = [line[:] for _ in xrange(4)]
But, if line contains mutable objects itself, then you'd have to use either copy.deepcopy or better create a new line object inside the list comprehension.
you could use numpy for that if you want to perform computations on your matrix
import numpy as np
zeros = np.zeros([4,4])
The problem is:
>>> line = [0] * 4
>>> matrix = [line, line, line, line]
>>> matrix[0][0] = 5
>>> matrix
[[5, 0, 0, 0], [5, 0, 0, 0], [5, 0, 0, 0], [5, 0, 0, 0]]
You have and array of references to the same vector.
What is wrong here that you create a list of 4 references to line list. If you change any of sub-lists or line itself, you affect all sub-lists of matrix, since they (sub-lists) are essentially the same list
Here is a little demonstration
In [108]: line = [0] * 4
In [109]: matrix = [line, line, line, line]
In [110]: line[1]=2
In [111]: matrix
Out[111]: [[0, 2, 0, 0], [0, 2, 0, 0], [0, 2, 0, 0], [0, 2, 0, 0]]
In [112]: matrix[1][3] = 4
In [113]: matrix
Out[113]: [[0, 2, 0, 4], [0, 2, 0, 4], [0, 2, 0, 4], [0, 2, 0, 4]]
In [114]: for row in matrix:
.....: print id(row)
.....:
3065415660
3065415660
3065415660
3065415660
You need to do this:
matrix = [[0 for row in range(4)] for col in range(4)]
Your code works ok, but it's not flexible one. So, if you want to create 5*5 martix you have explicitly add more line objects to matrix init code. So, using for + xrange generators looks more suitable.
Also, not sure about case of using this matrix - but be aware about using the same object (list) as matrix line. So, if you change it's element - it would be modified in all rows:
matrix[0][0] = 'new value'
print matrix
[['new value', 0, 0, 0], ['new value', 0, 0, 0], ['new value', 0, 0, 0], ['new value', 0, 0, 0]]