问题陈述
我有一个圆,上面有一定数量(零个或多个)的点。这些位置是固定的。现在我需要在圆上放置另一组点,使得所有点都尽可能均匀地分布在圆周上。
目标
我的目标是开发一个算法,该算法接收一个角度列表(代表固定点),一个整数值(代表应该放置多少个额外的点),并返回一个包含新点角度的角度列表。
这些点不必真正均匀分布(彼此距离相等),但应该尽可能均匀地分布。大多数情况下不存在完美的解决方案,因为某些点是固定的。
所有角度的范围位于 -pi 和 +pi 之间。
示例
下面是我想要实现的一些示例:
fixed_points = [-pi, -pi/2, pi/2]
v v v
|---------|---------|---------|---------|
-pi -pi/2 0 pi/2 pi
fill_circle(fixed_points, 1)
# should return: [0]
fill_circle(fixed_points, 2)
# should return: [-pi/6, pi/6]
或:
fixed_points = [-pi, -pi*3/4, -pi/4]
v v v
|---------|---------|---------|---------|
-pi -pi/2 0 pi/2 pi
fill_circle(fixed_points, 6)
这个最后的示例应该返回类似于:一个点被设置在-pi*3/4和-pi/4之间,也就是-pi/2,并在-pi/4和+pi之间分配另外5个点(记住这是一个圆,所以在这种情况下-pi=+pi):
v v x v x x x x x
|---------|---------|---------|---------|
-pi -pi/2 0 pi/2 pi
之前的尝试
我最初使用了一个递归算法,首先搜索两个点之间的最大间隔,并将新点设置在它们中间。但是,这并没有给出令人满意的结果。例如,考虑以下配置,需要插入两个点:
v v v
|---------|---------|---------|---------|
-pi -pi/2 0 pi/2 pi
first step: insert right in the middle of largest interval
v v x v
|---------|---------|---------|---------|
-pi -pi/2 0 pi/2 pi
second step: insert right in the middle of largest interval
-> all intervals are evenly large, so one of them will be taken
v x v v v
|---------|---------|---------|---------|
-pi -pi/2 0 pi/2 pi
这不是一个很好的解决方案,因为它本可以更好地分布(参见上面:-pi/6和+pi/6)。
问题有点长,希望你能理解我的意图。
我不需要一个完整的工作算法,而是需要开发一个正确的思路。如果你愿意,也许可以提供一些伪代码作为提示。非常感谢给我指明正确方向的一些提示。提前感谢!
更新:已完成算法:
谢谢大家的回答!事实证明,我基本上只需要一个非贪婪版本的现有算法。我真的很喜欢haydenmuhls简化问题的想法,通过封装间隔/段类:
class Segment:
def __init__(self, angle, prev_angle, wrap_around):
self.angle = angle
self.length = abs(angle - prev_angle + \
(2*math.pi if wrap_around else 0))
self.num_points = 0
def sub_length(self):
return self.length / (self.num_points + 1)
def next_sub_length(self):
return self.length / (self.num_points + 2)
def add_point(self):
self.num_points += 1
这使得实际算法非常简单易懂:
def distribute(angles, n):
# No points given? Evenly distribute them around the circle
if len(angles) == 0:
return [2*math.pi / n * i - math.pi for i in xrange(n)]
# Sort the angles and split the circle into segments
s, pi, ret = sorted(angles), math.pi, []
segments = [Segment(s[i], s[i-1], i == 0) for i in xrange(len(s))]
# Calculate the length of all subsegments if the point
# would be added; take the largest to add the point to
for _ in xrange(n):
max(segments, key = lambda x: x.next_sub_length()).add_point()
# Split all segments and return angles of the points
for seg in segments:
for k in xrange(seg.num_points):
a = seg.angle - seg.sub_length() * (k + 1)
# Make sure all returned values are between -pi and +pi
ret.append(a - 2*pi if a > pi else a + 2*pi if a < -pi else a)
return ret