【问题标题】:drawing circle without floating point calculation没有浮点计算的画圆
【发布时间】:2019-08-03 13:19:41
【问题描述】:

这是一个常见的面试问题(根据一些面试网站),但我在互联网上找不到正常的答案 - 有些是错误的,有些指向我希望在面试中不需要的复杂理论(比如 Bresenham 算法)。

问题很简单:

圆方程为:x2 + y2 = R2。 给定 R,尽可能不使用任何以 0,0 为中心的圆 浮点数(没有三角函数、平方根等,只有整数)

【问题讨论】:

  • 首先,您可以在任何计算中使用 3 作为 PI 的粗略近似值...
  • 这将帮助您准确地如何画一个圆?
  • 我在下面提供了一个答案,但感觉需要说明这一点。除非您正在面试专门处理图形或数学的工作,否则这个问题非常糟糕。与解决它所需的几何/代数技巧相比,它的编程部分微不足道。

标签: algorithm geometry


【解决方案1】:

类似 Bresenham 的算法可能是预期的答案,并且可以在没有“复杂理论”的情况下推导出来。从圆上的点(x,y) 开始:(R,0) 并保持值d=x^2+y^2-R^2,最初为 0。D 是从当前点到圆的平方距离。我们增加 Y,并根据需要减少 X 以保持 D 最小:

// Discretize 1/8 circle:
x = R ; y = 0 ; d = 0
while x >= y
  print (x,y)
  // increment Y, D must be updated by (Y+1)^2 - Y^2 = 2*Y+1
  d += (2*y+1) ; y++
  // now if we decrement X, D will be updated by -2*X+1
  // do it only if it keeps D closer to 0
  if d >= 0
    d += (-2*x+1) ; x--

【讨论】:

  • if d >= x,你的意思是if d >= 0
  • 好吧,修复它...谢谢
【解决方案2】:

老实说,Midpoint circle algorithm 还不够吗?只需在所有象限中镜像即可。无论如何 -- 除非你想找一份窗口应用程序测试员的工作,否则Bresenham's Line Algorithm 并不是复杂的理论。

【讨论】:

  • 如果您不学习计算机图形学,Bresenham's 就不会教,所以我认为不是每个人都知道。另一方面,这个面试问题通常被称为“一般练习”
  • 嗯...我在高中时被教过布雷森汉姆线算法:/。
【解决方案3】:

来自this page上的第二种方法:

对于每个像素,评估 x2+y2 看看是否 它在从 R2-R+1 到 R2+R 包括的。如果是这样,给像素上色 屏幕,如果没有,不要。

在上述页面上给出了进一步的细节和解释,但关键是你正在寻找距离原点在 R-0.5 和 R+0.5 之间的像素,所以距离的平方是 x2+y2,阈值距离的平方为 R2-R+0.25 和 R2+R+0.25。

对于其他方法,谷歌“仅使用整数算术绘制圆”。

