我有一组带有x和y坐标的点,如下图所示。 9个点的坐标存储在列表中,如下所示:
L = [[5,2], [4,1], [3.5,1], [1,2], [2,1], [3,1], [3,3], [4,3] , [2,3]]
这个想法是从原点顺时针对点进行排序。在这种情况下,原点是着色的点,并且具有指示排序方向的箭头。不要担心创建确定原点的方法,因为它已经解决了。
因此,在排序后,列表L
应如下所示:
L = [[2,3], [3,3], [4,3], [5,2], [4,1], [3.5,1], [3,1], [2,1], [1,2]]
请注意,x和y坐标不会更改。存储顺序有哪些变化。
您是否知道python语言中针对此问题的算法,脚本或方法?
答案 0 :(得分:9)
通过一点三角学,它并不那么难。也许你知道,但两个(标准化)向量之间的角度是acos(vec1 * vec2)
。但是,这仅计算投影角度,但可以使用atan2
来计算方向感知角度。
对于这意味着计算它然后将其用作key
进行排序的函数将是一个好方法:
import math
pts = [[2,3], [5,2],[4,1],[3.5,1],[1,2],[2,1],[3,1],[3,3],[4,3]]
origin = [2, 3]
refvec = [0, 1]
def clockwiseangle_and_distance(point):
# Vector between point and the origin: v = p - o
vector = [point[0]-origin[0], point[1]-origin[1]]
# Length of vector: ||v||
lenvector = math.hypot(vector[0], vector[1])
# If length is zero there is no angle
if lenvector == 0:
return -math.pi, 0
# Normalize vector: v/||v||
normalized = [vector[0]/lenvector, vector[1]/lenvector]
dotprod = normalized[0]*refvec[0] + normalized[1]*refvec[1] # x1*x2 + y1*y2
diffprod = refvec[1]*normalized[0] - refvec[0]*normalized[1] # x1*y2 - y1*x2
angle = math.atan2(diffprod, dotprod)
# Negative angles represent counter-clockwise angles so we need to subtract them
# from 2*pi (360 degrees)
if angle < 0:
return 2*math.pi+angle, lenvector
# I return first the angle because that's the primary sorting criterium
# but if two vectors have the same angle then the shorter distance should come first.
return angle, lenvector
sorted
运行:
>>> sorted(pts, key=clockwiseangle_and_distance)
[[2, 3], [3, 3], [4, 3], [5, 2], [4, 1], [3.5, 1], [3, 1], [2, 1], [1, 2]]
并且在原点周围有一个矩形网格,这也可以按预期工作:
>>> origin = [2,3]
>>> refvec = [0, 1]
>>> pts = [[1,4],[2,4],[3,4],[1,3],[2,3],[3,3],[1,2],[2,2],[3,2]]
>>> sorted(pts, key=clockwiseangle_and_distance)
[[2, 3], [2, 4], [3, 4], [3, 3], [3, 2], [2, 2], [1, 2], [1, 3], [1, 4]]
即使您更改参考向量:
>>> origin = [2,3]
>>> refvec = [1,0] # to the right instead of pointing up
>>> pts = [[1,4],[2,4],[3,4],[1,3],[2,3],[3,3],[1,2],[2,2],[3,2]]
>>> sorted(pts, key=clockwiseangle_and_distance)
[[2, 3], [3, 3], [3, 2], [2, 2], [1, 2], [1, 3], [1, 4], [2, 4], [3, 4]]
感谢@Scott Mermelstein
提供了更好的功能名称,感谢@f5r5e5d
提出atan2
建议。
答案 1 :(得分:1)
这应该说明问题,给出一个可视化工具
但是每次获取相同距离的一组点的正确入口点都不起作用
import random
import pylab
import cmath
from itertools import groupby
pts = [(random.randrange(-5,5), random.randrange(-5,5)) for _ in range(10)]
# for this problem complex numbers are just too good to pass up
z_pts = [ i[0] + 1j*i[1] for i in pts if i != (0, 0)]
z_pts.sort(key = lambda x: abs(x))
gpts = [[*g] for _, g in groupby(z_pts, key = lambda x: abs(x) ) ]
print(*gpts, sep='\n')
spts = [1j/2]
for e in gpts:
if len(e) > 1:
se = sorted(e, key = lambda x: cmath.phase(-x / spts[-1]))
spts += se
else:
spts += e
print(spts)
def XsYs(zs):
xs = [z.real for z in zs]
ys = [z.imag for z in zs]
return xs, ys
def SpiralSeg(a, b):
'''
construct a clockwise spiral segment connecting
ordered points a, b specified as complex numbers
Inputs
a, b complex numbers
Output
list of complex numbers
'''
seg = [a]
if a == 0 or a == b:
return seg
# rotation interpolation with complex numbers!
rot = ( b / a ) ** ( 1 / 30 )
# impose cw rotation direction constraint
if cmath.phase( b / a ) > 0: # add a halfway point to force long way around
plr = cmath.polar( b / a )
plr = (plr[0]**(1/2), plr[1] / 2 - 1 * cmath.pi ) # the rotor/2
a_b = cmath.rect(*plr) * a # rotate the start point halfway round
return SpiralSeg(a, a_b) + (SpiralSeg(a_b, b))
for _ in range(30):
a *= rot
seg.append(a)
return seg
segs = [SpiralSeg(a, b) for a, b in zip(spts, spts[1:])]
pylab.axes().set_aspect('equal', 'datalim')
pylab.scatter(*XsYs(z_pts))
for seg in segs:
pylab.plot(*XsYs(seg))
[(1-2j), (-2-1j)]
[(2-3j)]
[(1+4j)]
[(3+3j)]
[(-3-4j), (3-4j), (4-3j)]
[(1-5j)]
[(-4-4j)]
[0.5j, (-2-1j), (1-2j), (2-3j), (1+4j), (3+3j), (-3-4j), (3-4j), (4-3j), (1-5j), (-4-4j)]
[-1j]
[(-1-1j)]
[(-1-2j), (-1+2j), (2+1j)]
[(-4+0j)]
[(1-4j)]
[-5j, (-4-3j)]
[(1-5j)]
[0.5j, -1j, (-1-1j), (-1-2j), (2+1j), (-1+2j), (-4+0j), (1-4j), (-4-3j), -5j, (1-5j)]
答案 2 :(得分:0)
按角度排序是不够的
我们应该按极坐标和到原点的距离对字典点进行全部排序
我们按极角排序,如果出现平局,我们按距原点的距离排序