How do I add to a grid coordinate in python? - python

What I'm trying to do is have a 2D array and for every coordinate in the array, ask all the other 8 coordinates around it if they have stored a 1 or a 0. Similar to a minesweeper looking for mines.
I used to have this:
grid = []
for fila in range(10):
grid.append([])
for columna in range(10):
grid[fila].append(0)
#edited
for fila in range (10):
for columna in range (10):
neighbour = 0
for i in range 10:
for j in range 10:
if gird[fila + i][columna + j] == 1
neighbour += 1
But something didn't work well. I also had print statments to try to find the error that way but i still didnt understand why it only made half of the for loop. So I changed the second for loop to this:
#edited
for fila in range (10):
for columna in range (10):
neighbour = 0
if grid[fila - 1][columna - 1] == 1:
neighbour += 1
if grid[fila - 1][columna] == 1:
neighbour += 1
if grid[fila - 1][columna + 1] == 1:
neighbour += 1
if grid[fila][columna - 1] == 1:
neighbour += 1
if grid[fila][columna + 1] == 1:
neighbour += 1
if grid[fila + 1][columna - 1] == 1:
neighbour += 1
if grid[fila + 1][columna] == 1:
neighbour += 1
if grid[fila + 1][columna + 1] == 1:
neighbour += 1
And got this error:
if grid[fila - 1][columna + 1] == 1:
IndexError: list index out of range
It seems like I can't add on the grid coordinates but I can subtract. Why is that?

Valid indices in python are -len(grid) to len(grid)-1. the positive indices are accessing elements with offset from the front, the negative ones from the rear. adding gives a range error if the index is greater than len(grid)-1 that is what you see. subtracting does not give you a range error unless you get an index value less than -len(grid). although you do not check for the lower bound, which is 0 (zero) it seems to work for you as small negative indices return you values from the rear end. this is a silent error leading to wrong neighborhood results.

If you are computing offsets, you need to make sure your offsets are within the bounds of the lists you have. So if you have 10 elements, don't try to access the 11th element.
import collections
grid_offset = collections.namedtuple('grid_offset', 'dr dc')
Grid = [[0 for c in range(10)] for r in range(10)]
Grid_height = len(Grid)
Grid_width = len(Grid[0])
Neighbors = [
grid_offset(dr, dc)
for dr in range(-1, 2)
for dc in range(-1, 2)
if not dr == dc == 0
]
def count_neighbors(row, col):
count = 0
for nb in Neighbors:
r = row + nb.dr
c = col + nb.dc
if 0 <= r < Grid_height and 0 <= c < Grid_width:
# Add the value, or just add one?
count += Grid[r][c]
return count
Grid[4][6] = 1
Grid[5][4] = 1
Grid[5][5] = 1
for row in range(10):
for col in range(10):
print(count_neighbors(row, col), "", end='')
print()
Prints:
$ python test.py
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 1 1 1 0 0
0 0 0 1 2 3 1 1 0 0
0 0 0 1 1 2 2 1 0 0
0 0 0 1 2 2 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

The error is exactly what it says, you need to check if the coordinates fit within the grid:
0 <= i < 10 and 0 <= j < 10
Otherwise you're trying to access an element that doesn't exist in memory, or an element that's not the one you're actually thinking about - Python handles negative indexes, they're counted from the end.
E.g. a[-1] is the last element, exactly the same as a[len(a) - 1].

Related

creating chessboard pattern with tables in python

