【问题标题】:How to evenly distribute points around the perimeter of a rectangle如何在矩形周边均匀分布点
【发布时间】:2021-03-01 12:06:20
【问题描述】:

我正在寻找一种沿矩形周长的一部分分布点的方法。这些点之间的距离需要均匀。

我有一个矩形(通常是正方形)边界和 2 个点(pspe)沿该边界标记允许的点范围。这里我用红色标记了允许的范围:

我需要沿该段放置n 点(通常为 1-3)。这些点需要以d 的距离均匀分布。所以n0..n1n1..n2等之间的距离应该都是d。出于分布的目的,边界点也计算在内,因此第一个点和最后一个点与ps/pe 之间的距离也应该是d

这似乎是一项简单的任务,但我很快意识到幼稚的方法在这里行不通。取段的长度并除以n+1 不会考虑角。例如:n = 1,把点太靠近pe

我的数学很生疏(日常工作通常不需要太多),但我尝试了几种不同的方法,但都没有完全奏效。我能够使用向量求解n = 1,方法是找到pspe 之间的中点,找到一个垂直向量,然后将其与线段相交,如下所示。如果n 是别的东西,或者即使可以做到,我不知道如何使这种方法发挥作用。

最后一点,如果完全均匀分布是不切实际的,那么一个足够好的近似值就可以了。理想情况下,近似值在整个范围内的偏差大致相同(而不是说,边缘更差)。

【问题讨论】:

  • 有趣的问题!范围是否总是正好包含矩形的一个角,还是也可以是 2 或 3?
  • 它可能更多,例如 peps 是 SE 和 SW 角,并且允许的段除了 S 边缘。但是,由于这些点的使用方式,任何包含多个角的情况都可能只使用镜像的 1 角解决方案。
  • 你的意思是d是几何距离还是马氏距离?
  • 几何距离。

标签: algorithm math path 2d point


【解决方案1】:

我将建议一个算法,但由于推导在数学上有点混乱,我没有足够的时间仔细考虑并仔细检查它的正确性。另外,我可能已经包括了一些冗余检查,如果证明某些适当的不等式可能会变得多余,并且可以证明在合理假设下可能始终存在解决方案的存在。我相信这个想法是正确的,但我可能在写这篇文章时犯了一些错误,所以要小心。

因为根据您的评论,由于对称性,在沿正方形边界的线段内只有一个角足以解决其余情况,因此我将专注于一个角情况。

您的具有一个 90 度角的多边形段被分成一对垂直的直线段,第一个长度为 l1,第二个长度为 l2。这两个长度是给你的。您还想在总长度为l1 + l2 的多边形段上添加给定数量的n 点,以使任意两个连续点之间的欧几里得直线距离相同。打电话给那个未知的距离d。当您这样做时,您将在l1 上得到n1 未知长度的完整段dn2l2 上的未知长度d 的完整段,这样

n1 + n2 = n

一般而言,您还会在l1 上得到一个额外的长度为d1 <= d 的段,一端位于90 度角。类似地,您还将在l2 上有一个长度为d2 <= d 的额外段,其一端位于90 度角。因此,d1d2 两个线段共用一个公共端并且相互垂直,因此它们形成一个直角三角形。根据毕达哥拉斯定理,这两段满足方程

d1^2 + d2^2 = d^2

如果我们结合到目前为止的所有方程和信息,我们得到一个方程和限制系统,它们是:

n1*d + d1 = l1
n2*d + d2 = l2
d1^2 + d2^2 = d^2
n1 + n2 = n
n1 and n2 are non-negative integers

变量d, d1, d2, n1, n2 是未知的,而l1, l2, n 是给定的。 从前两个等式,你可以表达d1d2 并代入第三个等式:

d1 = l1 - n1*d
d2 = l2 - n2*d
(l1 - n1*d)^2 + (l2 - n2*d)^2 = d^2
n1 + n2 = n
n1 and n2 are non-negative integers

在特殊情况下,当一个人只想添加一个点时,即n = 1,一个人要么有n1 = n = 1,要么有n2 = n = 1,这取决于分别是l1 > l2还是l1 <= l2。 说l1 > l2。然后n1 = n = 1n2 = 0 所以

d1 = l1 - d
d2 = l2
(l1 - d)^2 + l2^2 = d^2

展开方程,化简并求解d

