0

I have a matrix variable in size where 1 indicates the cell such as:

Cells   = [[0,0,0,0,0],
           [0,0,0,0,0],
           [0,0,1,0,0],
           [0,0,0,0,0],
           [0,0,0,0,0],
          ]

I need to find neigbours in a parametric sized diamond shape. Not a box as answer given in here or not a fixed sized 1 diamond, answer given here. For example, N=2 I want to know the column, rows for below:

Mask    = [[0,0,1,0,0],
           [0,1,1,1,0],
           [1,1,0,1,1],
           [0,1,1,1,0],
           [0,0,1,0,0],
          ]

The function should receive x and y for the requested column and row, (for above I will input 2,2) and N (input 2) the size of diamond. The function should return list of tuples (x,y) for the given diamond size.

I struggled at defining the shape as a function of x, y and k in for loops. I need to know both numpy (if there is anything that helps) and non-numpy solution.

  • Please repeat [on topic](https://stackoverflow.com/help/on-topic) and [how to ask](https://stackoverflow.com/help/how-to-ask) from the [intro tour](https://stackoverflow.com/tour). “Show me how to solve this coding problem” is not a Stack Overflow issue. We expect you to make an honest attempt, and *then* ask a *specific* question about your algorithm or technique. Stack Overflow is not intended to replace existing documentation and tutorials. In particular, asking us to provide finished code in two forms is out of scope; this is not a coding service. – Prune Nov 13 '20 at 15:12
  • This is only a small portion of what I am trying to achive. I was trying to be precise but to the point. I did make an attempt and wrote on where I struggled. Sorry. – Murat Erenturk Nov 13 '20 at 16:09

3 Answers3

2

For an iterative approach where you just construct the diamond:

def get_neighbors(center, n=1):
    ret = []
    for dx in range(-n, n + 1):
        ydiff = n - abs(dx)
        for dy in range(-ydiff, ydiff + 1):
            ret.append((center[0] + dx, center[1] + dy))
    return ret

Result of get_neighbors((2, 2), 2):

[(0, 2), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 1), (3, 2), (3, 3), (4, 2)]

Or, for a recursive approach:

dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)]

def add_tuples(a, b):
    return tuple([x + y for (x, y) in zip(a, b)])

def get_neighbors(center, n=1, seen=set()):
    seen.add(center)
    if n <= 0:
        return seen
    for dir in dirs:
        newpos = add_tuples(center, dir)
        if newpos in seen:
            continue
        get_neighbors(newpos, n - 1, seen)
    return seen
Aplet123
  • 33,825
  • 1
  • 29
  • 55
  • using abs is a elegant way to solve this problem. Probably [this question](https://stackoverflow.com/questions/56870063/looking-for-a-readable-elegant-way-to-select-direct-neighbors-in-2d-list-in-pyt) was looking for this. I will add this answer. – Murat Erenturk Nov 13 '20 at 16:13
0

I would start by taking out a "sub-matrix" that is the smallest square that can contain your result cells. This is the part that numpy should be able to help with.

Then define a function that calculates the manhattan distance between two cells (abs(x - x_p) + abs(y - y_p)) and iterate through the cells of your sub-matrix and return the values with a manhattan distance of less than N from your origin.

M.Isenholt
  • 193
  • 1
  • 1
  • 8
0
  • Make mask with rotation
  • Convolute cell and mask
  • Fix the result

Diamond filter Before convolution After convolution and fix

import numpy as np
from scipy.ndimage import rotate, convolve
import matplotlib.pyplot as plt


def diamond_filter(radius):
    s = radius * 2 + 1
    x = np.ones((s, s), dtype=int)
    x[radius, radius] = 0
    return rotate(x, angle=45)


def make_diamonds(x, radius):
    filter = diamond_filter(radius)
    out = convolve(x, filter)
    out[out > 1] = 1
    out -= x
    out[out < 0] = 0
    return out


def plot(x):
    plt.imshow(x)
    plt.show()
    plt.close()


def main():
    cell = np.random.choice([0, 1], size=(200, 200), p=[0.95, 0.05])
    plot(diamond_filter(2))
    plot(cell)
    result = make_diamonds(cell, 2)
    plot(result)


if __name__ == '__main__':
    main()


Crawl Cycle
  • 257
  • 2
  • 8