How to use numpy/python to make a "chain reaction"? - python

I am trying to implement a "chain reaction". For example, for a 3x3 matrix all elements are initialized to zero, then one element (randomly) is assigned 1. A first iteration occurs and all the elements neighbouring 1 (up/down/left/right) gets changed from 0 -> 1. Now second iteration occurs and the process continues till all the elements in a(1) column become 1.
I have thought of using nested loops (for, for if, if elif..) but the code becomes kind of ugly (imo) and time consuming. I was wondering if this can be done with any other approach.
My Approach: Initialize with the help of np.zeros, then randomly assign any one element to 1.
import numpy as np
from random import randint, choice
import time
n = int(input("Enter number of modes(n):"))
start = time.time()
m1 = [i+1 for i in range(n)]
print(m1)
# initializing
m = []
ones = [1,1,1,1,1]
for i in range(n):
a = np.zeros(n)
m.append(a)
m1 = choice(m)
A = randint(0, len(m1)-1)
B = randint(0, len(ones)-1)
m1[A]=ones[B]
# game begins here:
print("----Initializing----")
print("1st iteration")
print(np.array(m))

One approach is to use np.argwhere() to get the indices of nonzero elements at each iteration. That way you don't need to go through the entire matrix at each iteration, but you will need to do some if-else checking to see if you're on a boundary.
import numpy as np
def flood_fill_naive(n):
A = np.zeros((n,n)) # Initialize an array of zeros.
snapshots = [] # Copies of the matrix after each change.
def fill_entry(i,j):
"""Set A[i,j] = 1 if (i,j) is a valid index pair for A."""
if 0 <= i < A.shape[0] and 0 <= j < A.shape[1]:
A[i,j] = 1
snapshots.append(A.copy())
# Fill in a single random entry to start.
i,j = np.random.randint(A.shape)
fill_entry(i,j)
# Do the reaction until all entries are 1.
while np.count_nonzero(A) != A.size:
# Find the indices of nonzero elements.
for i,j in np.argwhere(A):
# Set 0 -> 1 on neighbors, except at boundaries.
fill_entry(i-1,j) # Up
fill_entry(i,j-1) # Left
fill_entry(i,j+1) # Right
fill_entry(i+1,j) # Down
return snapshots
if __name__ == "__main__":
n = int(input("Enter integer size of matrix: "))
for snap in flood_fill_naive(n):
print(snap)
But notice that this approach does a lot of repeat work. For example, the initial 1 only updates its neighbors once, and then you can forget about it. So at each step, you really only need to remember the spaces that were just updated, since they are the only ones that potentially have neighbors that are 0.
0 0 0 0 0 0 0 1 0 0 0 1 x 1 0 1 x x x 1
0 0 1 0 0 0 1 x 1 0 1 x x x 1 x x x x x
0 0 0 0 0 --> 0 0 1 0 0 --> 0 1 x 1 0 --> 1 x x x 1 --> ...
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 x 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
So let's just try to keep track of the indices of the "current 1s" and "previous 1s" (marked as x's above). In fact, what we're doing here is a breadth-first search. See BFS and Flood Fill on Wikipedia.
def flood_fill_bfs(n):
A = np.zeros((n,n)) # Initialize an array of zeros.
snapshots = [] # Copies of the matrix after each change.
visit_queue = [] # List of indices to visit in the future.
visited = set() # Set of indices already visited.
def fill_entry(i,j):
"""If (i,j) has not been visited, visit it by setting A[i,j] = 1
and adding it to the visit queue.
"""
# Do nothing if this index has been visited.
if (i,j) in visited:
return
# If the index has not been visited but is valid,
if 0 <= i < A.shape[0] and 0 <= j < A.shape[1]:
A[i,j] = 1
visited.add((i,j))
visit_queue.append((i,j))
snapshots.append(A.copy())
# Fill in a single random entry to start.
i,j = np.random.randint(A.shape)
fill_entry(i,j)
# Fill in neighbors of 1's until all entries are 1.
while np.count_nonzero(A) != A.size:
i,j = visit_queue.pop(0)
# Set 0 -> 1 on neighbors, except at boundaries.
fill_entry(i-1,j) # Up
fill_entry(i,j-1) # Left
fill_entry(i,j+1) # Right
fill_entry(i+1,j) # Down
return snapshots
if __name__ == "__main__":
n = int(input("Enter integer size of matrix: "))
for snap in flood_fill_bfs(n):
print(snap)
You can guess which routine will be faster:
In [1]: %time _ = flood_fill_naive(100)
CPU times: user 6.21 s, sys: 258 ms, total: 6.47 s
Wall time: 6.49 s
In [2]: %time _ = flood_fill_bfs(100)
CPU times: user 709 ms, sys: 310 ms, total: 1.02 s
Wall time: 1.03 s