I need to make a chessboard pattern filled with 0 and 1 but it it doesn't have to be square table
I need to get rows and columns from user
just an example:
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0
I have the solution but I couldn't understand the last row of code (table[i][j] = int(not table[i][j-1]))
can we solve it using another method?
m = int(input("insert number of rows: "))
n = int(input("insert number of colomns: "))
table = list()
for i in range(0,m):
if i%2 == 0 :
table[i][0] = 1
else:
table[i][0] = 0
for j in range(1,n):
table[i][j] = int(not table[i][j-1])
print("print the table with chessboard pattern")
for i in range(0,m):
for j in range(0,n):
print(table[i][j],end='')
print()
You'll have a simpler time with a nested list comprehension that uses the x and y coordinates of the cell being generated and the modulo operator % to alternate between two values:
n_rows = n_cols = 5
table = [
[((x + y + 1) % 2) for x in range(n_cols)]
for y in range(n_rows)
]
for row in table:
print(*row)
prints out
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
The list comprehension can be written out as nested for loops, too.
table = []
for y in range(n_rows):
row = []
for x in range(n_cols):
row.append((x + y + 1) % 2)
table.append(row)
You will have an error on line 6 because you can't assign a value to an index that doesn't exist..
You can do this instead:
m = int(input("insert number of rows: "))
n = int(input("insert number of colomns: "))
table = []
for i in range(m):
if i%2==0:
table.append([0+j%2 for j in range(n)])
else:
table.append([0+j%2 for j in range(1, n+1)])
You could also print it like this:
print("".join([str(row).replace('[', '').replace(']', '\n').replace(', ', ' ') for row in table]))

Would like to vectorize while loop for performance

I am trying to set values for a window of an array based on the current value of another array.
It should ignore values that the windown overrides.
I need to be able to change the size of the window for different runs.
This works but it is very slow.
I thought there would be a vectorized solution somewhere.
window_size=3
def signal(self):
signal = pd.Series(data=0, index=arr.index)
i = 0
while i < len(self.arr) - 1:
s = self.arr.iloc[i]
if s in [-1, 1]:
j = i + window_size
signal.iloc[i: j] = s
i = i + window_size
else:
i += 1
return signal
arr = [0 0 0 0 1 0 0 0 0 0 0 -1 -1 0 0 0 0 ]
signal = [0 0 0 0 1 1 1 0 0 0 0 -1 -1 -1 0 0 0 ]
You could use shift function of pd.Series
arr_series = pd.Series(arr)
arr_series + arr_series.shift(periods=1, fill_value=0) + arr_series.shift(periods=2, fill_value=0)

Integers wont append to my array for grid - python

Recently for a school project i've been making a "Treasure hunt" where the player finds treasure and bandits on a grid in python. I have a way to have the grid at a set size but, as an extra point they ask for us to be able to change the size of the grid, the amount of chests and the amount of bandits.
Here is the code for my grid maker but it wont make the "grid" array but it does for "playergrid":
def gridmaker(gridsize, debug):
global grid
global playergrid
gridinator = 1
grid = [[0]]
playergrid = [[" "]]
if debug == 1:
while gridinator <= gridsize:
grid[gridinator].append(0)
gridinator = gridinator + 1
gridinator = 1
else:
while gridinator <= gridsize:
playergrid[0].append(gridinator)
gridinator = gridinator + 1
gridinator = 1
while gridinator <= gridsize:
if debug == 1:
grid.append([0])
for i in range(gridsize):
grid[gridinator].append(0)
else:
playergrid.append([gridinator])
for i in range(gridsize):
playergrid[gridinator].append("#")
gridinator = gridinator+1
if debug == 1:
grid[1][1] = 1
else:
playergrid[1][1] = "P"
gridmaker(9, 1)
for row in grid:
print(" ".join(map(str,row)))
Sorry if it is formatted differently as there are 2 space tabs rather than 4, it works best on repl.it
print(grid) should return a grid like this:
0 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 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
Please let me know,
Thanks!
You have to remember that lists are 0-indexed.
Which means that to access the 1st element of the grid list you would use the index 0.
With grid = [[0]] you create a list with one item (you can get that item with grid[0]), which is a list whose 1st item (grid[0][0]) is 0.
But your gridinator's starting value is 1. So when your first append runs:
grid[gridinator].append(0)
it tries to access the 2nd element of grid:
grid[1].append(0)
Which gives you an IndexError since, as the traceback should tell you* list index out of range.
You can try this yourself:
grid = [[0]]
grid[0]
grid[1]
One of your solutions could be starting the gridinator with 0, and using strict less instead of less or equal here: gridinator <= gridsize (because grid[8] gives you the 9th element of the grid).
*Please remember to include the traceback for errors in the future. They really help both yourself and the people trying to help you.
Let me know if this helps, or if I should find another way to explain it.