【讨论】:

    【解决方案4】:

    相当老的问题,但我将尝试在 python 中提供带有可视化测试的最终解决方案,作为 Bresenham 算法的替代方案 - 此任务的最佳和最短解决方案。我认为这个想法也可以占有一席之地,也许更容易理解,但需要更多的代码。有人可能也最终得到了这个解决方案。

    这个想法基于以下事实:

    1. 圆上的每个点与圆中心点的距离相同
    2. 一个圆包含 4 个象限,如果 r 是半径并且中心点在 (r, r ) 点。
    3. 圆是连续图形,每个点可以有 8 个相邻点。如果在一个方向上做圆周运动,我们只对三个点感兴趣——3 个在相反方向,2 个离中心太远。例如点 (r, 0) 指向 (2r, r) 有趣的点将是 (r + 1, 1), (r, 1) 和 (r + 1, 0)
    import matplotlib.pyplot as plt
    from itertools import chain
    
    def get_distance(x1, y1, x2, y2):
        """
            Calculates squared distance between (x1, y1) and (x2, y2) points
        """
        return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
    
    def get_next_point(x, y, dx, dy, cx, cy, r):
        """
            Returns the next circle point base on base point (x, y), 
            direction (dx, dy), circle central point (cx, cy) and radius r
        """
        r2 = r * r
    
        # three possible points
        x1, y1 = x + dx, y + dy
        x2, y2 = x, y + dy
        x3, y3 = x + dx, y
    
        # calculate difference between possible point distances 
        # with central point and squared radius
        dif1 = abs(get_distance(x1, y1, cx, cy) - r2)
        dif2 = abs(get_distance(x2, y2, cx, cy) - r2)
        dif3 = abs(get_distance(x3, y3, cx, cy) - r2)
    
        # choosing the point with minimum distance difference
        diff_min = min(dif1, dif2, dif3)
    
        if diff_min == dif1:
            return x1, y1
        elif diff_min == dif2:
            return x2, y2
        else:
            return x3, y3
    
    def get_quadrant(bx, by, dx, dy, cx, cy, r):
        """
            Returns circle quadrant starting from base point (bx, by), 
            direction (dx, dy), circle central point (cx, cy) and radius r
        """
        x = bx
        y = by
    
        # maximum or minimum quadrant point (x, y) values
        max_x = bx + dx * r
        max_y = by + dy * r
    
        # choosing only quadrant points
        while (dx * (x - max_x) <= 0) and (dy * (y - max_y) <= 0):
            x, y = get_next_point(x, y, dx, dy, cx, cy, r)
            yield x, y
    
    def get_circle(r, cx, cy):
        """
            Returns circle points (list) with radius r and center point (cx, cy)
        """
         north_east_quadrant = get_quadrant(cx, cy - r, 1, 1, cx, cy, r)
         south_east_quadrant = get_quadrant(cx + r, cy, -1, 1, cx, cy, r)
         south_west_quadrant = get_quadrant(cx, cy + r, -1, -1, cx, cy, r)
         north_west_quadrant = get_quadrant(cy - r, cy, 1, -1, cx, cy, r)
    
        return chain(north_east_quadrant, south_east_quadrant,
                     south_west_quadrant, north_west_quadrant)
    
    # testing
    
    r = 500
    
    circle_points = get_circle(r, r, r)
    
    for x, y in circle_points:
        plt.plot([x], [y], marker='o', markersize=3, color="red")
    
    plt.show()
    

    【讨论】:

    • 只要接受问题的假设complex theory [like Bresenham],我同意您建议自己的简短解决方案(并且有义务表扬您对docstrings 和cmets 的使用) .我想我在a)前提b)你的措辞best and [shortest]冒犯了。
    【解决方案5】:

    我将使用 Bresenham 的圆绘制算法或中点圆绘制算法。两者都产生相同的坐标点。并且由于圆的八个八分圆之间的对称性,我们只需要生成一个八分圆并将其反射并复制到所有其他位置即可。

    【讨论】:

      【解决方案6】:

      这将是我的面试答案(没有研究,这是现场)...

      设置两个嵌套的 for 循环,共同在 {-R, -R, 2R, 2R} 定义的正方形上循环。对于每个像素,计算 (i^2 + j^2) 其中 i 和 j 是您的循环变量。如果这在 R^2 的某个容差范围内,则将该像素着色为黑色,如果不是,则不理会该像素。

      我懒得确定该容忍度应该是多少。您可能需要将最后计算的值存储为零,以确定哪个像素最能代表圆......但这个基本方法应该工作得很好。

      【讨论】:

        【解决方案7】:

        有没有人考虑过他们可能正在寻找横向的答案,例如“用指南针和铅笔”或“使用一卷透明胶带的内部作为模板”。

        每个人都认为所有问题都必须用计算机来解决。

        【讨论】:

          【解决方案8】:

          您可以使用一阶泰勒近似轻松计算 x^2= r^2- y^2 中的 x

          sqrt(u^2 + a) = u + a / 2u

          这是 Mathematica 中的一个程序(简短,但可能不太好)

           rad=87; (* Example *)
           Calcy[r_,x_]:= ( 
               y2 = rad^2 - x^2;
               u = Ordering[Table[ Abs[n^2-y2], {n,1,y2}]] [[1]]; (* get the nearest perfect square*)
               Return[ u-(u^2-y2)/(2 u) ]; (* return Taylor approx *)
           )
          
           lista = Flatten[Table[{h Calcy[rad, x], j x}, {x, 0, rad}, {h, {-1, 1}}, {j, {-1, 1}}], 2];
           ListPlot[Union[lista, Map[Reverse, lista]], AspectRatio -> 1];
          

          这是结果

          还不错恕我直言...我对图形算法一无所知...

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2023-04-01
            • 2012-03-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-01-01
            相关资源
            最近更新 更多