l1^2 - 2*l1*d + d^2 + l2^2 = d^2
l1^2 + l2^2 - 2*l1*d = 0
d = (l1^2 + l2^2) / (2*l1)

接下来,让我们回到一般情况。你必须解决系统

(l1 - n1*d)^2 + (l2 - n2*d)^2 = d^2
n1 + n2 = n
n1 and n2 are non-negative integers

变量d, n1, n2 是未知的,而l1, l2, n 是给定的。展开第一个方程:

l1^2  -  2 * l1 * n1 * d  +  n1^2 * d^2  +  l2^2  -  2 * l2 * n2 * d  +  n2^2 * d^2 = d^2
n1 + n2 = n
n1 and n2 are non-negative integers

并将术语组合在一起

(n1^2 + n2^2 - 1) * d^2  - 2 * (l1*n1 + l2*n2) * d  +  (l1^2 + l2^2) = 0
n1 + n2 = n
n1 and n2 are non-negative integers

第一个方程是d中的二次方程

(n1^2 + n2^2 - 1) * d^2  - 2 * (l1*n1 + l2*n2) * d  +  (l1^2 + l2^2) = 0

通过二次公式,您期望有两个解决方案(通常,您选择哪个是正数。 如果两者都是肯定的并且d < l1d < l2,你可能有两种解决方案):

d = ( (l1*n1 + l2*n2) +- sqrt( (l1*n1 + l2*n2)^2 - (l1^2 + l2^2)*(n1^2 + n2^2 - 1) ) ) / (n1^2 + n2^2 - 1)
n1 + n2 = n
n1 and n2 are non-negative integers

现在,如果你能找到合适的n1n2,你可以使用上面的二次公式计算出必要的d。 为了存在解决方案,平方根中的表达式必须是非负的,所以你有不等式限制

d = ( (l1*n1 + l2*n2) +- sqrt( (l1*n1 + l2*n2)^2 - (l1^2 + l2^2)*(n1^2 + n2^2 - 1) ) ) / (n1^2 + n2^2 - 1)
(l1*n1 + l2*n2)^2 - (l1^2 + l2^2)*(n1^2 + n2^2 - 1) >= 0
n1 + n2 = n
n1 and n2 are non-negative integers

化简不等式:

(l1*n1 + l2*n2)^2 - (l1^2 + l2^2)*(n1^2 + n2^2 - 1) = (l1^2 + l2^2) - (l1*n2 - l2*n1)^2

这将我们带到以下系统

d = ( (l1*n1 + l2*n2) +- sqrt( (l1^2 + l2^2) - (l1*n2 - l2*n1)^2 ) ) / (n1^2 + n2^2 - 1)
(l1^2 + l2^2) - (l1*n2 - l2*n1)^2 >= 0
n1 + n2 = n
n1 and n2 are non-negative integers

分解不等式,

d = ( (l1*n1 + l2*n2) +- sqrt( (l1^2 + l2^2) - (l1*n2 - l2*n1)^2 ) ) / (n1^2 + n2^2 - 1)
(sqrt(l1^2 + l2^2) - l1*n2 + l2*n1) * (sqrt(l1^2 + l2^2) + l1*n2 - l2*n1) >= 0
n1 + n2 = n
n1 and n2 are non-negative integers

所以这些限制有两种情况:

案例一:

d = ( (l1*n1 + l2*n2) +- sqrt( (l1^2 + l2^2) - (l1*n2 - l2*n1)^2 ) ) / (n1^2 + n2^2 - 1)
sqrt(l1^2 + l2^2) - l1*n2 + l2*n1 >= 0 
sqrt(l1^2 + l2^2) + l1*n2 - l2*n1 >= 0
n1 + n2 = n
n1 and n2 are positive integers

或 案例二:

d = ( (l1*n1 + l2*n2) +- sqrt( (l1^2 + l2^2) - (l1*n2 - l2*n1)^2 ) ) / (n1^2 + n2^2 - 1)
sqrt(l1^2 + l2^2) - l1*n2 + l2*n1 <= 0 
sqrt(l1^2 + l2^2) + l1*n2 - l2*n1 <= 0
n1 + n2 = n
n1 and n2 are positive integers

我们专注于案例 1,发现案例 2 是不可能的。首先表达n2 = n - n1,然后将其替换为两个不等式中的每一个,并在每个不等式的一侧隔离n1。此过程产生:

案例1:

d = ( (l1*n1 + l2*n2) +- sqrt( (l1^2 + l2^2) - (l1*n2 - l2*n1)^2 ) ) / (n1^2 + n2^2 - 1)
( l1*n - sqrt(l1^2 + l2^2) ) / (l1 + l2) <=  n1  <= ( l1*n + sqrt(l1^2 + l2^2) ) / (l1 + l2) 
n1 + n2 = n
n1 and n2 are positive integers

可以看到情况 2 反转了不等式,这是不可能的,因为左边小于右边。

所以算法可能是这样的:

function d = find_d(l1, l2, n)
{
   if n = 1 and l1 > l2 { 
      return d = (l1^2 + l2^2) / (2*l1)
   } else if n = 1 and l1 <= l2 {
      return d = (l1^2 + l2^2) / (2*l2)
   }
   for integer n1 >= 0 starting from floor( ( l1*n - sqrt(l1^2 + l2^2) ) / (l1 + l2) ) to floor( ( l1*n + sqrt(l1^2 + l2^2) ) / (l1 + l2) ) + 1 
   {
      n2 = n - n1
      D = (l1^2 + l2^2) - (l1*n2 - l2*n1)^2
      if D >= 0
      {
         d1 = ( (l1*n1 + l2*n2) - sqrt( D ) ) / (n1^2 + n2^2 - 1)  
         d2 = ( (l1*n1 + l2*n2) + sqrt( D ) ) / (n1^2 + n2^2 - 1) 
         if 0 < d1 < max(l1, l2) {       
            return d1
         } else if  0 < d2 < max(l1, l2) {
            return d2
         } else {
            return "could not find a solution"
         }
      }
   }
}

【讨论】:

  • 我在 1 角的情况下工作得很好,似乎每次都能给出准确的结果。您引用了我在我的一个 cmets 中所说的方法,它可以被镜像以解决 >1 个角。我刚刚被告知规格是错误的,实际上并非如此(尽管它最多只能是 2 个角)。我会将此标记为答案,因为它确实解决了最初的问题,但是任何关于如何扩展它以适用于这种情况的提示将不胜感激!
  • @DanSC 我很高兴它有帮助,这是一个有趣的问题。正如你所看到的,一个角落已经让事情变得复杂了,我没有尝试延伸到两个角落。此外,我检查了二次公式和一些不等式,但可能存在更严格的不等式来排除某些情况。我只是不想过多地讨论。完成一些与工作相关的任务后,我会尝试考虑两个角落。
  • @DanSC 我添加了另一个答案,其中包含两个角的算法的初步版本。它在 python 中,我让它画了一些图片作为健全性检查。我没有添加算法背后的数学,但如果你愿意,我可以在周末添加一些解释。
【解决方案2】:

这是一个初步版本,所以我建议谨慎对待。我没有足够的时间来检查算法是否可能存在一些接近退化的情况,为此可能需要在某处添加一些带有 if 语句的短循环。但是,一般来说,这可能几乎总是有效。我只是发布了一个 python 实现,但是当我找到更多时间并且如果你想要我的话,我可以写下这个算法背后的数学。这个算法的一些想法可以简化前面的一个角。

import numpy as np
import math
import matplotlib.pyplot as plt


def sq_root(x, m, K):
  return math.sqrt(x**2 - (K - m*x)**2)

def f(x, n, L):
  return sq_root(x, n[0], L[0]) + sq_root(x, n[2], L[2]) + n[1]*x - L[1]

def df(x, n, L):
  return ((1-n[0]**2)*x + L[0]*n[0])/sq_root(x, n[0], L[0]) + ((1-n[2]**2)*x + L[2]*n[2])/sq_root(x, n[2], L[2]) + n[1]

#Solving the nonlinear equation for d by using Newton's method:
def solve_f(n, L):
  x = sum(L) / (sum(n) + 2)
  y = f(x, n, L)
  while abs(y) >= 0.0000001:
    x = x - y / df(x, n, L)
    y = f(x, n, L) 
  return x - y / df(x, n, L)

def find_n(L, N):
  x0 = sum(L) / (N + 1)
  # x <= x0
  n = np.array([0,0,0])
  n[0] = math.floor(L[0]/x0)
  n[2] = math.floor(L[2]/x0)
  n[1] = N - n[0] - n[2] - 1
  return n

