格雷厄姆扫描问题

Posted

技术标签:

【中文标题】格雷厄姆扫描问题【英文标题】:Trouble with Graham's Scan 【发布时间】:2015-04-05 01:42:44 【问题描述】:

目前正在与 Convex HUll 一起使用 Graham's Scan。我是一名学生,所以我试图自己完成它,但是我一直在筛选多个站点以找到答案。简而言之,我有我的构造函数,一个来自文件,一个随机生成,可以工作,所以我能够创建一个点数组。下一步是实现快速排序,按极角排序。这是通过比较器类完成的。比较器类是我卡住的地方,我们被告知使用点比较和交叉比较来进行角度比较,但我很迷茫。

/**
 * Use cross product and dot product to implement this method.  Do not take square roots 
 * or use trigonometric functions. See the PowerPoint notes on how to carry out cross and 
 * dot products. 
 * 
 * Call comparePolarAngle() and compareDistance(). 
 * 
 * @param p1
 * @param p2
 * @return -1 if one of the following three conditions holds: 
 *                a) p1 and referencePoint are the same point but p2 is a different point; 
 *                b) neither p1 nor p2 equals referencePoint, and the polar angle of 
 *                   p1 with respect to referencePoint is less than that of p2; 
 *                c) neither p1 nor p2 equals referencePoint, p1 and p2 have the same polar 
 *                   angle w.r.t. referencePoint, and p1 is closer to referencePoint than p2. 
 *         0  if p1 and p2 are the same point  
 *         1  if one of the following three conditions holds:
 *                a) p2 and referencePoint are the same point but p1 is a different point; 
 *                b) neither p1 nor p2 equals referencePoint, and the polar angle of
 *                   p1 with respect to referencePoint is greater than that of p2;
 *                c) neither p1 nor p2 equals referencePoint, p1 and p2 have the same polar
 *                   angle w.r.t. referencePoint, and p1 is further to referencePoint than p2. 
 *                   
 */
public int compare(Point p1, Point p2)
    if(p1 == referencePoint && p2 != referencePoint)
        return -1;
     else if(p1 == p2)
        return 0;
     else 

    
    return 0; 



/**
 * Compare the polar angles of two points p1 and p2 with respect to referencePoint.  Use 
 * cross products.  Do not use trigonometric functions. 
 * 
 * Precondition:  p1 and p2 are distinct points. 
 * 
 * @param p1
 * @param p2
 * @return   -1  if p1 equals referencePoint or its polar angle with respect to referencePoint
 *               is less than that of p2. 
 *            0  if p1 and p2 have the same polar angle. 
 *            1  if p2 equals referencePoint or its polar angle with respect to referencePoint
 *               is less than that of p1. 
 */
public int comparePolarAngle(Point p1, Point p2)
    // TODO 
    return 0; 



/**
 * Compare the distances of two points p1 and p2 to referencePoint.  Use dot products. 
 * Do not take square roots. 
 * 
 * @param p1
 * @param p2
 * @return   -1   if p1 is closer to referencePoint 
 *            0   if p1 and p2 are equidistant to referencePoint
 *            1   if p2 is closer to referencePoint
 */
public int compareDistance(Point p1, Point p2)
    int distance = 0;

    return distance; 

就是这样,我只是在比较方法上经历了一些小事情,然后才卡住。

quickSort 和 partition 方法是相当标准的,但我会添加它们,以便你们可以广泛了解所有内容:

/**
 * Sort the array points[] in the increasing order of polar angle with respect to lowestPoint.  
 * Use quickSort.  Construct an object of the pointComparator class with lowestPoint as the 
 * argument for point comparison.  
 * 
 * Ought to be private, but is made public for testing convenience.   
 */
public void quickSort()

    // TODO 



/**
 * Operates on the subarray of points[] with indices between first and last. 
 * 
 * @param first  starting index of the subarray
 * @param last   ending index of the subarray
 */
private void quickSortRec(int first, int last)

    // TODO



/**
 * Operates on the subarray of points[] with indices between first and last.
 * 
 * @param first
 * @param last
 * @return
 */
private int partition(int first, int last)

    // TODO 
    return 0; 

我知道我基本上需要启动并运行 Compare 类,然后才能启动快速排序方法,但我觉得我什至不知道如何使用点/交叉比较,所以我真的很迷茫.

如果有人愿意提供帮助,我将非常感激! 非常感谢您的观看,祝您有个愉快的夜晚。

