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

Posted

技术标签:

【中文标题】在 C# 中实现格雷厄姆扫描【英文标题】:Implementing Graham Scan in C# 【发布时间】:2013-08-16 15:59:05 【问题描述】:

我正在尝试从 Wikipedia 伪代码实现 Graham Scan,但在将内容转换为 C# 时遇到了一些麻烦。也许你不介意看看?

这是我所拥有的:

public class GrahamScan
    
        public static List<CoordinatesD> Scan(List<CoordinatesD> coordinateslist) 
        


                int coordinatesize = coordinateslist.Count;
                CoordinatesD[] coordinatesarray = new CoordinatesD[coordinatesize];
                for (int i = 0; i < coordinatesize; i++)
                
                    coordinatesarray[i] = coordinateslist[i];
                

                //swap points[1] with the point with the lowest y-coordinate;
                int lowestyindex = LowestY(coordinatesarray);
                Swap(coordinatesarray[0], coordinatesarray[lowestyindex]);

                //sort points by polar angle with points[1];
                coordinatesarray = SortByPolarAngle(coordinatesarray[0], coordinateslist);


                // We want points[0] to be a sentinel point that will stop the loop.
                coordinatesarray[0] = coordinatesarray[coordinatesize];

                // M will denote the number of points on the convex hull.
                int numpointsonhull = 1;
                for (int i = 2; i < coordinatesize; i++) 
                
                    // Find next valid point on convex hull.
                    while(CCW(coordinatesarray[numpointsonhull-1], coordinatesarray[numpointsonhull], coordinatesarray[i]) <= 0)
                    
                          if(numpointsonhull > 1) 
                          
                                  numpointsonhull -= 1;
                          
                          // All points are collinear
                          else if (i == coordinatesize)
                           
                                  break;
                          
                          else 
                          
                                  i += 1;
                          
                    

                    // Update M and swap points[i] to the correct place.
                    numpointsonhull += 1;
                    //swap(points[M],points[i]);
                    Swap(coordinatesarray[numpointsonhull], coordinatesarray[i]);
                

            List<CoordinatesD> pointsonhulllist = new List<CoordinatesD>();

            for (int i = 0; i < numpointsonhull; i++) 
            
                pointsonhulllist.Add(coordinatesarray[i]);

            

            return pointsonhulllist;



        

        /// <summary>
        /// Swaps two points.
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        private static void Swap(CoordinatesD p1, CoordinatesD p2) 
        
            CoordinatesD temp = p1;
            p1 = p2;
            p2 = temp;

        

        /// <summary>
        /// Attempts to Sort by Polar Angle, with respect to p1.
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="points"></param>
        /// <returns></returns>
        private static CoordinatesD[] SortByPolarAngle(CoordinatesD p1, List<CoordinatesD> points) 
        

            CoordinatesD[] sortedpoints = new CoordinatesD[points.Count];
            int sortedpointiterator = 0;

            while(true) 
            
                int current = 0;
                for (int i = 0; i < points.Count; i++) 
                
                    if (p1.PolarAngle - points[i].PolarAngle < p1.PolarAngle - points[current].PolarAngle)
                    
                        current = i;
                    

                

                sortedpoints[sortedpointiterator] = points[current];
                sortedpointiterator++;
                points.RemoveAt(current);

                if (points.Count == 0)
                
                    break;
                
            



            return sortedpoints;
        
        /// <summary>
        /// Finds the index of the CoordinateD with the lowest Y.
        /// </summary>
        /// <param name="coords"></param>
        /// <returns></returns>
        private static int LowestY(CoordinatesD[] coords) 
        
            int index = 0;
            for (int i = 0; i < coords.Length; i++) 
             
                if(coords[i].Y < coords[index].Y) 
                
                    index = i;
                
            
            return index;
        




        // Three points are a counter-clockwise turn if ccw > 0, clockwise if
        // ccw < 0, and collinear if ccw = 0 because ccw is a determinant that
        // gives the signed area of the triangle formed by p1, p2 and p3.
        private static double CCW(CoordinatesD p1, CoordinatesD p2, CoordinatesD p3)
        
            return (p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X);
        
    

它正在使用这个类:

public class CoordinatesD : iCoordinates
    
        private double latitude = 0.0;
        private double longitude = 0.0;

        public enum ServiceType  Google = 0, Bing = 1 ;

        public double Latitude 
        
            get  return latitude; 
        

        public double Longitude
        
            get  return longitude; 
        

        public double X 
        
            get  return longitude; 
        

        public double Y 
        
            get  return latitude; 
        

        public double PolarAngle  get  return CalculatePolarAngle();  

        public CoordinatesD(double latitude, double longitude) 
        
            this.latitude = latitude;
            this.longitude = longitude;
        


        private double CalculatePolarAngle() 
         

            double polarangle = Math.Atan(latitude / longitude);
            if (polarangle > 0.0)
            
                return polarangle;
            
            return polarangle + Math.PI;

        

        public CoordinatesD Change(double changelat, double changelong) 
        

            CoordinatesD newCoordinates = new CoordinatesD(this.latitude + changelat, this.longitude + changelong);

            return newCoordinates;

        

        public string ToJScriptString(ServiceType service) 
        

            string jscriptstring = string.Empty;

            switch (service) 
             
                case ServiceType.Bing:
                    jscriptstring = String.Format("new Microsoft.Maps.Location(0,1)", this.latitude.ToString(), this.longitude.ToString());
                    break;
                case ServiceType.Google:
                    jscriptstring = String.Format("new google.maps.LatLng(0,1)", this.latitude.ToString(), this.longitude.ToString());
                    break;
                default:
                    break;          
            


            return jscriptstring;
        




    

代码喜欢爆炸,主要是因为我读过十几个完全不同的伪实现,没有一个能充分解释事情。我遇到了数组的越界错误,我什至不确定应该是坐标,还是坐标 + 1,还是坐标 - 1,或者坐标 + killme -> 最初是“N”或“N” +1',但正如你所看到的,我正在慢慢失去对这个翻译的想法。

【问题讨论】:

你能告诉我它在哪里爆炸吗? 你试过this吗? 目前它在 'coordinatesarray[0] = coordinatesarray[coordinatesize];' 上爆炸与索引超出范围异常一致(它应该这样做,因为它显然是在访问数组之外​​的东西)。我遇到的部分问题是翻译这段代码:“让 N = 点数”和“让点 [N+1] = 点数组”......就像他们使用从 0 开始然后使用 1有时基于索引。 【参考方案1】:

这个问题很可能已经死了,但是它出现在 *** 的“相关”问题中,因为我在这里添加了 Graham 扫描的 c# 实现:Graham scan issue at high amount of points。 Wikipedia 算法在点和起始最小点共线的情况下确实存在错误。

【讨论】:

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

如何在 IBM Worklight 上的多个平台中实现条码扫描器?

在 C# 中实现接口与显式实现接口 [重复]

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

如何在 C# 中实现 ORM

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

在并行位片代码中实现快速计数器