def find_d(L, N):
  if N==1:
    d2 = (L[2]**2 + L[1]**2 - L[0]**2)/(2*L[1])
    return math.sqrt(L[0]**2 + d2**2), np.array([0,0,0])
  n = find_n(L, N)
  return solve_f(n, L), n

def find_the_points(L, N):
  d, n = find_d(L, N)
  d2 = math.sqrt(d**2 - (L[0]-n[0]*d)**2)
  #d3 = math.sqrt(d**2 - (L[2]-n[2]*d)**2)
  p = np.zeros((sum(n) + 3, 2))
  p[ 0] = np.array([0, L[1]-L[0]])
  p[-1] = np.array([L[1], L[1]-L[2]])
  e_x = np.array([1,0])
  e_y = np.array([0,1])
  corner = np.array([0,L[1]]) 
  for i in range(n[0]):
    p[i+1] = p[0] + (i+1)*d*e_y
  for i in range(n[1]+1):
    p[n[0]+i+1] = corner + (d2 + i*d)*e_x
  for i in range(n[2]):
    p[-(2+i)] = p[-1] + (i+1)*d*e_y
  return p, d, n 

'''
Test example:
'''

# lengths of the three straight segments along the edges of a square of edge_length L2:
L1 = 5
L2 = 7
L3 = 3
L = np.array([L1, L2, L3]) 

N = 7
# N = number of points to be added
# If there are two corners then number of segments aligned with edges of square is N - 1
# total number of equidistant segments is N + 1
# n = n[0], n[1], n[2] represents the number of segments aligned with each 
# striaght segment from the rectangular polyline along square's boundary


points, d, n = find_the_points(L, N)
print(points)
print(d)
print(n)

plt.figure()
plt.plot(points[:,0], points[:,1]) 
for j in range(points.shape[0]):
  plt.plot(points[j,0], points[j,1], 'ro')
axx = plt.gca()
axx.set_aspect('equal')
plt.show() # if you need...

【讨论】:

  • @DanSc 我发现了几个错误并修复了它们。
  • 我已将其合并到我之前的代码中,据我所知,它对于我现在正在测试的所有案例都是准确的。如果我找到它们,我相信我可以消除任何错误。感谢您抽出宝贵时间做出更新的答案,这非常有见地。
  • @DanSC 感谢您提出有趣的问题!我还进行了更多测试,它可以准确地工作(在函数'solve_f(n,L)'的while循环中用'abs(y)> ='纠正'y > ='之后,这很重要)。唯一可能(或不可能)发生的事情是,如果 n[0], n[1], n[2] 的计算不起作用,则可能需要设置 n[0] = n[0]+1 并分别调整其余的 n。
【解决方案3】:

毕达哥拉斯来救援。

考虑n = 2有一个角1的情况。其余情况可能会相似,只是有更多的边缘情况和可能的配置2

让我们将垂直线段的长度命名为i,将水平线段的长度命名为j
我们正在寻找一个点X,Y,这样距离dist(ps,x) = dist(x,y) = dist(y,pe)

首先,假设拐角位于点 X 和 Y 之间。在这种情况下,我们正在寻找这个方程的解: x2 = (i - x)2 + (j - x)2 其中 i 和 j 是已知的.

如果上述方程无解,则意味着角必须位于ps和X之间或Y和pe之间。我将只介绍 ps 和 X 的情况,因为另一个是对称的:
x2 = i2 + (j-2x)2 是这种情况的等式。

对于更多的点和角,只会有更多可能的点和角配置。然而,由于距离应该相等并且长度是已知的,一系列二次方程很可能就足够了。不过,5+ 点和三个角的情况会有点不确定。


1n=1 的情况与我将介绍的n=2 的不对称变体几乎相同。
2这会让它们变得相当丑陋。

【讨论】:

    【解决方案4】:

    根据问题,任何一侧的点都是等距的。只需猜测距离并使用二进制搜索来找到任何准确度的解决方案。确定每个矩形边上有多少点有一个稍微棘手的部分。 (希望只是“稍微”。:)

    【讨论】:

      猜你喜欢
      • 2019-12-23
      • 2012-05-21
      • 2020-12-24
      • 2012-06-26
      • 1970-01-01
      • 1970-01-01
      • 2021-02-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多