Flood fill working only on squared matrix? - python

I'm trying to implement flood fill to find all available cells in a grid
from which my robot can move to. if a cell is occupied its value will be 1,
and if a cell is free its value will be 0. my code seems to work on squared
matrices but not on other matrices. In my code I mark the reachable cells with
the number 2.
Here is my code:
def floodfill(matrix, x, y):
if matrix[x][y] == 0:
matrix[x][y] = 2
if x > 0:
floodfill(matrix,x-1,y)
if x < len(matrix[y]) - 1:
floodfill(matrix,x+1,y)
if y > 0:
floodfill(matrix,x,y-1)
if y < len(matrix) - 1:
floodfill(matrix,x,y+1)
This matrix seems to work:
def main():
maze = [[0, 1, 1, 1, 1, 0, 0, 0, 1, 0],
[0, 1, 0, 1, 1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 1, 0, 1, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 1, 0, 1, 0, 1, 1],
[0, 1, 0, 1, 1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 1, 0, 1, 0, 1, 0],
[0, 0, 0, 1, 1, 0, 1, 0, 1, 0]]
floodfill(maze, 0,0)
print(maze)
And this matrix does not (same matrix with last column removed):
def main():
maze = [[0, 1, 1, 1, 1, 0, 0, 0, 1],
[0, 1, 0, 1, 1, 0, 1, 0, 1],
[0, 1, 0, 1, 1, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 0, 1, 0, 1],
[0, 1, 0, 1, 1, 0, 1, 0, 1],
[0, 1, 0, 1, 1, 0, 1, 0, 1],
[0, 1, 0, 1, 1, 0, 1, 0, 1],
[0, 1, 0, 1, 1, 0, 1, 0, 1],
[0, 1, 0, 1, 1, 0, 1, 0, 1],
[0, 0, 0, 1, 1, 0, 1, 0, 1]]
floodfill(maze, 0,0)
print(maze)
Would appreciate your help.
Thanks!

Your first matrix works because it is a square matrix where the number of rows and the numbers of columns are equal = 10.
In your second case, your matrix is not a square matrix because you have 10 rows (x variable) but only 9 columns (y variable). Hence, when you do
y < len(matrix) - 1
len(matrix) is 10 which means you are going up to y < 9. Otherwise, you will get "List Index Out of Range Error". To get the correct numbers, you should check against the length of your rows which gives you the number of columns. One way is to use the length of first row as len(matrix[0]).
Similarly, for the x you should use the corresponding number of rows which can be accessed using len(matrix) which is 10 in your case. So, you should use
if x < len(matrix) - 1
instead of if x < len(matrix[y]) - 1: as juvian also pointed it out in the comments.
Other way is to convert your list of lists to a NumPy array and use the shape command to get the corresponding number of rows and columns.

When accessing elements in the matrix, the row index comes first (the matrix is an array of rows), followed by the column index (each row is an array of numbers).
You want matrix[y][x], not matrix[x][y].

Related

Convert OR Tools IntervalVars into Binary List

Good Morning,
I'm trying to find a way to convert IntervalVars into a List of BooleanVars with or tools in order to use the binary list as a sort of field in which the booleans indicate where the Interval is located in the window between time 0 and horizon.
I've got it working with just one Interval, i.e. when i print all Solutions it shows the interval in all possible positions:
all combination/solutions:
[
[0, 0, 1, 1, 0],
[0, 1, 1, 0, 0],
[0, 0, 0, 1, 1],
[1, 1, 0, 0, 0]
]
working model:
duration = 2
horizon = 5
model = cp_model.CpModel()
bool_vars = []
start_var = model.NewIntVar(0, horizon, "start")
interval_var = model.NewFixedSizeIntervalVar(start_var, duration, "interval")
for i in range(horizon):
bool_var = model.NewBoolVar(f"bool_{i}")
model.Add(interval_var.StartExpr() <= i).OnlyEnforceIf(bool_var)
model.Add(interval_var.EndExpr() > i).OnlyEnforceIf(bool_var)
bool_vars.append(bool_var)
model.Add(sum(bool_vars) == duration)
Now if i try to extend this method to two intervals it doesn't work.
durations = [1,2]
horizon = 5
model = cp_model.CpModel()
bool_vars = [model.NewBoolVar(f"bool_{i}") for i in range(horizon)]
for duration in durations:
start_var = model.NewIntVar(0, horizon, f"start_{duration}")
interval_var = model.NewFixedSizeIntervalVar(start_var, duration, f"interval_{duration}")
for i in range(horizon):
model.Add(interval_var.StartExpr() <= i).OnlyEnforceIf(bool_vars[i])
model.Add(interval_var.EndExpr() > i).OnlyEnforceIf(bool_vars[i])
model.Add(sum(bool_vars) == sum(durations))
I suspect its the lines
model.Add(interval_var.StartExpr() <= i).OnlyEnforceIf(bool_var)
model.Add(interval_var.EndExpr() > i).OnlyEnforceIf(bool_var)
I know theres something fundamental which I'm missing but I cant think of another solution at the moment
Any input and criticism is appreciated! <3
The concrete Problem I'm trying to solve in this current step is:
Modify the current method (utilizing Google OR Tools) such that: given a list of durations and a horizon, find all possible combinations. With the constraint that sequences are separated by a gap/pause with duration_of_gap >= 1.
Example 1:
inputs:
durations = [1,2]
horizon = 5
solution:
combinations = [
[1, 0, 1, 1, 0],
[1, 0, 0, 1, 1],
[0, 1, 0, 1, 1],
]
Example 2:
inputs:
durations = [3,1,2]
horizon = 10
solution:
combinations = [
[1, 1, 1, 0, 1, 0, 1, 1, 0, 0],
[1, 1, 1, 0, 1, 0, 0, 1, 1, 0],
[1, 1, 1, 0, 1, 0, 0, 0, 1, 1],
[1, 1, 1, 0, 0, 1, 0, 1, 1, 0],
[1, 1, 1, 0, 0, 1, 0, 0, 1, 1],
[1, 1, 1, 0, 0, 0, 1, 0, 1, 1],
[0, 1, 1, 1, 0, 1, 0, 1, 1, 0],
[0, 1, 1, 1, 0, 1, 0, 0, 1, 1],
[0, 1, 1, 1, 0, 0, 1, 0, 1, 1],
[0, 0, 1, 1, 1, 0, 1, 0, 1, 1]
]