Related

How to change the array elements according specific condition

I have an array for an example:
import numpy as np
data=np.array([[4,4,4,0,1,1,1,0,0,0,0,1,0,0,1],
[3,0,0,1,1,1,1,1,1,1,1,0,0,1,0],
[6,0,0,1,1,1,1,1,0,0,0,0,1,0,0]])
Requirement :
In the data array, if element 1's are consecutive as the square size
of ((3,3)) and more than square size no changes. Otherwise, replace
element value 1 with zero except the square size.
Expected output :
[[4 4 4 0 1 1 1 0 0 0 0 0 0 0 0]
[3 0 0 0 1 1 1 0 0 0 0 0 0 0 0]
[6 0 0 0 1 1 1 0 0 0 0 0 0 0 0]]
I will provide here as solutions two different approaches. One which doesn't and one which is using Python loops. Let's start with the common header:
import numpy as np
from skimage.util import view_as_windows as winview
data=np.array([[4,4,4,0,1,1,1,0,0,0,0,1,0,0,1],
[3,0,0,1,1,1,1,1,1,1,1,0,0,1,0],
[6,0,0,1,1,1,1,1,0,0,0,0,1,0,0]])
Below an approach without using Python loops resulting in shortest code, but requiring import of an additional module skimage:
clmn = np.where(np.all(winview(data,(3,3))[0],axis=(1,2)))[0][0]
data[data == 1] = 0 # set all ONEs to zero
data[0:3,clmn+3:] = 0 # set after match to zero
data[0:3,clmn:clmn+3] = 1 # restore ONEs
Another one is using Python loops and only two lines longer:
for clmn in range(0,data.shape[1]):
if np.all(data[0:3,clmn:clmn+3]):
data[data==1] = 0
data[0:3,clmn+3:] = 0
data[0:3,clmn:clmn+3] = 1
break
Instead of explaining how the above code using loops works I have put the 'explanations' into the names of the used variables so the code becomes hopefully self-explaining. With this explanations and some redundant code you can use the code below for another shaped haystack to search for in another array of same kind. For an array with more rows as the shape of the sub-array there will be necessary to loop also over the rows and optimize the code skipping some unnecessary checks.
import numpy as np
data=np.array([[4,4,4,0,1,1,1,0,0,0,0,1,0,0,1],
[3,0,0,1,1,1,1,1,1,1,1,0,0,1,0],
[6,0,0,1,1,1,1,1,0,0,0,0,1,0,0]])
indx_of_clmns_in_shape = 1
indx_of_rows_in_shape = 0
subarr_shape = (3, 3)
first_row = 0
first_clmn = 0
for clmn in range(first_clmn,data.shape[indx_of_clmns_in_shape],1):
sub_data = data[
first_row:first_row+subarr_shape[indx_of_rows_in_shape],
clmn:clmn+subarr_shape[indx_of_clmns_in_shape]]
if np.all(sub_data):
data[data == 1] = 0
data[first_row : subarr_shape[indx_of_rows_in_shape],
clmn+subarr_shape[indx_of_clmns_in_shape] : ] = 0
data[first_row : subarr_shape[indx_of_rows_in_shape],
clmn : clmn+subarr_shape[indx_of_clmns_in_shape]] = 1
break
# print(sub_data)
print(data)
all three versions of the code give the same result:
[[4 4 4 0 1 1 1 0 0 0 0 0 0 0 0]
[3 0 0 0 1 1 1 0 0 0 0 0 0 0 0]
[6 0 0 0 1 1 1 0 0 0 0 0 0 0 0]]
Should be easy to do with a double for loop and a second array
rows = len(source_array)
columns = len(source_array[0])
# Create a result array of same size
result_array = [[0 for _ in range(rows)] for _ in range(columns)]
for i in range(rows):
for j in range(columns):
# Copy non 1s
if source_array[i][j] != 1:
result_array[i][j] = source_array[i][j]
# if enough rows left to check then check
if i < rows - 3:
if j < columns - 3:
# Create set on the selected partition
elements = set(source_array[i][j:j+3] + source_array[i+1][j:j+3] + source_array[i+2][j:j+3])
# Copy 1s to new array
if len(elements) == 1 and 1 in elements:
for sq_i in range(i,i+3):
for sq_j in range(j,j+3):
result_array[sq_i][sq_j] = 1

