背景:
我正在编写一个程序来处理与各种规则形状的顶点网络相关的大量数据。我有一个工作的发生器,它根据一系列用户输入参数产生一个与所述形状的顶点相对应的笛卡尔坐标列表。然后将数据传递给过滤器,过滤器清除重复的条目,对数据和各种其他功能进行排序,从中将清理后的数据送到画布模块,画布模块循环并绘制顶点。
题:
我需要实现一个有效循环坐标的新过滤器,将每对与每对其他对比较,即 (x1,y1)
- >(x2,y2)
至 (x1,y1)
- >(xn,yn)
, (x2,y2)
- >(x3,y3)
至 (x2,y2)
- >(xn,yn)
等等,用于所有条目,例如,如果之间的关系 (x1,y1)
和 (x5,y5)
适合 [(x5-x1)^2+(y5-y1)^2]=vertex_spacing^2
然后将两组坐标与它们各自的列表条目编号配对,并附加到一个新的列表中,其中一个条目的格式为: [(x1,y1), (x5,y5), 0, 4]
例如。实现这一目标的最有效方法是什么?
我的尝试:
我在这里和各种指南上看了很多处理列表的方法。我尝试嵌套'for'和'if'循环,但发现这个方法可以工作时会导致运行时间过长,并试图将问题分解成许多较小的for循环。
进一步说明:
这样做的最终目的是为前端界面元素使用生成的坐标,并根据需要进行保存和导入。列表的功能位置为0和4 [(x1,y1), (x5,y5), 0, 4]
是为了使接口能够对坐标进行分组,以便以后在canvas对象中使用。该方法应该能够处理数千个坐标。
提前感谢您的帮助,我当然愿意改进我提供的措辞/信息和/或添加示例代码,如果不清楚我在问什么 - 我还是很新的! :)
你基本上检查的是:
对于每个顶点 v
,找到所有顶点 u
这样的 u
在半径圆上 vertex_spacing
周围 v
。
如果您的积分分布不是所有积分都很接近,我想到两种加速搜索的方法:
加速此过程的最简单方法是按x坐标对点进行排序。这样,您可以跳过许多比较。举个简单的例子,假设x坐标是 [1, 2, 10, 15, 18, 20, 21]
和 vertex_spacing = 5
。您只需要比较第一个顶点和第二个顶点,因为所有其他顶点显然位于第一个顶点周围的圆圈之外。
请注意,如果所有点都靠得很近,这种方法就没用了。换句话说,如果 vertex_spacing = 25
,你不能跳过任何比较。
沿着同样的路线,你可以使用二维 k-d树。这相当于排序方法,但在两个方面。给定一个顶点 (x, y)
和 vertex_spacing = v
,你必须检查范围内的所有点 ([x-v, x+v], [y-v, y+v])
。使用与以前相同的示例,假设第一个点具有坐标 (1, 0)
和第二个 (2, 10)
,没有必要将第一个顶点与任何东西进行比较。
这两种方法都是启发式方法,并没有对最坏情况下的运行时间进行任何改进(完全相反:你也有排序/构建k-d树的开销),但如果顶点通常至少是 vertex_space
除此之外,这可以加快你的搜索速度。
我太慢了,无法击败Heuster对算法的描述,但这里是按x排序方法的实现:
def pairs(coords, vertex_spacing):
results = []
vsquared = vertex_spacing * vertex_spacing
coords = sorted(coords)
for ia, (xa, ya) in enumerate(coords):
for ib, (xb, yb) in enumerate(coords[ia:]):
dx = xb - xa
if dx > vertex_spacing:
break
dy = yb - ya
if dx * dx + dy * dy == vsquared:
results.append([(xa, ya), (xb, yb), ia, ia + ib])
return results
......在这里它正在行动:
>>> coords = list((x, y) for x in range(100) for y in range(100))
>>> p = pairs(coords, 5)
>>> from random import choice
>>> choice(p)
[(93, 36), (96, 40), 9336, 9640]
>>> choice(p)
[(9, 57), (13, 54), 957, 1354]
>>> choice(p)
[(46, 69), (46, 74), 4669, 4674]
在我的机器上 pairs(coords, 5)
需要1.5秒来检查10,000个坐标对(和0.15秒来检查2,500个)。
编辑:我忘了添加 ia
至 ib
补偿在切片上的枚举 - 现在修复。
算法中最慢的部分是单独处理 X 和 ÿ 坐标和斜边计算。通过使用Python的本机复数类型可以加快这两个方面:
>>> from itertools import starmap
>>> parray = list(starmap(complex, [(5, 1), (8.5, 3), (3.75, 4.25)]))
>>> a = parray[0]
>>> b = parray[1]
>>> a
(5+1j)
>>> b
(8.5+3j)
>>> a-b
(-3.5-2j)
>>> abs(a-b)
4.031128874149275