Iterate over 2d array in an expanding circular spiral

前端 未结 7 620
自闭症患者
自闭症患者 2020-12-23 14:39

Given an n by n matrix M, at row i and column j, I\'d like to iterate over all the neighboring values in a c

7条回答
  •  遥遥无期
    2020-12-23 15:15

    One way for yielding points with increasing distance is to break it down into easy parts, and then merge the results of the parts together. It's rather obvious that itertools.merge should do the merging. The easy parts are columns, because for fixed x the points (x, y) can be ordered by looking at the value of y only.

    Below is a (simplistic) implementation of that algorithm. Note that the squared Euclidian distance is used, and that the center point is included. Most importantly, only points (x, y) with x in range(x_end) are considered, but I think that's OK for your use case (where x_end would be n in your notation above).

    from heapq import merge
    from itertools import count
    
    def distance_column(x0, x, y0):
        dist_x = (x - x0) ** 2
        yield dist_x, (x, y0)
        for dy in count(1):
            dist = dist_x + dy ** 2
            yield dist, (x, y0 + dy)
            yield dist, (x, y0 - dy)
    
    def circle_around(x0, y0, end_x):
        for dist_point in merge(*(distance_column(x0, x, y0) for x in range(end_x))):
            yield dist_point
    

    Edit: Test code:

    def show(circle):
        d = dict((p, i) for i, (dist, p) in enumerate(circle))
        max_x = max(p[0] for p in d) + 1
        max_y = max(p[1] for p in d) + 1
        return "\n".join(" ".join("%3d" % d[x, y] if (x, y) in d else "   " for x in range(max_x + 1)) for y in range(max_y + 1))
    
    import itertools
    print(show(itertools.islice(circle_around(5, 5, 11), 101)))
    

    Result of test (points are numbered in the order they are yielded by circle_around):

                 92  84  75  86  94                
         98  73  64  52  47  54  66  77 100        
         71  58  40  32  27  34  42  60  79        
     90  62  38  22  16  11  18  24  44  68  96    
     82  50  30  14   6   3   8  20  36  56  88    
     69  45  25   9   1   0   4  12  28  48  80    
     81  49  29  13   5   2   7  19  35  55  87    
     89  61  37  21  15  10  17  23  43  67  95    
         70  57  39  31  26  33  41  59  78        
         97  72  63  51  46  53  65  76  99        
                 91  83  74  85  93                
    

    Edit 2: If you really do need negative values of i, replace range(end_x) with range(-end_x, end_x) in the cirlce_around function.

提交回复
热议问题