Looping in a spiral

前端 未结 30 2400
独厮守ぢ
独厮守ぢ 2020-11-22 15:07

A friend was in need of an algorithm that would let him loop through the elements of an NxM matrix (N and M are odd). I came up with a solution, but I wanted to see if my fe

30条回答
  •  无人及你
    2020-11-22 15:37

    Here's an answer in Julia: my approach is to assign the points in concentric squares ('spirals') around the origin (0,0), where each square has side length m = 2n + 1, to produce an ordered dictionary with location numbers (starting from 1 for the origin) as keys and the corresponding coordinate as value.

    Since the maximum location per spiral is at (n,-n), the rest of the points can be found by simply working backward from this point, i.e. from the bottom right corner by m-1 units, then repeating for the perpendicular 3 segments of m-1 units.

    This process is written in reverse order below, corresponding to how the spiral proceeds rather than this reverse counting process, i.e. the ra [right ascending] segment is decremented by 3(m+1), then la [left ascending] by 2(m+1), and so on - hopefully this is self-explanatory.

    import DataStructures: OrderedDict, merge
    
    function spiral(loc::Int)
        s = sqrt(loc-1) |> floor |> Int
        if s % 2 == 0
            s -= 1
        end
        s = (s+1)/2 |> Int
        return s
    end
    
    function perimeter(n::Int)
        n > 0 || return OrderedDict([1,[0,0]])
        m = 2n + 1 # width/height of the spiral [square] indexed by n
        # loc_max = m^2
        # loc_min = (2n-1)^2 + 1
        ra = [[m^2-(y+3m-3), [n,n-y]] for y in (m-2):-1:0]
        la = [[m^2-(y+2m-2), [y-n,n]] for y in (m-2):-1:0]
        ld = [[m^2-(y+m-1), [-n,y-n]] for y in (m-2):-1:0]
        rd = [[m^2-y, [n-y,-n]] for y in (m-2):-1:0]
        return OrderedDict(vcat(ra,la,ld,rd))
    end
    
    function walk(n)
        cds = OrderedDict(1 => [0,0])
        n > 0 || return cds
        for i in 1:n
            cds = merge(cds, perimeter(i))
        end
        return cds
    end
    

    So for your first example, plugging m = 3 into the equation to find n gives n = (5-1)/2 = 2, and walk(2) gives an ordered dictionary of locations to coordinates, which you can turn into just an array of coordinates by accessing the dictionary's vals field:

    walk(2)
    DataStructures.OrderedDict{Any,Any} with 25 entries:
      1  => [0,0]
      2  => [1,0]
      3  => [1,1]
      4  => [0,1]
      ⋮  => ⋮
    
    [(co[1],co[2]) for co in walk(2).vals]
    25-element Array{Tuple{Int64,Int64},1}:
     (0,0)  
     (1,0)  
     ⋮       
     (1,-2) 
     (2,-2)
    

    Note that for some functions [e.g. norm] it can be preferable to leave the coordinates in arrays rather than Tuple{Int,Int}, but here I change them into tuples—(x,y)—as requested, using list comprehension.

    The context for "supporting" a non-square matrix isn't specified (note that this solution still calculates the off-grid values), but if you want to filter to only the range x by y (here for x=5,y=3) after calculating the full spiral then intersect this matrix against the values from walk.

    grid = [[x,y] for x in -2:2, y in -1:1]
    5×3 Array{Array{Int64,1},2}:
     [-2,-1]  [-2,0]  [-2,1]
       ⋮       ⋮       ⋮ 
     [2,-1]   [2,0]   [2,1]
    
    [(co[1],co[2]) for co in intersect(walk(2).vals, grid)]
    15-element Array{Tuple{Int64,Int64},1}:
     (0,0)  
     (1,0)  
     ⋮ 
     (-2,0) 
     (-2,-1)
    

提交回复
热议问题