Algorithm for iterating over an outward spiral on a discrete 2D grid from the origin

前端 未结 12 1633
独厮守ぢ
独厮守ぢ 2020-12-05 06:54

For example, here is the shape of intended spiral (and each step of the iteration)

          y
          |
          |
   16 15 14 13 12
   17  4  3  2 11
--         


        
12条回答
  •  萌比男神i
    2020-12-05 07:40

    I would solve it using some math. Here is Ruby code (with input and output):

    (0..($*.pop.to_i)).each do |i|
        j = Math.sqrt(i).round
        k = (j ** 2 - i).abs - j
        p = [k, -k].map {|l| (l + j ** 2 - i - (j % 2)) * 0.5 * (-1) ** j}.map(&:to_i)
        puts "p => #{p[0]}, #{p[1]}"
    end
    

    E.g.

    $ ruby spiral.rb 10
    p => 0, 0
    p => 1, 0
    p => 1, 1
    p => 0, 1
    p => -1, 1
    p => -1, 0
    p => -1, -1
    p => 0, -1
    p => 1, -1
    p => 2, -1
    p => 2, 0
    

    And golfed version:

    p (0..$*.pop.to_i).map{|i|j=Math.sqrt(i).round;k=(j**2-i).abs-j;[k,-k].map{|l|(l+j**2-i-j%2)*0.5*(-1)**j}.map(&:to_i)}
    

    Edit

    First try to approach the problem functionally. What do you need to know, at each step, to get to the next step?

    Focus on plane's first diagonal x = y. k tells you how many steps you must take before touching it: negative values mean you have to move abs(k) steps vertically, while positive mean you have to move k steps horizontally.

    Now focus on the length of the segment you're currently in (spiral's vertices - when the inclination of segments change - are considered as part of the "next" segment). It's 0 the first time, then 1 for the next two segments (= 2 points), then 2 for the next two segments (= 4 points), etc. It changes every two segments and each time the number of points part of that segments increase. That's what j is used for.

    Accidentally, this can be used for getting another bit of information: (-1)**j is just a shorthand to "1 if you're decreasing some coordinate to get to this step; -1 if you're increasing" (Note that only one coordinate is changed at each step). Same holds for j%2, just replace 1 with 0 and -1 with 1 in this case. This mean they swap between two values: one for segments "heading" up or right and one for those going down or left.

    This is a familiar reasoning, if you're used to functional programming: the rest is just a little bit of simple math.

提交回复
热议问题