【问题讨论】:

【参考方案1】:

在所有这些方法中,当您需要查看两个 Point 对象是否相等时,您应该使用 Point 的 equals 方法,而不是 "==" :

if(p1.equals(p2)) 
    //code

实现比较

请注意,您的 compare 方法需要在其实现中使用 equals()、comparePolarAngle() 和 compareDistance()。最后一组条件(返回 1)也可以在 else 语句中处理。

public int compare(Point p1, Point p2) 
   if(p1.equals(p2)) 
      return 0;
   
   else if(p1.equals(referencePoint) ||
          (!p1.equals(referencePoint) && !p2.equals(referencePoint) && comparePolarAngle(p1, p2) == -1) ||
          (!p1.equals(referencePoint) && !p2.equals(referencePoint) && comparePolarAngle(p1, p2) == 0 && compareDistance(p1, p2) == -1))
   
      return -1;
   
   else 
      return 1;
   

实现比较距离

这里我们需要的主要信息是如何仅使用点积来确定从 referencePoint 到 Point 对象的向量长度。首先,让我们实现一个辅助方法,它以两个 Points 作为输入并将点积作为整数值返回。

private int dotProduct(Point p1, Point p2) 
   int p1X = p1.getX() - referencePoint.getX();
   int p1Y = p1.getY() - referencePoint.getY();
   int p2X = p2.getX() - referencePoint.getX();
   int p2Y = p2.getY() - referencePoint.getY();
   //compensate for a reference point other than (0, 0)

   return (p1X * p2X) + (p1Y * p2Y);  //formula for dot product

那么我们如何使用它来计算向量的长度呢?如果我们将 Point 与自身进行点积,我们得到 (xx) + (yy),这是勾股定理 (a^2 + b^2 = c^) 的左侧2)。因此,如果我们调用 dotProduct(p1, p1),我们将得到其向量长度的平方。现在让我们实现 compareDistance。

public int compareDistance(Point p1, Point p2) 
   if(dotProduct(p1, p1) == dotProduct(p2, p2)) 
      return 0;
   
   else if(dotProduct(p1, p1) < dotProduct(p2, p2)) 
      return -1;
   
   else 
      return 1;
   

不需要取点积的平方根,您只需比较平方长度即可。另请注意,这里可以使用“==”,因为我们比较的是整数,而不是点数。

实现 comparePolarAngle

与点积一样,让我们​​实现一个计算两个输入点的叉积的辅助方法。

private int crossProduct(Point p1, Point p2) 
   int p1X = p1.getX() - referencePoint.getX();
   int p1Y = p1.getY() - referencePoint.getY();
   int p2X = p2.getX() - referencePoint.getX();
   int p2Y = p2.getY() - referencePoint.getY();
   //compensate for a reference point other than (0, 0)

   return (p1X * p2Y) - (p2X * p1Y);  //formula for cross product

另一种写出两点叉积结果的方式是|p1||p2|sin(theta) where |p1|是 p1 向量的长度,|p2|是 p2 的向量的长度,theta 是从 p1 到 p2 的角度。

相对于参考点具有相同极角的两个点的 theta 值为零。 sin(0) = 0,所以极角相同的两点的叉积为零。

如果 p1 相对于参考点的极角小于 p2 的极角,则 p1 到 p2 的角度为正。对于 0

如果 p1 相对于参考点的极角大于 p2 的极角,则 p1 到 p2 的角度为负。对于 -180

使用这些信息,我们最终可以实现 comparePolarAngle。

public int comparePolarAngle(Point p1, Point p2) 
   if(crossProduct(p1, p2) == 0) 
      return 0;
   
   else if(p1.equals(referencePoint) || crossProduct(p1, p2) > 0) 
      return -1;
   
   else 
      return 1;
   

我将把快速排序的实现留给你,因为我不知道你的 Point 对象是如何存储、访问和比较的。

【讨论】:

以上是关于格雷厄姆扫描问题的主要内容,如果未能解决你的问题,请参考以下文章

凸壳算法 - 格雷厄姆扫描最快的比较功能?

在 C# 中实现格雷厄姆扫描

格雷厄姆扫描算法 -> sqrt 和 arctan2 巨大的价值

凸包中的额外点(使用格雷厄姆扫描)错误+ java

Python中的重复直到或等效循环[重复]

在Java中按极角对点进行排序