Series calculation based on shifted values / recursive algorithm

I have the following:
df['PositionLong'] = 0
df['PositionLong'] = np.where(df['Alpha'] == 1, 1, (np.where(np.logical_and(df['PositionLong'].shift(1) == 1, df['Bravo'] == 1), 1, 0)))
This lines basically only take in df['Alpha'] but not the df['PositionLong'].shift(1).. It cannot recognize it but I dont understand why?
It produces this:
df['Alpha'] df['Bravo'] df['PositionLong']
0 0 0
1 1 1
0 1 0
1 1 1
1 1 1
However what I wanted the code to do is this:
df['Alpha'] df['Bravo'] df['PositionLong']
0 0 0
1 1 1
0 1 1
1 1 1
1 1 1
I believe the solution is to loop each row, but this will take very long.
Can you help me please?
You are looking for a recursive function, since a previous PositionLong value depends on Alpha, which itself is used to determine PositionLong.
But numpy.where is a regular function, so df['PositionLong'].shift(1) is evaluated as a series of 0 values, since you initialise the series with 0.
A manual loop need not be expensive. You can use numba to efficiently implement your recursive algorithm:
from numba import njit
#njit
def rec_algo(alpha, bravo):
res = np.empty(alpha.shape)
res[0] = 1 if alpha[0] == 1 else 0
for i in range(1, len(res)):
if (alpha[i] == 1) or ((res[i-1] == 1) and bravo[i] == 1):
res[i] = 1
else:
res[i] = 0
return res
df['PositionLong'] = rec_algo(df['Alpha'].values, df['Bravo'].values).astype(int)
Result:
print(df)
Alpha Bravo PositionLong
0 0 0 0
1 1 1 1
2 0 1 1
3 1 1 1
4 1 1 1

Generate binary strings that are at least in d hamming distance using ECC

