以扩展的圆形螺旋迭代二维数组
给定一个 n
by n
矩阵 M
,位于行 i
和列 j
,我想迭代圆形螺旋中的所有相邻值。
这样做的目的是测试某个函数 f
(它取决于 M),以找到远离 (i, j)
的半径,其中 f< /code> 返回
True
。因此,f
看起来像这样:
def f(x, y):
"""do stuff with x and y, and return a bool"""
并且会这样调用:
R = numpy.zeros(M.shape, dtype=numpy.int)
# for (i, j) in M
for (radius, (cx, cy)) in circle_around(i, j):
if not f(M[i][j], M[cx][cy]):
R[cx][cy] = radius - 1
break
其中,circle_around
是返回圆形螺旋中索引(迭代器)的函数。因此,对于 M
中的每个点,此代码都会计算并存储从该点开始的半径,其中 f
返回 True
。
如果有更有效的计算 R 的方法,我也愿意这样做。
更新:
感谢所有提交答案的人。我编写了一个简短的函数来绘制 circle_around
迭代器的输出,以显示它们的作用。如果您更新答案或发布新答案,则可以使用此代码来验证您的解决方案。
from matplotlib import pyplot as plt
def plot(g, name):
plt.axis([-10, 10, -10, 10])
ax = plt.gca()
ax.yaxis.grid(color='gray')
ax.xaxis.grid(color='gray')
X, Y = [], []
for i in xrange(100):
(r, (x, y)) = g.next()
X.append(x)
Y.append(y)
print "%d: radius %d" % (i, r)
plt.plot(X, Y, 'r-', linewidth=2.0)
plt.title(name)
plt.savefig(name + ".png")
结果如下: 绘图(circle_around(0, 0),“FJ”):
plot(circle_around(0, 0, 10), "WolframH")
:
我已将 Magnesium 的建议编码如下:
def circle_around_magnesium(x, y):
import math
theta = 0
dtheta = math.pi / 32.0
a, b = (0, 1) # are there better params to use here?
spiral = lambda theta : a + b*theta
lastX, lastY = (x, y)
while True:
r = spiral(theta)
X = r * math.cos(theta)
Y = r * math.sin(theta)
if round(X) != lastX or round(Y) != lastY:
lastX, lastY = round(X), round(Y)
yield (r, (lastX, lastY))
theta += dtheta
plot(circle_around(0, 0, 10) ,“镁”)
:
如您所见,满足我正在寻找的界面的结果都没有产生圆形螺旋它涵盖了 0, 0 周围的所有索引。FJ 是最接近的,尽管 WolframH 命中了正确的点,只是不是按螺旋顺序排列。
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 circular spiral.
The point of doing this is to test some function, f
, which depends on M, to find the radius away from (i, j)
in which f
returns True
. So, f
looks like this:
def f(x, y):
"""do stuff with x and y, and return a bool"""
and would be called like this:
R = numpy.zeros(M.shape, dtype=numpy.int)
# for (i, j) in M
for (radius, (cx, cy)) in circle_around(i, j):
if not f(M[i][j], M[cx][cy]):
R[cx][cy] = radius - 1
break
Where circle_around
is the function that returns (an iterator to) indices in a circular spiral. So for every point in M
, this code would compute and store the radius from that point in which f
returns True
.
If there's a more efficient way of computing R
, I'd be open to that, too.
Update:
Thanks to everyone who submitted answers. I've written a short function to plot the output from your circle_around
iterators, to show what they do. If you update your answer or post a new one, you can use this code to validate your solution.
from matplotlib import pyplot as plt
def plot(g, name):
plt.axis([-10, 10, -10, 10])
ax = plt.gca()
ax.yaxis.grid(color='gray')
ax.xaxis.grid(color='gray')
X, Y = [], []
for i in xrange(100):
(r, (x, y)) = g.next()
X.append(x)
Y.append(y)
print "%d: radius %d" % (i, r)
plt.plot(X, Y, 'r-', linewidth=2.0)
plt.title(name)
plt.savefig(name + ".png")
Here are the results:plot(circle_around(0, 0), "F.J")
:
plot(circle_around(0, 0, 10), "WolframH")
:
I've coded up Magnesium's suggestion as follows:
def circle_around_magnesium(x, y):
import math
theta = 0
dtheta = math.pi / 32.0
a, b = (0, 1) # are there better params to use here?
spiral = lambda theta : a + b*theta
lastX, lastY = (x, y)
while True:
r = spiral(theta)
X = r * math.cos(theta)
Y = r * math.sin(theta)
if round(X) != lastX or round(Y) != lastY:
lastX, lastY = round(X), round(Y)
yield (r, (lastX, lastY))
theta += dtheta
plot(circle_around(0, 0, 10), "magnesium")
:
As you can see, none of the results that satisfy the interface I'm looking for have produced a circular spiral that covers all of the indices around 0, 0. F.J's is the closest, although WolframH's hits the right points, just not in spiral order.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
由于提到点的顺序并不重要,因此我只是按照它们在给定半径处出现的角度 (
arctan2
) 对它们进行排序。更改N
以获得更多积分。为
N=8
提供:更多积分
N=16< /code> (抱歉色盲):
这明显接近一个圆并按顺序击中每个网格点半径不断增加。
Since it was mentioned that the order of the points do not matter, I've simply ordered them by the angle (
arctan2
) in which they appear at a given radius. ChangeN
to get more points.Gives for
N=8
:More points
N=16
(sorry for the colorblind):This clearly approaches a circle and hits every grid point in order of increasing radius.
随着距离的增加而产生点的一种方法是将其分解为简单的部分,然后将各部分的结果合并在一起。很明显,itertools.merge 应该进行合并。 简单的部分是列,因为对于固定的x,点(x, y)可以通过仅查看y的值来排序。
下面是该算法的(简单的)实现。请注意,使用平方欧几里德距离,并且包括中心点。最重要的是,仅考虑 x 在
range(x_end)
中的点 (x, y),但我认为这适合您的用例(其中x_end
将是n
在上面的符号中)。编辑:测试代码:
测试结果(点按照
circle_around
产生的顺序编号):编辑2:如果你真的这样做需要
i
为负值,请将cirlce_around
中的range(end_x)
替换为range(-end_x, end_x)
功能。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 (wherex_end
would ben
in your notation above).Edit: Test code:
Result of test (points are numbered in the order they are yielded by
circle_around
):Edit 2: If you really do need negative values of
i
, replacerange(end_x)
withrange(-end_x, end_x)
in thecirlce_around
function.如果您遵循 x 和 y 螺旋索引,您会发现它们都可以以递归方式定义。因此,很容易想出一个递归生成正确索引的函数:
正如您可以从上图,这正是所要求的。
If you follow the x and y helical indices you notice that both of them can be defined in a recursive manner. Therefore, it is quite easy to come up with a function that recursively generates the correct indices:
As you can see from the image above, this gives exactly what's asked for.
以下是
circle_around()
的基于循环的实现:Here is a loop based implementation for
circle_around()
:虽然我不完全确定你想做什么,但我会这样开始:
我不确定你想要的螺旋有多少顺序,这重要吗?它必须是递增的 R 顺序吗?或者也许从特定的方位角开始顺时针?
R 曼哈顿的距离是多少?欧几里得?还有别的东西吗?
Although I'm not entirely sure what you are trying to do, I'd start like this:
I'm not sure how much order you want for your spiral, is it important at all? does it have to be in increasing R order? or perhaps clockwise starting at particular azimuth?
What is the distance measure for R, manhattan? euclidean? something else?
我要做的是使用阿基米德螺旋方程:
然后使用
cos 和 sin 将极坐标 (r,theta) 转换为 (x,y)位于
math
库中。然后将所得的 x 和 y 舍入为整数。您可以随后将 x 和 y 偏移起始索引,以获得数组的最终索引。但是,如果您只是想找到 f 返回 true 的第一个半径,我认为执行以下伪代码会更有益:
What I would do is use the equation for an Archimedean spiral:
and then convert the polar coordinates (r,theta) into (x,y), by using
cos
andsin
are in themath
library. Then round the resulting x and y to integers. You can offset x and y afterward by the starting index, to get the final indices of the array.However, if you are just interested in finding the first radius where f returns true, I think it would be more beneficial to do the following pseudocode:
好吧,我很尴尬,这是我迄今为止想到的最好的。但也许它会对你有帮助。由于它实际上不是循环迭代器,因此我必须接受您的测试函数作为参数。
问题:
这是代码。您问题的关键解决方案是顶层“spiral_search”函数,它在方形螺旋迭代器的顶部添加了一些额外的逻辑,以确保找到最近的点。
Well, I'm pretty embarrassed this is the best I have come up with so far. But maybe it will help you. Since it's not actually a circular iterator, I had to accept your test function as an argument.
Problems:
Here is the code. The key solution to your question is the top level "spiral_search" function which adds some extra logic on top of the square spiral iterator to make sure that the closest point is found.