代码之家  ›  专栏  ›  技术社区  ›  n3rd

在最小直径的圆上定位正方形

  •  18
  • n3rd  · 技术社区  · 14 年前

    鉴于 n 边长正方形 L ,如何确定最小半径 R 使我可以沿着圆的周长均匀分布所有的正方形而不重叠?(限制条件:第一个正方形始终位于12点钟。)

    后续问题:我如何放置 n 高度相同的矩形 H 宽度 W ?

    example http://pub.n3rd.org/circle.png

    6 回复  |  直到 14 年前
        1
  •  12
  •   Carl Smotricz    14 年前

    可能有一种数学上聪明的方法可以做到这一点,但我不知道。 我认为这有点复杂,因为几何图形对于每个不同数量的正方形都是不同的;对于4,它是菱形,对于5,它是五角大楼,依此类推。

    我要做的是把这些正方形放在一个1单位的圆上(太小了,我知道,请忍受),均匀分布在上面。这很简单,只需将360度除以平方数就可以了。然后,只需测试所有的方块是否与相邻方块重叠;如果它们重叠,请增加半径。

    通过使用智能算法接近正确的大小,您可以使此过程比听起来更简单。我在考虑牛顿的算法:假设两个连续的猜测,其中一个太小,一个太大,您的下一个猜测需要是这两个的平均值。

    您可以迭代到您喜欢的任何精度。当猜测之间的距离小于某个任意小误差范围时停止。

    编辑 我有更好的解决方案:

    我在想,如果你问,“我怎么知道正方形是否重叠?”这给了我一个如何精确计算圆大小的想法,只需一步:

    把你的正方形放在一个非常小的圆圈上。你知道怎么做:计算你的360/N角与它相交的圆上的点,然后把正方形的中心放在那里。实际上,您还不需要放置正方形,接下来的步骤只需要中点。

    计算一个正方形与其相邻的最小距离:计算x的差和中点y的差,并取其中的最小值。x和y实际上只是圆上的余弦和正弦。

    您需要对其邻居(顺时针,比如说)求最小值 any square。因此,你需要绕着圆圈努力找到最小的一个。

    方格之间的最小(x或y)距离需要变为1.0。所以只要取最小距离的倒数,再乘以圆的大小。普雷斯托,你的圆圈是正确的大小。

    编辑

    在不失去一般性的情况下,我认为有可能把我的解决方案定下来一点,这样它就接近于编码了。这里有一个改进:

    • 假设正方形的尺寸为1,即每边的长度为1个单位。最后,您的方框肯定会大于1像素,但这只是缩放的问题。
    • 去掉角落的箱子:

      if(n<2)throw new illegalargumentexception();
      如果(n==2)返回0.5;//2个正方形正好适合半径为0.5的圆
      
    • 从一个圆的大小开始 r 0.5,对于任何数量的正方形来说,它肯定太小了>2.

      r=0.5;
      dmin=1.0;//假设最小距离为良好,则开始
      a=2×π/n;
      对于(p1=0.0;p1<=pi;p1+=a)//从角度0开始,尝试所有点直到一半
      //(是的,我们是从东开始,不是从北开始。没关系)
      p2=p1+a;//圆上的下一个点
      dx=abs(r*cos(p2)-r*cos(p1)
      dy=abs(r*sin(p2)-r*sin(p1)
      dmin=最小(dmin,dx,dy)
      }
      
      r=r/dmin;
      

    编辑

    我把它变成了真正的Java代码,得到了与此类似的东西来运行。此处显示代码和结果: http://ideone.com/r9aiu

    我使用gnuplot创建了图形输出。通过将输出的点集剪切粘贴到数据文件中,然后运行

    带BoxXyErrorBars的“5.dat”图 < /代码>

    文件中的 0.5 用于调整框的大小…懒惰但有效的解决方案。.5应用于中心的两侧,因此盒子的大小正好为1.0。

    唉, 我的算法不起作用。它使半径过大,因此放置盒子的距离比需要的要远得多。即使按2的比例缩小(在某些地方使用0.5可能是一个错误)也没有帮助。

    对不起,我放弃了。也许我的方法可以挽救,但它并不像我想象的那样有效。(

    )


    编辑

    我讨厌放弃。我正要离开我的电脑时,我想了一个办法挽救我的算法:

    算法正在调整x或y距离的 较小值,使其至少为1。很容易证明这很愚蠢。当你有很多盒子时,那么在圆的东边缘和西边缘,你的盒子几乎直接堆叠在一起,它们的x非常接近彼此,但是它们之间只有足够的y距离,可以避免触摸。

    所以…要使其工作,您必须将dx和dy的最大值缩放为(对于所有情况)至少是半径(或是半径的两倍?)

    这里是更正的代码: http://ideone.com/eq03g http://ideone.com/vryyo

    在gnuplot中再次测试,它产生了漂亮的小盒子圆圈,有时候只有1或2个盒子是完全接触的。 问题解决了!(强>:)

    (这些图片比高宽,因为gnuplot不知道我想要比例布局。想象一下,整个作品被压缩成一个方形。)

    不知道。 我认为这有点复杂,因为几何图形对于每一个不同数量的正方形都是不同的;对于4,它是菱形,对于5,它是五角大楼等等。

    我要做的是把这些正方形放在一个1单位的圆上(太小了,我知道,请忍受),均匀分布在上面。这很简单,只需将360度除以平方数就可以了。然后测试你所有的方块是否与它们的邻居重叠;如果它们重叠,增加半径。

    通过使用智能算法接近正确的大小,您可以使此过程比听起来更简单。我在想牛顿的算法:假设两个连续的猜测,其中一个太小,一个太大,你的下一个猜测需要是这两个的平均值。

    您可以迭代到您喜欢的任何精度。当猜测之间的距离小于某个任意小误差范围时停止。

    编辑 我有一个更好的解决方案:

    我在想,如果你问,“我怎么知道正方形是否重叠?”这让我想到了如何通过一个步骤精确计算圆的大小:

    把你的正方形放在一个非常小的圆圈上。你知道怎么做:计算你的360/N角与它相交的圆上的点,然后把正方形的中心放在那里。实际上,您还不需要放置正方形,接下来的步骤只需要中点。

    计算一个正方形与其相邻的最小距离:计算x的差和中点y的差,并取其中的最小值。x和y实际上只是圆上的余弦和正弦。

    你要的是 任何 与它的邻居成直角(比如顺时针方向)。所以你需要绕着这个圆圈找一个最小的。

    方格之间的最小(x或y)距离需要变为1.0。所以只要取最小距离的倒数,再乘以圆的大小。普雷斯托,你的圆圈大小合适。

    编辑

    在不失去一般性的情况下,我认为有可能把我的解决方案定下来一点,这样它就接近于编码了。这里有一个改进:

    • 假设正方形的尺寸为1,即每边的长度为1个单位。最后,您的方框肯定会大于1像素,但这只是一个缩放问题。
    • 去掉角落的箱子:

      if (n < 2) throw new IllegalArgumentException();
      if (n == 2) return 0.5; // 2 squares will fit exactly on a circle of radius 0.5
      
    • 从圆形开始 r 0.5,对于任何数量的平方来说都太小了>2。

      r = 0.5;
      dmin = 1.0; // start assuming minimum distance is fine
      a = 2 * PI / n;
      for (p1 = 0.0; p1 <= PI; p1+=a) { // starting with angle 0, try all points till halfway around
         // (yeah, we're starting east, not north. doesn't matter)
         p2 = p1 + a; // next point on the circle 
         dx = abs(r * cos(p2) - r * cos(p1))
         dy = abs(r * sin(p2) - r * sin(p1))
         dmin = min(dmin, dx, dy)
      }
      
      r = r / dmin;
      

    编辑

    我把它变成了真正的Java代码,得到了与此类似的东西来运行。此处显示代码和结果: http://ideone.com/r9aiu

    我使用gnuplot创建了图形输出。通过将输出的点集剪切粘贴到数据文件中,然后运行

    plot '5.dat' with boxxyerrorbars
    

    这个 .5 文件中的用于调整框的大小…懒惰但有效的解决方案。.5应用于中心的两侧,因此盒子的大小最终精确到1.0。

    唉, 我的算法不起作用 . 它使半径过大,因此放置盒子的距离比需要的要远得多。即使缩小2倍(在某些地方使用0.5可能是一个错误),也无济于事。

    对不起,我放弃了。也许我的方法可以挽救,但它并不像我想象的那样有效。:(


    编辑

    我讨厌放弃。我正要离开我的电脑时,我想了一个方法来挽救我的算法:

    算法正在调整 更小的 X或Y距离至少为1。很容易证明这很愚蠢。当你有很多盒子的时候,那么在圆的东边缘和西边缘,你会发现盒子几乎是直接叠在一起的,它们的x非常接近,但是它们之间只有足够的y距离可以避免接触。

    所以…要使其工作,必须缩放 最大限度 在dx和dy中(所有情况下)至少是半径(或者是半径的两倍?).

    更正的代码如下: http://ideone.com/EQ03g http://ideone.com/VRyyo

    在gnuplot中再次测试,它产生了漂亮的小盒子圆圈,有时候只有1或2个盒子是完全接触的。 问题解决了! :)

    (这些图片比高宽,因为gnuplot不知道我想要比例布局。想象一下,整个作品被压缩成一个方形。)

        2
  •  4
  •   Eyal Schneider    14 年前

    我会计算最小半径的上界,通过使用包围正方形的圆而不是正方形本身。

    我的计算结果是:

    rmin<=x/(sqrt(2)*sin(180/n))

    在哪里? x是平方边长,n是所需的平方数。

    我假设这些圆的位置使它们的中心落在大圆的圆周上。

    --编辑——

    利用下面评论中Dave的想法,我们也可以通过考虑圆圈在正方形内(因此半径为x/2)来计算一个很好的下界。这个界限是:

    rmin>=x/(2*sin(180/n))。

        3
  •  1
  •   High Performance Mark    14 年前

    如前所述,在圆的圆周上等距定位n个点的问题很小。问题的(不是非常困难的)部分是找出圆的半径,以给出一个令人满意的广场布局。我建议你遵循其他答案中的一个,并考虑到正方形在一个足够大的圆形“缓冲区”内,以容纳正方形和足够的空间来满足你的审美要求。然后检查公式 chord length 在相邻广场的中心之间。现在你有一个角度,在圆的中心,被圆心之间的弦所对,可以很容易地用三角形的三角学计算圆的半径。

    关于你的后续问题:我建议你解决边长平方的问题。 min(h,w) 在圆上,然后将正方形转换为矩形,将圆转换为偏心距为h/w(或w/h)的椭圆。

        4
  •  0
  •   Unreason    14 年前

    我会这样解决:

    为了找出半径r和长度l之间的关系,让我们分析无量纲表示法。

    • 把圆心放在一个圆上(x1,y1)…(xn,yn)
    • 从每个中心到第i个正方形的右下角和第i+1个正方形的左上角
    • 这两点要么等于x,要么等于y,两者中取较小的l。
    • 对于每个中心都要重复这个过程,得到最小L的就是最终的解。

    这是最优解,可以用r=f(l)来求解。 通过调整xlr[i]和yul[i+1]的公式,解决方案可以适应矩形。

    将尝试给出一些伪代码。

    编辑:
    程序中有一个错误,右下和左上不是相邻两个正方形/矩形的最接近点。

        5
  •  0
  •   Mau    14 年前

    假设您解决了3或4个平方的问题。

    如果你有 n >=5个正方形,将一个正方形放在圆的顶部,另一个正方形将落在与圆同心的笛卡尔平面的第一象限中。

    问题是找到半径 R 对于圆,使圆的左侧靠近顶部,而顶部圆的右侧不“交叉”。

    这个 X 顶圆右侧坐标为 X1 = L 2,在哪里 L 是正方形的边。这个 X 靠近顶部圆的左边的坐标是 X2 = R 余弦 - L 2,在哪里 R 是半径和 是每对方形中心之间的角度( = 360 n 度数)。

    所以我们需要解决 X1 & = X2 从而导致

    R = L COS .

    L 大家都知道,所以我们结束了:—)

        6
  •  0
  •   Svante    14 年前

    从任意圆开始(例如,直径为 (* n l) )将正方形均匀地放置在圆周上。然后通过每一对相邻的正方形:

    • 计算连接其中点的直线,
    • 计算该线与中间方的交点(m1和m2为中点,s1和s2为与该方的相应交点:

                      S2         S1
      M1--------------*----------*---------------M2
      
      ------------------------
      |                      |
      |                      |
      |                      |
      |                      |
      |          M1          |
      |           \          |
      |            \         |
      |      -------*------- +--------
      |      |       \       |       |
      |      |        \      |       |
      -------+---------*------       |
             |          \            |
             |           M2          |
             |                       |
             |                       |
             |                       |
             |                       |
             -------------------------
      
    • 计算使s1和s2同时存在所需的比例因子(简单地计算m1-s1和s2-m2与m1-m2之和的比率),以及

    最后根据找到的比例因子的最大值缩放圆。

    编辑: 这是确切的解决方案。但是,稍微考虑一下就可以进一步优化速度:

    • 您只需要对最接近45度的正方形进行此操作(如果 n 是偶数)。45度和135度(如果 n 很奇怪;事实上,您可能会证明只有其中一个是必需的)。
    • 为大 n ,圆上正方形的最佳间距将快速接近正方形对角线的长度。因此,您可以预先计算几个小的比例因子 n (最多12个左右),然后与对角线有足够好的近似值。