I want to generate binary strings of length n=128 with the property that any pair of such strings are at least in d=10 hamming distance.
For this I am trying to use an Error Correcting Code (ECC) with minimum distance d=10. However, I cannot find any ecc that has code words of 128 bit length. If the code word length (n) and d are a little bit smaller/greater than 128 and 10, that still works for me.
Is there any ecc with this (similar) properties? Is there any python implementation of this?
Reed-Muller codes RM(3,7) have:
a block size of 128 bits
a minimum distance of 16
a message size of 64 bits
First construct a basis like this:
def popcnt(x):
return bin(x).count("1")
basis = []
by_ones = list(range(128))
by_ones.sort(key=popcnt)
for i in by_ones:
count = popcnt(i)
if count > 3:
break
if count <= 1:
basis.append(((1 << 128) - 1) // ((1 << i) | 1))
else:
p = ((1 << 128) - 1)
for b in [basis[k + 1] for k in range(7) if ((i >> k) & 1) != 0]:
p = p & b
basis.append(p)
Then you can use any linear combination of them, which are created by XORing subsets of rows of the basis, for example:
def encode(x, basis):
# requires x < (1 << 64)
r = 0
for i in range(len(basis)):
if ((x >> i) & 1) != 0:
r = r ^ basis[i]
return r
In some other implementation I found this was done by taking dot products with columns of the basis matrix and then reducing modulo 2. I don't know why they do that, it seems much easier to do it more directly by summing a subset of rows.
I needed the exact same thing. For me the naive approach worked very well! Simply generate random bit strings and check hamming distance between them, gradually building a list of strings that fulfills the requirement:
def random_binary_array(width):
"""Generate random binary array of specific width"""
# You can enforce additional array level constraints here
return np.random.randint(2, size=width)
def hamming2(s1, s2):
"""Calculate the Hamming distance between two bit arrays"""
assert len(s1) == len(s2)
# return sum(c1 != c2 for c1, c2 in zip(s1, s2)) # Wikipedia solution
return np.count_nonzero(s1 != s2) # a faster solution
def generate_hamm_arrays(n_values, size, min_hamming_dist=5):
"""
Generate a list of binary arrays ensuring minimal hamming distance between the arrays.
"""
hamm_list = []
while len(hamm_list) < size:
test_candidate = random_binary_array(n_values)
valid = True
for word in hamm_list:
if (word == test_candidate).all() or hamming2(word, test_candidate) <= min_hamming_dist:
valid = False
break
if valid:
hamm_list.append(test_candidate)
return np.array(hamm_list)
print(generate_hamm_arrays(16, 10))
Output:
[[0 0 1 1 0 1 1 1 0 1 0 1 1 1 1 1]
[1 0 1 0 0 1 0 0 0 1 0 0 1 0 1 1]
[1 1 0 0 0 0 1 0 0 0 1 1 1 1 0 0]
[1 0 0 1 1 0 0 1 1 0 0 1 1 1 0 1]
[0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 1]
[1 1 0 0 0 0 0 1 0 1 1 1 0 1 1 1]
[1 1 0 1 0 1 0 1 1 1 1 0 0 1 0 0]
[0 1 1 1 1 1 1 0 0 0 1 1 0 0 0 0]
[1 1 0 0 0 0 1 1 1 0 0 1 0 0 0 1]
[0 1 0 1 1 0 1 1 1 1 1 1 1 1 1 0]]
And it's not too slow as long as you don't want a very dense list of strings (a small number of bits in a string + large hamming distance). From your specifications (128 bit strings with hamming distance 10 it is no problem) we can generate a 1000 bit strings in under 0.2 seconds on a really weak cpu:
import timeit
timeit.timeit(lambda: generate_hamm_arrays(n_values=128, size=100, min_hamming_dist=10), number=10)
>> 0.19202665984630585
Hope this solution is sufficient for you too.
My O(n*n!) solution (works in a reasonable time for N<14)
def hammingDistance(n1, n2):
return bin(np.bitwise_xor(n1, n2)).count("1")
N = 10 # binary code of length N
D = 6 # with minimum distance D
M = 2**N # number of unique codes in general
# construct hamming distance matrix
A = np.zeros((M, M), dtype=int)
for i in range(M):
for j in range(i+1, M):
A[i, j] = hammingDistance(i, j)
A += A.T
def recursivly_find_legit_numbers(nums, codes=set()):
codes_to_probe = nums
for num1 in nums:
codes.add(num1)
codes_to_probe = codes_to_probe - {num1}
for num2 in nums - {num1}:
if A[num1, num2] < D:
"Distance isn't sufficient, remove this number from set"
codes_to_probe = codes_to_probe - {num2}
if len(codes_to_probe):
recursivly_find_legit_numbers(codes_to_probe, codes)
return codes
group_of_codes = {}
for i in tqdm(range(M)):
satisfying_numbers = np.where(A[i] >= D)[0]
satisfying_numbers = satisfying_numbers[satisfying_numbers > i]
nums = set(satisfying_numbers)
if len(nums) == 0:
continue
group_of_codes[i] = recursivly_find_legit_numbers(nums, set())
group_of_codes[i].add(i)
largest_group = 0
for i, nums in group_of_codes.items():
if len(nums) > largest_group:
largest_group = len(nums)
ind = i
print(f"largest group for N={N} and D={D}: {largest_group}")
print("Number of unique groups:", len(group_of_codes))
largest group for N=10 and D=6: 6 Number of unique groups: 992
# generate largest group of codes
[format(num, f"0{N}b") for num in group_of_codes[ind]]
['0110100001',
'0001000010',
'1100001100',
'1010010111',
'1111111010',
'0001111101']

Categories