How to find figures with even areas

We have shape with n angles and its coordinates. I need to divide that figure into 2 figures with the same area. Division is performed in parallel to the axis y. The task is to find such x-coordinate, which would comply with the terms.
Test,E.g
4 (n angles)
0 0
0 2
2 2
2 0
The output must be 1
I wrote
n = int(input())
coordinates = []
for i in range(n):
x,y = map(int,input().split())
coordinates.append([x,y])
s = 0
for i in range(len(coordinates)-1):
s += coordinates[i][0]*coordinates[i+1][1]
s -= coordinates[i][1]*coordinates[i+1][0]
s = s/2
x = 0
print(s)
And after that I don't have any ideas what to do next..

changing the boolean values of an array according to a formula for the indices

I want to create a 64 components array showing all the squares in which the two rooks of an empty chessboard could move from their current position. So far I am doing it with for and while loops.
I first create a function just to better visualize the board:
import numpy as np
def from_array_to_matrix(v):
m=np.zeros((8,8)).astype('int')
for row in range(8):
for column in range(8):
m[row,column]=v[row*8+column]
return m
and here I show how I actually build the array:
# positions of the two rooks
a=np.zeros(64).astype('int')
a[15] = 1
a[25] = 1
print from_array_to_matrix(a)
# attack_a will be all the squares where they could move in the empty board
attack_a=np.zeros(64).astype('int')
for piece in np.where(a)[0]:
j=0
square=piece+j*8
while square<64:
attack_a[square]=1
j+=1
square=piece+j*8
j=0
square=piece-j*8
while square>=0:
attack_a[square]=1
j+=1
square=piece-j*8
j=0
square=piece+j
while square<8*(1+piece//8):
attack_a[square]=1
j+=1
square=piece+j
j=0
square=piece-j
while square>=8*(piece//8):
attack_a[square]=1
j+=1
square=piece-j
print attack_a
print from_array_to_matrix(attack_a)
I have been advised to avoid for and while loops whenever it is possible to use other ways, because they tend to be time consuming. Is there any way to achieve the same result without iterating the process with for and while loops ?
Perhaps using the fact that the indices to which I want to assign the value 1 can be determined by a function.
There are a couple of different ways to do this. The simplest thing is of course to work with matrices.
But you can vectorize operations on the raveled array as well. For example, say you had a rook at position 0 <= n < 64 in the linear array. To set the row to one, use integer division:
array[8 * (n // 8):8 * (n // 8 + 1)] = True
To set the column, use modulo:
array[n % 8::8] = True
You can convert to a matrix using reshape:
matrix = array.reshape(8, 8)
And back using ravel:
array = martix.ravel()
Or reshape:
array = matrix.reshape(-1)
Setting ones in a matrix is even simpler, given a specific row 0 <= m < 8 and column 0 <= n < 8:
matrix[m, :] = matrix[:, n] = True
Now the only question is how to vectorize multiple indices simultaneously. As it happens, you can use a fancy index in one axis. I.e, the expression above can be used with an m and n containing multiple elements:
m, n = np.nonzero(matrix)
matrix[m, :] = matrix[:, n] = True
You could even play games and do this with the array, also using fancy indexing:
n = np.nonzero(array)[0]
r = np.linspace(8 * (n // 8), 8 * (n // 8 + 1), 8, False).T.ravel()
c = np.linspace(n % 8, n % 8 + 64, 8, False)
array[r] = array[c] = True
Using linspace allows you to generate multiple sequences of the same size simultaneously. Each sequence is a column, so we transpose before raveling, although this is not required.
Use reshaping to convert 1-D array to 8x8 2-D matrix and then numpy advance indexing to select rows and columns to set to 1:
import numpy as np
def from_array_to_matrix(v):
return v.reshape(8,8)
# positions of the two rooks
a=np.zeros(64).astype('int')
a[15] = 1
a[25] = 1
a = from_array_to_matrix(a)
# attack_a will be all the squares where they could move in the empty board
attack_a=np.zeros(64).astype('int')
attack_a = from_array_to_matrix(attack_a)
#these two lines replace your for and while loops
attack_a[np.where(a)[0],:] = 1
attack_a[:,np.where(a)[1]] = 1
output:
a:
[[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 1]
[0 0 0 0 0 0 0 0]
[0 1 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 0 0 0 0 0]
[0 0 0 0 0 0 0 0]]
attack_a:
[[0 1 0 0 0 0 0 1]
[1 1 1 1 1 1 1 1]
[0 1 0 0 0 0 0 1]
[1 1 1 1 1 1 1 1]
[0 1 0 0 0 0 0 1]
[0 1 0 0 0 0 0 1]
[0 1 0 0 0 0 0 1]
[0 1 0 0 0 0 0 1]]

How can I recursively divide the grid in my maze?

I am trying to create a maze generator using recursive division. I use this link:
Maze generation - recursive division (how it works?) as my guide as to how to approach the problem.
Here is my code so far:
import random
# Maze: 0 - N : 4 x 4 Grid
# Grid: 0 - (2n + 1) : 9 x 9 Array
# TODO: Now, Find a way to save the previous walls and not just only one at a time
rows = 9
cols = 9
start = 2
end = 7
# -------------------------------------------------------------------------------------
# Lists for all even / odd numbers in given range
evens = [n for n in range(start, end+1) if n % 2 == 0]
odds = [m for m in range(start, end+1) if m % 2 != 0]
# Generate random even/odd integer value for walls/ passages respectively
# Walls: Not sure if 2 variables are necessary-----------------------------------------
wallX = random.choice(evens)
wallY = random.choice(evens)
# Passages
passageX = random.choice(odds)
passageY = random.choice(odds)
#--------------------------------------------------------------------------------------
# Random direction: True = Horizontal Slice, False = Vertical Slice
randomDirection = random.choice([True, False])
arr = [['0' for i in range(cols)] for j in range(rows)]
def displayBoard(arr):
print()
for i in range(len(arr)):
for j in range(len(arr[i])):
# Print just the edges
if i == 0 or i == 8 or j == 0 or j == 8:
print('*', end = ' ')
# Print wall
elif arr[i][j] == 1:
print('.', end = ' ')
else:
print (' ', end = ' ')
print()
# Function choose direction to slice
def chooseDir(arr):
for i in range(len(arr)):
for j in range(len(arr[i])):
# Horizontal Direction Slice
if randomDirection:
arr[wallX][j] = 1
arr[wallX][passageY] = 2
print(arr[i][j], end = ' ')
# Vertical Slice
else:
arr[i][wallY] = 1
arr[passageX][wallY] = 2
print(arr[i][j], end = ' ')
print()
displayBoard(arr)
print()
mazeX = 0
mazeY = 0
# Write the recursive division function:
def divide():
chooseDir(arr)
print()
divide()
What this produces is a grid that is randomly sliced at an even index (creating walls) and creates passages at odd indices.
Output: 1 = wall, 2 = passage made
0 0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0 0
0 0 0 0 2 0 0 0 0
0 0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0 0
* * * * * * * * *
* . *
* . *
* *
* . *
* . *
* . *
* . *
* * * * * * * * *
My issue is that I don't know how to write my recursive function. Do I call division on the two new cells created when a wall is made and continuously divide the "sub cells".
Or I know that a 4 x 4 cell grid will provide an array of 9 x 9 and I will have 16 cells total.
Then I can call division until a certain condition is met, increment to the next cell until all 16 were visited.
In both cases, I am not sure how to represent the new walls/cells created so that I can write the division function. Up until now, I've been using the grid coordinates.
You asked "Do I call division on the two new cells created when a wall is made and continuously divide the "sub cells"". Yes, that is the essence of recursion. Take a big problem and make smaller problem(s). Repeat with the smaller problems. Eventually, the problems are small enough to easily solve. Then put all the small problems back together to solve the original problem.
For the maze, the first wall split the maze into two smaller mazes. Use the same algorithm to split each of them and there are not 4 smaller mazes. Repeat until the sub-mazes are too small to split any more.
Your code that splits the maze should go in a function. If the sub-maze is big enough to split, the function splits the sub-maze and then calls itself on the two smaller sub-mazes.

Gernerate all the possible undirected graphs

What is an efficient solution to generate all the possible graphs using an incidence matrix?
The problems is equivalent of generating all the possible binary triangular matrix.
My first idea was to use python with itertools. For instance, for generating all the possibile 4x4 matrix
for b in itertools.combinations_with_replacement((0,1), n-3):
b_1=[i for i in b]
for c in itertools.combinations_with_replacement((0,1), n-2):
c_1=[i for i in c]
for d in itertools.combinations_with_replacement((0,1), n-1):
d_1=[i for i in d]
and then you create the matrix adding the respective number of zeroes..
But this is not correct since we skip some graphs...
So, any ideas?
Perhaps i can use the isomorphism between R^n matrix and R^(n*n) vector, and generate all the possibile vector of 0 and 1, and then cut it into my matrix, but i think there's a more efficient solutions.
Thank you
I add the matlab tab because it's a problem you can have in numerical analysis and matlab.
I assume you want lower triangular matrices, and that the diagonal needs not be zero. The code can be easily modified if that's not the case.
n = 4; %// matrix size
vals = dec2bin(0:2^(n*(n+1)/2)-1)-'0'; %// each row of `vals` codes a matrix
mask = tril(reshape(1:n^2, n, n))>0; %// decoding mask
for v = vals.' %'// `for` picks one column each time
matrix = zeros(n); %// initiallize to zeros
matrix(mask) = v; %// decode into matrix
disp(matrix) %// Do something with `matrix`
end
Each iteration gives one possible matrix. For example, the first matrices for n=4 are
matrix =
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
matrix =
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 1
matrix =
0 0 0 0
0 0 0 0
0 0 0 0
0 0 1 0
matrix =
0 0 0 0
0 0 0 0
0 0 0 0
0 0 1 1
Here is an example solution using numpy that generates all simple graphs:
It first generates the indices of the upper triangular part iu. The loop converts the number k to it's binary representation and then assigns it to the upper triangular part G[iu].
import numpy as np
n = 4
iu = np.triu_indices(n,1) # Start at first minor diagonal
G = np.zeros([n,n])
def dec2bin(k, bitlength=0):
return [1 if digit=='1' else 0 for digit in bin(k)[2:].zfill(bitlength)]
for k in range(0,2**(iu[0].size)):
G[iu] = dec2bin(k, iu[0].size)
print(G)

Categories

Resources