Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 格雷厄姆扫描问题的分数很高_C#_Wpf_Algorithm_Computational Geometry - Fatal编程技术网

C# 格雷厄姆扫描问题的分数很高

C# 格雷厄姆扫描问题的分数很高,c#,wpf,algorithm,computational-geometry,C#,Wpf,Algorithm,Computational Geometry,当我的列表中有很多点时,我的graham扫描算法有一个问题,但每次都可以很好地处理少量的点。我做了一些截图: 工作:(300分) 不工作(5000分) 角度计算: public static double angle(MyVector3D vec1, MyVector3D vec2) { return Math.Atan2(vec2.Y - vec1.Y, vec2.X - vec1.X) * 180 / Math.PI; } 按极轴角度排序的点取决于最大Y点: //bubble

当我的列表中有很多点时,我的graham扫描算法有一个问题,但每次都可以很好地处理少量的点。我做了一些截图:

工作:(300分)

不工作(5000分)

角度计算:

public static double angle(MyVector3D vec1, MyVector3D vec2)
{
    return Math.Atan2(vec2.Y - vec1.Y, vec2.X - vec1.X) * 180 / Math.PI;

}
按极轴角度排序的点取决于最大Y点:

//bubblesort
    private void sortList()
    {
        double temp = 0.0;
        MyVector3D tempVector = new MyVector3D();
        for (int i = 1; i < points.Count; i++)
        {
            for (int j = 1; j < points.Count - 1; j++)
            {
                if (angles[j] < angles[j + 1])
                {
                    temp = angles[j + 1];
                    tempVector = points[j + 1];
                    angles[j + 1] = angles[j];
                    points[j + 1] = points[j];
                    angles[j] = temp;
                    points[j] = tempVector;
                }
            }   
        }
格雷厄姆扫描算法:

for (int i = 2; i < points.Count; i++)
    {
        while (ccw(points[M - 1], points[M], points[i]) > 0)
        {
            if (M > 1)
            {
                points.RemoveAt(M);
                M -= 1;
                i--;
            }
            else if (i == points.Count - 1)
            {
                break;
            }

            else
            {
                i += 1;
            }
        }
        //goodPoints.Add(points[M]);
        //listBoxInfos.Items.Add("g" + (int)points[M].X + "," + (int)points[M].Y + "," + 0);
        //listBoxInfos.Items.Add("ccw" + ccwTest);
        M += 1;

    }
for(int i=2;i0)
{
如果(M>1)
{
移除点(M);
M-=1;
我--;
}
else if(i==points.Count-1)
{
打破
}
其他的
{
i+=1;
}
}
//添加(点[M]);
//添加(“g”+(int)点[M].X+”,“+(int)点[M].Y+”,“+0”);
//listBoxInfos.Items.Add(“ccw”+ccwTest);
M+=1;
}
我真的不知道为什么我的程序在800+点上爆炸。。。很难调试,因为该算法在300400500上运行得很好。。。要点

Ty获取信息。

根据这一点:和其他问题,Graham扫描算法的实现至少有两个问题,我认为您的数字越低,就越“幸运”:

1) 你在外for和else中都在增加i(一般来说,你每隔一点就跳过一次测试)


2) 我不相信你删除失败点的方法,是的,点不在外壳“此处”,但可能是外壳上的一个点,你需要向下交换这些点或使用基于堆栈的方法。

维基百科上的算法被破坏。它不处理多个点彼此共线且与最小点共线的情况。例如,以下测试用例将失败:

        Vector3[] points3 = new Vector3[] 
        {
            new Vector3( 1, 1, 0),
            new Vector3( 5, 5, 0),
            new Vector3( 3, 3, 0),
            new Vector3( 2, 2, 0),
            new Vector3( 1, 1, 0),
            new Vector3( 1, 10, 0),

        };
问题是,当沿着点行进时,如果点位于外壳的最后两个点之间,则可能需要放弃当前点,而不是延伸外壳或替换外壳上的最后一个点。(只有当点也与最小点共线时,才可能发生这种情况,否则前面的角度排序将阻止这种双回退。)所示的伪代码中没有这种逻辑


我还认为Wikipedia算法可能会严重处理浮点舍入错误。特别是检查ccw我想这超出了评论部分的范围,因此我将对此作出回答:


