绘制圆没有浮点计算

时间:2010-05-31 03:35:49

标签: algorithm geometry

这是一个常见的访谈问题(根据一些访谈网站)但我在互联网上找不到正常的答案 - 有些是错的,有些人指的是复杂的理论我期望在访谈中不需要(比如Bressenham算法)。 / p>

问题很简单:

  

圆方程为:x ^ 2 + y ^ 2 = R ^ 2。   给定R,尽可能最好地绘制0,0居中的圆而不使用任何   浮点(没有三角形,平方根等,只有整数)

8 个答案:

答案 0 :(得分:8)

类似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--

答案 1 :(得分:6)

老实说,Midpoint circle algorithm不够吗?只需在所有象限中镜像它。并且无论如何 no - 除非您试图获得作为窗口应用程序测试人员的工作,Bresenham's Line Algorithm并不是复杂的理论。

答案 2 :(得分:5)

来自this page的第二种方法:

  

对于每个像素,进行评估   x 2 + y 2 并查看是否   它在...的范围内   R 2 -R + 1至R 2 + R.   包括的。如果是这样,请为像素着色   屏幕,如果没有,不要。

在上述页面上给出了进一步的细节和解释,但关键是你正在寻找距离原点R-0.5和R + 0.5之间距离的像素,因此距离平方为x 2 < / sup> + y 2 且阈值距离平方为R 2 -R + 0.25且R 2 + R + 0.25。

对于其他方法,Google“仅使用整数运算绘制圆圈”。

答案 3 :(得分:4)

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

这个想法基于以下事实:

  1. 圆上的每个点与圆心点相同的距离
  2. 圆圈包含4个象限,如果r是半径且中心点在(r,r),则以(r,0),(2r,r),(r,2r)和(0,r)点开始和结束)点。
  3. 圆是一个连续的数字,每个点可以有8个相邻点。如果在一个方向上移动圆圈只有三个点对我们来说很有趣 - 3个位于相反方向,2个位于距离中心太远的位置。例如对于方向为(2r,r)的点(r,0),有趣的点将是(r + 1,1),(r,1)和(r + 1,0)
  4. 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()
    

答案 4 :(得分:3)

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

答案 5 :(得分:2)

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

设置两个嵌套的for循环,它们共同循环遍历由{-R,-R,2R,2R}定义的方块。对于每个像素,计算(i ^ 2 + j ^ 2)其中i和j是您的循环变量。如果这在R ^ 2的某个容差范围内,则将该像素设置为黑色,如果不是则将该像素单独留下。

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

答案 6 :(得分:2)

使用一阶泰勒近似可以很容易地计算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];

这是结果

alt text http://i47.tinypic.com/246nfxt.jpg

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

答案 7 :(得分:2)

有没有人认为他们可能正在寻找一个侧面的答案,例如“用指南针和铅笔”或“使用一卷卖点作为模板”。

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