从原点开始在离散 2D 网格上迭代向外螺旋的算法
例如,以下是预期螺旋的形状(以及迭代的每个步骤),
y
|
|
16 15 14 13 12
17 4 3 2 11
-- 18 5 0 1 10 --- x
19 6 7 8 9
20 21 22 23 24
|
|
其中线条是 x 轴和 y 轴。
这里是算法每次迭代时“返回”的实际值(点的坐标):
[0,0],
[1,0], [1,1], [0,1], [-1,1], [-1,0], [-1,-1], [0,-1], [1,-1],
[2,-1], [2,0], [2,1], [2,2], [1,2], [0,2], [-1,2], [-2,2], [-2,1], [-2,0]..
等等。
我已经尝试过搜索,但我不确定要搜索什么,以及我搜索了什么尝试过却发现了死胡同。
我什至不知道从哪里开始,除了一些凌乱、不优雅和临时的事情,比如为每一层创建/编码一个新的螺旋。
有人可以帮助我开始吗?
另外,有没有一种方法可以轻松地在顺时针和逆时针(方向)之间切换,以及从哪个方向“开始”螺旋? (旋转)
另外,有没有办法递归地执行此操作?
我的应用程序
我有一个填充了数据点的稀疏网格,我想向网格添加一个新的数据点,并使其“尽可能接近”给定的其他点。
为此,我将调用 grid.find_closest_available_point_to(point) ,它将迭代上面给出的螺旋并返回第一个空且可用的位置。
因此,首先,它将检查 point+[0,0]
(只是为了完整性)。然后它会检查point+[1,0]
。然后它会检查point+[1,1]
。然后point+[0,1]
等。并返回网格中位置为空(或尚未被数据点占据)的第一个。
网格大小没有上限。
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
-- 18 5 0 1 10 --- x
19 6 7 8 9
20 21 22 23 24
|
|
Where the lines are the x and y axes.
Here would be the actual values the algorithm would "return" with each iteration (the coordinates of the points):
[0,0],
[1,0], [1,1], [0,1], [-1,1], [-1,0], [-1,-1], [0,-1], [1,-1],
[2,-1], [2,0], [2,1], [2,2], [1,2], [0,2], [-1,2], [-2,2], [-2,1], [-2,0]..
etc.
I've tried searching, but I'm not exactly sure what to search for exactly, and what searches I've tried have come up with dead ends.
I'm not even sure where to start, other than something messy and inelegant and ad-hoc, like creating/coding a new spiral for each layer.
Can anyone help me get started?
Also, is there a way that can easily switch between clockwise and counter-clockwise (the orientation), and which direction to "start" the spiral from? (the rotation)
Also, is there a way to do this recursively?
My application
I have a sparse grid filled with data points, and I want to add a new data point to the grid, and have it be "as close as possible" to a given other point.
To do that, I'll call grid.find_closest_available_point_to(point)
, which will iterate over the spiral given above and return the first position that is empty and available.
So first, it'll check point+[0,0]
(just for completeness's sake). Then it'll check point+[1,0]
. Then it'll check point+[1,1]
. Then point+[0,1]
, etc. And return the first one for which the position in the grid is empty (or not occupied already by a data point).
There is no upper bound to grid size.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
这是 C++ 中的一个有状态迭代器。
应该尽可能高效。
Here's a stab at it in C++, a stateful iterator.
Should be about as efficient as it gets.
这是基于答案的javascript解决方案
螺旋循环
小提琴:http://jsfiddle.net/N9gEC/18/
This is the javascript solution based on the answer at
Looping in a spiral
fiddle: http://jsfiddle.net/N9gEC/18/
通过分析螺旋角的坐标如何变化可以最好地理解这个问题。考虑前 8 个螺旋角的表格(不包括原点):
通过查看此表格,我们可以计算给定 (k-1) 角的 X,Y 的第 k 个角的 X,Y:
现在当您知道 k 和 k 的坐标时+1 螺旋角,只需将最后一个点的 x 或 y 加 1 或 -1 即可获得 k 和 k+1 之间的所有数据点。
就是这样。
祝你好运。
This problem is best understood by analyzing how changes coordinates of spiral corners. Consider this table of first 8 spiral corners (excluding origin):
By looking at this table we can calculate X,Y of k-th corner given X,Y of (k-1) corner:
Now when you know coordinates of k and k+1 spiral corner you can get all data points in between k and k+1 by simply adding 1 or -1 to x or y of last point.
Thats it.
good luck.
我会用一些数学来解决它。这是 Ruby 代码(带有输入和输出):
例如
,高尔夫版本:
编辑
首先尝试从功能上解决问题。在每一步中,您需要了解什么才能进入下一步?
关注平面的第一条对角线
x = y
。k
告诉您在触摸它之前必须走多少步:负值表示您必须垂直移动abs(k)
步,而正值表示您必须移动>k
水平步。现在关注您当前所在线段的长度(螺旋的顶点 - 当线段的倾斜度发生变化时 - 被视为“下一个”线段的一部分)。第一次为
0
,接下来的两段为1
(= 2 分),接下来的两段为2
(= 4 分)点)等。它每两个段发生变化,并且每次该段的点数部分增加时。这就是j
的用途。意外地,这可以用于获取另一位信息:
(-1)**j
只是“1
”的简写,如果您要减少一些坐标来获取到此步骤;如果要增加,则为-1
”(请注意,每一步仅更改一个坐标)。j%2
也是如此,只需将1
替换为0
,将-1
替换为1
代码> 在这种情况下。这意味着它们在两个值之间交换:一个用于“向上”或向右“前进”的线段,一个用于向下或向左“前进”的线段。如果您习惯了函数式编程,这是一个熟悉的推理:剩下的只是一些简单的数学运算。
I would solve it using some math. Here is Ruby code (with input and output):
E.g.
And golfed version:
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 moveabs(k)
steps vertically, while positive mean you have to movek
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, then1
for the next two segments (= 2 points), then2
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 whatj
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 forj%2
, just replace1
with0
and-1
with1
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.
它可以使用递归以相当简单的方式完成。我们只需要一些基本的二维向量数学和工具来生成和映射(可能是无限的)序列:
现在我们可以通过生成一条扁线,加上一条旋转的(扁线,加上一条旋转的(扁线,加上旋转...)):
这会产生您感兴趣的坐标的无限列表。以下是内容示例:
您也可以取消旋转角度以朝另一个方向移动,或者播放围绕变换和腿长以获得更复杂的形状。
例如,相同的技术也适用于向内螺旋。这只是一个稍微不同的变换,以及一个稍微不同的改变腿长度的方案:
产生(这个螺旋是有限的,所以我们不需要
采取
一些任意数字):< /a>
如果你想尝试一下,这里是整个事情的实时片段:
It can be done in a fairly straightforward way using recursion. We just need some basic 2D vector math and tools for generating and mapping over (possibly infinite) sequences:
And now we can express a spiral recursively by generating a flat line, plus a rotated (flat line, plus a rotated (flat line, plus a rotated ...)):
Which produces an infinite list of the coordinates you're interested in. Here's a sample of the contents:
You can also negate the rotation angle to go in the other direction, or play around with the transform and leg length to get more complex shapes.
For example, the same technique works for inward spirals as well. It's just a slightly different transform, and a slightly different scheme for changing the length of the leg:
Which produces (this spiral is finite, so we don't need to
take
some arbitrary number):Here's the whole thing together as a live snippet if you want play around with it:
这是基于@mako 答案的Python 实现。
运行此代码:
产量:
Here is a Python implementation based on the answer by @mako.
Running this code:
Yields:
尝试搜索参数方程或极坐标方程。两者都适合绘制螺旋状的事物。 这里有一个页面,其中有大量示例,带有图片(和方程式)。它应该会给你更多关于要寻找什么的想法。
Try searching for either parametric or polar equations. Both are suitable to plotting spirally things. Here's a page that has plenty of examples, with pictures (and equations). It should give you some more ideas of what to look for.
我已经完成了与训练练习几乎相同的任务,但输出和螺旋方向有一些差异,并且有一个额外的要求,即函数的空间复杂度必须为 O(1)。
经过思考一段时间后,我想到,通过知道螺旋从哪里开始以及我计算值的位置,我可以通过减去螺旋的所有完整“圆”来简化问题,然后只计算一个更简单的值。
这是我在 ruby 中对该算法的实现:
这并不完全是您所要求的,但我相信它会帮助您思考您的问题
I've done pretty much the same thin as a training exercise, with some differences in the output and the spiral orientation, and with an extra requirement, that the functions spatial complexity has to be O(1).
After think for a while I came to the idea that by knowing where does the spiral start and the position I was calculating the value for, I could simplify the problem by subtracting all the complete "circles" of the spiral, and then just calculate a simpler value.
Here is my implementation of that algorithm in ruby:
This is not exactly the thing you asked for, but I believe it'll help you to think your problem
我遇到了类似的问题,但我不想每次都循环整个螺旋以找到下一个新坐标。要求是您知道您的最后一个坐标。
这是我通过大量阅读其他解决方案得出的结论:
仍然不确定它是否是最优雅的解决方案。也许一些优雅的数学可以删除一些 if 语句。一些限制是需要进行一些修改来改变螺旋方向,不考虑非方形螺旋并且不能围绕固定坐标螺旋。
I had a similar problem, but I didn't want to loop over the entire spiral each time to find the next new coordinate. The requirement is that you know your last coordinate.
Here is what I came up with with a lot of reading up on the other solutions:
Still not sure if it is the most elegant solution. Perhaps some elegant maths could remove some of the if statements. Some limitations would be needing some modification to change spiral direction, doesn't take into account non-square spirals and can't spiral around a fixed coordinate.
我在java中有一个算法,它输出与你类似的输出,只不过它优先考虑右边的数字,然后是左边的数字。
I have an algorithm in java that outputs a similar output to yours, except that it prioritizes the number on the right, then the number on the left.
这是算法。它顺时针旋转,但可以轻松地逆时针旋转,只需进行一些更改。我只用了不到一个小时就做到了。
它将处理任何 x / y 值(无限)。
它是用 GML(游戏制作语言)编写的,但实际逻辑在任何编程语言中都是合理的。
单线算法只有 2 个变量(sx 和 sy)用于 x 和 y 输入。我基本上扩大了括号,很多。它使您可以更轻松地将其粘贴到记事本中,并将“sx”更改为您的 x 参数/变量名称,将“sy”更改为您的 y 参数/变量名称。
我知道回复太晚了 :D 但我希望它对未来的访客有所帮助。
Here's the algorithm. It rotates clockwise, but could easily rotate anticlockwise, with a few alterations. I made it in just under an hour.
It will handle any x / y values (infinite).
It's written in GML (Game Maker Language), but the actual logic is sound in any programming language.
The single line algorithm only has 2 variables (sx and sy) for the x and y inputs. I basically expanded brackets, a lot. It makes it easier for you to paste it into notepad and change 'sx' for your x argument / variable name and 'sy' to your y argument / variable name.
I know the reply is awfully late :D but i hope it helps future visitors.
这是 Javascript 中的一个非常简单的答案:
请注意上面给定所谓半径的情况下由
count
计算出的正方形总数。完整代码示例
Here's a very simple answer in Javascript:
Note the total number of squares computed by
count
above given the so called radius.Full code example
直接的“临时”解决方案没有任何问题。它也可以足够干净。
请注意,螺旋是由分段构建的。您可以将当前线段旋转 90 度以获得下一段。每旋转两次,线段长度增加1。
编辑插图,这些线段编号
要改变原始方向,请查看
di
和dj.要将旋转切换为顺时针,请查看如何修改这些值。
There's nothing wrong with direct, "ad-hoc" solution. It can be clean enough too.
Just notice that spiral is built from segments. And you can get next segment from current one rotating it by 90 degrees. And each two rotations, length of segment grows by 1.
edit Illustration, those segments numbered
To change original direction, look at original values of
di
anddj
. To switch rotation to clockwise, see how those values are modified.