Consecutive values in array with periodic boundaries in Python

I have some 2D-arrays filled with 0 and 1:
import numpy as np
a = np.random.randint(2, size=(20, 20))
b = np.random.randint(2, size=(20, 20))
c = np.random.randint(2, size=(20, 20))
d = np.random.randint(2, size=(20, 20))
and I want to count the consecutive occurrence of the ones with periodic boundaries.
That means (in 1D for clearness):
[1 1 0 0 1 1 0 1 1 1]
should give me 5(last three elements + first two).
The 2D-arrays should be compared/counted in the third (second if you start with 0) axis, like first stacking the arrays in axis=2 and then applying the same algorithm like for 1D. But I am not sure if this is the most simple way.
Here's one way for ndarrays a of 2D and higher dim arrays, meant for performance efficiency -
def count_periodic_boundary(a):
a = a.reshape(-1,a.shape[-1])
m = a==1
c0 = np.flip(m,axis=-1).argmin(axis=-1)+m.argmin(axis=-1)
z = np.zeros(a.shape[:-1]+(1,),dtype=bool)
p = np.hstack((z,m,z))
c = (p[:,:-1]<p[:,1:]).sum(1)
s = np.r_[0,c[:-1].cumsum()]
l = np.diff(np.flatnonzero(np.diff(p.ravel())))[::2]
d = np.maximum(c0,np.maximum.reduceat(l,s))
return np.where(m.all(-1),a.shape[-1],d)
Sample runs -
In [75]: np.random.seed(0)
...: a = np.random.randint(2, size=(5, 20))
In [76]: a
Out[76]:
array([[0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0],
[0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0],
[0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0]])
In [77]: count_periodic_boundary(a)
Out[77]: array([7, 4, 5, 2, 6])
In [72]: np.random.seed(0)
...: a = np.random.randint(2, size=(2, 5, 20))
In [73]: a
Out[73]:
array([[[0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0],
[0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1],
[1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0],
[0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0]],
[[1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0],
[1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0],
[1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1],
[0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0],
[1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0]]])
In [74]: count_periodic_boundary(a)
Out[74]: array([7, 4, 5, 2, 6, 2, 5, 4, 2, 1])
You can use groupby from itertools:
from itertools import groupby
a = [1, 1, 0, 0, 1, 1, 0, 1, 1, 1]
def get_longest_seq(a):
if all(a):
return len(a)
a_lens = [len(list(it)) for k, it in groupby(a) if k != 0]
if a[0] == 1 and a[-1] == 1:
m = max(max(a_lens), a_lens[0] + a_lens[-1])
else:
m = max(a_lens)
return m
print(get_longest_seq(a))
Here is a two-liner, admittedly containing one rather long line:
*m,n = a.shape
return np.minimum(n,(np.arange(1,2*n+1)-np.maximum.accumulate(np.where(a[...,None,:],0,np.arange(1,2*n+1).reshape(2,n)).reshape(*m,2*n),-1)).max(-1))
How it works:
Let's first ignore the wrap around and consider a simple example: a = [1 0 0 1 1 0 1 1 1 0]
We want to transform this into b = [1 0 0 1 2 0 1 2 3 0], so we can simply take the maximum. One way of generating b is taking the arange r = [1 2 3 4 5 6 7 8 9 10] and subtracting aux = [0 2 3 3 3 6 6 6 6 10]. aux we create by multiplying r with (1-a) yielding [0 2 3 0 0 6 0 0 0 10] and taking the cumulative maximum.
To deal with the wrap around we simply put two copies of a next to each other and then use the above.
Here is the code again broken down into smaller bits and commented:
*m,n = a.shape
# r has length 2*n because of how we deal with the wrap around
r = np.arange(1,2*n+1)
# create r x (1-a) using essentially np.where(a,0,r)
# it's a bit more involved because we are cloning a in the same step
# a will be doubled along a new axis we insert before the last one
# this will happen by means of broadcasting against r which we distribute
# over two rows along the new axis
# in the very end we merge the new and the last axis
r1_a = np.where(a[...,None,:],0,r.reshape(2,n)).reshape(*m,2*n)
# take cumulative max
aux = np.maximum.accumulate(r1_a,-1)
# finally, take the row wise maximum and deal with all-one rows
return np.minimum(n,(r-aux).max(-1))