设点[M-1]与点[i]处于相同的坐标

然后知道:ccw=((vec2.X-vec1.X)*(vec3.Y-vec1.Y))-((vec2.Y-vec1.Y)*(vec3.X-vec1.X)),点[M-1]对应于vec1,点[i]对应于vec3

用vec1替换vec3我们得到:ccw=((vec2.X-vec1.X)*(vec1.Y-vec1.Y))-((vec2.Y-vec1.Y)*(vec1.X-vec1.X)),我们可以清楚地看到这将是==0。既然你肯定了,如果ccw==0,这些点将保持在凸壳中,那么这将使壳的一部分成为两条完全重叠的线,除非我在什么地方弄错了


谢谢。我检查了算法,你是对的,库根。问题是 当ccw==0时,主要问题是如何使用 ccw==0,不属于凸包的一部分,保留, 因为同一向量中的3个点可能是凸包的一部分,也可能不是。 知道怎么解决吗

(您可能想看看dbc的代码,但下面是我的答案)

我认为您不希望在凸包中保留ccw==0的任何链接。 当ccw==0时,向量之间的角度为180度或0度

0deg的情况就像我在回答开始时所说的(这些点实际上不需要重叠,这样更容易证明)

至于180度的情况,你需要做一些额外的代码。我将引用dbc:

可能需要放弃当前点,而不是 延伸外壳或替换外壳上的最后一点(如果 点位于外壳的最后两个点之间


如果你想让测试更容易,你也可以提取所有凸包点的位置,然后用这些点重放。

bubblesort中的
点是什么?在(…)
循环中,它不应该以“0”而不是“1”开头吗?它是cavas上的点列表(每个点携带x、y、z(0)坐标)。角度列表是最大Y点和其他点之间的计算角度列表。当我对角度进行排序时,我也会对点列表进行排序,然后用最大Y点和点替换点[0][最大Y点索引)在graham扫描开始之前。查看wikipedia伪代码,我发现当ccw==0时,您使用的是ccw>0而不是ccw,而while循环没有执行,这意味着该点保留在列表中,并且是凸包的一部分。让点[M-1]与点[I]处于相同的坐标处。然后知道:ccw=((vec2.X-vec1.X)*(vec3.Y-vec1.Y))-((vec2.Y-vec1.Y)*(vec3.X-vec1.X)),点[M-1]对应于vec1,点[i]对应于vec3。用vec1替换vec3我们得到:ccw=((vec2.X-vec1.X)*(vec1.Y-vec1.Y))-((vec2.Y-vec1.Y)*(vec1.X-vec1.X))我们可以清楚地看到,这将是==0。既然您确认,如果ccw==0,那么点将保留在凸面外壳中,那么这将使外壳的一部分成为两条完全重叠的线,除非我在某个地方弄错了。Ty tolanj寻求建议。我将尝试修复这些问题,并在明天再次粘贴代码。经过一些测试后,您的代码将显示出来似乎是失败的一些要点。考虑输入点:0):{x=11.581625 y= -110.983437 } [ 1 ]:{x=11.1816254 y=-108.983437 }[2 ]:{x= 11.88781 y= -113.115852 }[3 ]:{x= 11.587204 y= -111.015938 }[un]:{x=yy=-}}[y]:{x= yy=-113.11
        Vector3[] points3 = new Vector3[] 
        {
            new Vector3( 1, 1, 0),
            new Vector3( 5, 5, 0),
            new Vector3( 3, 3, 0),
            new Vector3( 2, 2, 0),
            new Vector3( 1, 1, 0),
            new Vector3( 1, 10, 0),

        };
    public static IList<Vector3> GrahamScanCompute(IList<Vector3> initialPoints)
    {
        if (initialPoints.Count < 2)
            return initialPoints.ToList();

        // Find point with minimum y; if more than one, minimize x also.
        int iMin = Enumerable.Range(0, initialPoints.Count).Aggregate((jMin, jCur) =>
        {
            if (initialPoints[jCur].Y < initialPoints[jMin].Y)
                return jCur;
            if (initialPoints[jCur].Y > initialPoints[jMin].Y)
                return jMin;
            if (initialPoints[jCur].X < initialPoints[jMin].X)
                return jCur;
            return jMin;
        });
        // Sort them by polar angle from iMin, 
        var sortQuery = Enumerable.Range(0, initialPoints.Count)
            .Where((i) => (i != iMin)) // Skip the min point
            .Select((i) => new KeyValuePair<double, Vector3>(Math.Atan2(initialPoints[i].Y - initialPoints[iMin].Y, initialPoints[i].X - initialPoints[iMin].X), initialPoints[i]))
            .OrderBy((pair) => pair.Key)
            .Select((pair) => pair.Value);
        List<Vector3> points = new List<Vector3>(initialPoints.Count);
        points.Add(initialPoints[iMin]);     // Add minimum point
        points.AddRange(sortQuery);          // Add the sorted points.

        int M = 0;
        for (int i = 1, N = points.Count; i < N; i++)
        {
            bool keepNewPoint = true;
            if (M == 0)
            {
                // Find at least one point not coincident with points[0]
                keepNewPoint = !NearlyEqual(points[0], points[i]);
            }
            else
            {
                while (true)
                {
                    var flag = WhichToRemoveFromBoundary(points[M - 1], points[M], points[i]);
                    if (flag == RemovalFlag.None)
                        break;
                    else if (flag == RemovalFlag.MidPoint)
                    {
                        if (M > 0)
                            M--;
                        if (M == 0)
                            break;
                    }
                    else if (flag == RemovalFlag.EndPoint)
                    {
                        keepNewPoint = false;
                        break;
                    }
                    else
                        throw new Exception("Unknown RemovalFlag");
                }
            }
            if (keepNewPoint)
            {
                M++;
                Swap(points, M, i);
            }
        }
        // points[M] is now the last point in the boundary.  Remove the remainder.
        points.RemoveRange(M + 1, points.Count - M - 1);
        return points;
    }

    static void Swap<T>(IList<T> list, int i, int j)
    {
        if (i != j)
        {
            T temp = list[i];
            list[i] = list[j];
            list[j] = temp;
        }
    }

    public static double RelativeTolerance { get { return 1e-10; } }

    public static bool NearlyEqual(Vector3 a, Vector3 b)
    {
        return NearlyEqual(a.X, b.X) && NearlyEqual(a.Y, b.Y);
    }

    public static bool NearlyEqual(double a, double b)
    {
        return NearlyEqual(a, b, RelativeTolerance);
    }

    public static bool NearlyEqual(double a, double b, double epsilon)
    {
        // See here: http://floating-point-gui.de/errors/comparison/
        if (a == b)
        { // shortcut, handles infinities
            return true;
        }

        double absA = Math.Abs(a);
        double absB = Math.Abs(b);
        double diff = Math.Abs(a - b);
        double sum = absA + absB;
        if (diff < 4*double.Epsilon || sum < 4*double.Epsilon)
            // a or b is zero or both are extremely close to it
            // relative error is less meaningful here
            return true;

        // use relative error
        return diff / (absA + absB) < epsilon;
    }

    static double CCW(Vector3 p1, Vector3 p2, Vector3 p3)
    {
        // Compute (p2 - p1) X (p3 - p1)
        double cross1 = (p2.X - p1.X) * (p3.Y - p1.Y);
        double cross2 = (p2.Y - p1.Y) * (p3.X - p1.X);
        if (NearlyEqual(cross1, cross2))
            return 0;
        return cross1 - cross2;
    }

    enum RemovalFlag
    {
        None,
        MidPoint,
        EndPoint
    };

    static RemovalFlag WhichToRemoveFromBoundary(Vector3 p1, Vector3 p2, Vector3 p3)
    {
        var cross = CCW(p1, p2, p3);
        if (cross < 0)
            // Remove p2
            return RemovalFlag.MidPoint;
        if (cross > 0)
            // Remove none.
            return RemovalFlag.None;
        // Check for being reversed using the dot product off the difference vectors.
        var dotp = (p3.X - p2.X) * (p2.X - p1.X) + (p3.Y - p2.Y) * (p2.Y - p1.Y);
        if (NearlyEqual(dotp, 0.0))
            // Remove p2
            return RemovalFlag.MidPoint;
        if (dotp < 0)
            // Remove p3
            return RemovalFlag.EndPoint;
        else
            // Remove p2
            return RemovalFlag.MidPoint;
    }