Setting indicators based in index per row in numpy

I am looking for an efficient way to set a indicators from zero to a known number (which differs for each row).
e.g.
a =
array([[1, 1, 1, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 0]])
and I know the vector with the index when a goes from 1 to zero.
b = [3, 1, 6, 2, 8]
Rather than filling all the rows of a using a for-loop, I want to know if there is a fast way to set these indicators.
Use outer-comparison on ranged array vs. b -
In [16]: ncols = 9
In [17]: b
Out[17]: [3, 1, 6, 2, 8]
In [19]: np.greater.outer(b,np.arange(ncols)).view('i1')
Out[19]:
array([[1, 1, 1, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 0]], dtype=int8)
Other similar ways to express the same -
(np.asarray(b)[:,None] > np.arange(ncols)).view('i1')
(np.asarray(b)[:,None] > np.arange(ncols)).astype(int)
With b being an array, simplifies further, as we can skip the array conversion with np.asarray(b).
Simplest way I can think of is:
result=[]
for row in array:
result.append(row.tolist().index(0))
print(result)
[3, 1, 6, 2, 8]
The reason this works is, that list has a method called index, which tells the first occurrence of a specific item in the list. So I am iterating over this 2-dimentional array, converting each of it to list and using index of 0 on each.
You can store these values into another list and append to it for each row and that's it.
You can use broadcasting to do an outer comparison:
b = np.asarray([3, 1, 6, 2, 8])
a = (np.arange(b.max() + 1) < b[:, None]).astype(int)
# array([[1, 1, 1, 0, 0, 0, 0, 0, 0],
# [1, 0, 0, 0, 0, 0, 0, 0, 0],
# [1, 1, 1, 1, 1, 1, 0, 0, 0],
# [1, 1, 0, 0, 0, 0, 0, 0, 0],
# [1, 1, 1, 1, 1, 1, 1, 1, 0]])

Permutation without duplicates in Python

I have N positions, and each position can be either 0 or 1. I have fixed number of 1s, and I want to permutate these fixed number of 1s in these N positions.
from itertools import permutations
p = [0 for k in xrange(6)]
for k in xrange(0,3):
p[k] = 1
print(list(permutations(p)))
But above result contains four [0,0,0,1,1,1] in the list. I only want one of them. How can I get rid of these duplicates?
You could grab the positions of the 1s instead:
from itertools import combinations
def place_ones(size, count):
for positions in combinations(range(size), count):
p = [0] * size
for i in positions:
p[i] = 1
yield p
In action:
>>> list(place_ones(6, 3))
[
[1, 1, 1, 0, 0, 0],
[1, 1, 0, 1, 0, 0],
[1, 1, 0, 0, 1, 0],
[1, 1, 0, 0, 0, 1],
[1, 0, 1, 1, 0, 0],
[1, 0, 1, 0, 1, 0],
[1, 0, 1, 0, 0, 1],
[1, 0, 0, 1, 1, 0],
[1, 0, 0, 1, 0, 1],
[1, 0, 0, 0, 1, 1],
[0, 1, 1, 1, 0, 0],
[0, 1, 1, 0, 1, 0],
[0, 1, 1, 0, 0, 1],
[0, 1, 0, 1, 1, 0],
[0, 1, 0, 1, 0, 1],
[0, 1, 0, 0, 1, 1],
[0, 0, 1, 1, 1, 0],
[0, 0, 1, 1, 0, 1],
[0, 0, 1, 0, 1, 1],
[0, 0, 0, 1, 1, 1],
]
Set is perfect for this, as set does not not contain any duplicated element:
set(permutations(p))

How to increase a grid world's size by 1000 times

I'm using a program in which I have to input the environment's map. The input form looks like this.
self.map=[ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
I want to increase the size of the given structure by thousand times and maintain the form of the structure. After increasing the structure size will be 18000x6000. The code looks like this
Can someone suggest me a way to achieve this or any alternate way.
If you really want to use Python's lists (numpy's arrays are better for large matrices) you could use
repeatfactor = 1000
mat = self.map # copy reference, not data
m = len(mat)
n = len(mat[0])
newmatrix = [[mat[r % m][c % n]
for c in range(n * repeatfactor)]
for r in range(m * repeatfactor)]
Try np.repeat twice--once in each axis. Not the prettiest, but should work. So something like this:
map_array = np.array(self.map)
map_array = np.repeat(map_array, 1000, axis=0)
map_array = np.repeat(map_array, 1000, axis=1)

Categories

Resources