检测周期的算法可以找到很少的周期

时间:2015-12-22 10:10:50

标签: c# algorithm

我有以下算法:

class CycleData : List<IntPoint>{

   public IntPoint startPoint;
   public Boolean ended=false;
    public CycleData(IntPoint startpoint) { startPoint = startpoint; base.Add(startpoint); }
}
class GeoDataGraphPoint
{
    private IntPoint point;
    private List<GeoDataGraphPoint> connected = new List<GeoDataGraphPoint>();
    private int generation=-9999;

    public void AddConnection(GeoDataGraphPoint c)
    {
        connected.Add(c);
        c.connected.Add(this);
    }
    public GeoDataGraphPoint(IntPoint point)
    {
        this.point = point;
    }

    public List<CycleData> GetCycles(int gen)
    {
        if (generation !=  -9999)
        {
            var r = new CycleData(point);
            return new List<CycleData> { r };
        }
        generation = gen;
        List<CycleData> res = new List<CycleData>();

        foreach (GeoDataGraphPoint p in connected)
        {
            if (p.generation != gen-1)
            {
                res.AddRange(p.GetCycles(gen + 1));
            }
        }
        foreach (CycleData list in res)
        {
            if (list.ended == false)
            {
                list.Add(point);
                if (list.startPoint == this.point)
                {
                    list.ended = false;
                }
            }          
        }
        gen = -9999;
        return res;
    }
}

现在原则上这应该返回图中的每个循环(用于多边形检测)。然而,在某些情况下似乎无法返回任何内容,我怀疑存在某种内存问题,因为删除图形的某些部分有时会导致找到新的周期。

以下是输入失败的部分:

connection:(2282,3) to (2282,-192)
connection:(2282,3) to (2085,3)
connection:(2282,-192) to (2282,3)
connection:(2282,-192) to (2466,-192)
connection:(2466,-192) to (2282,-192)
connection:(2466,-192) to (2466,581)
connection:(2466,581) to (2466,-192)
connection:(2466,581) to (1494,581)
connection:(1494,581) to (2466,581)
connection:(1494,581) to (1494,397)
connection:(1494,397) to (1494,581)
connection:(1494,397) to (2282,397)
connection:(2282,397) to (1494,397)
connection:(2282,397) to (2282,187)
connection:(2282,187) to (2282,397)
connection:(2282,187) to (2085,187)
connection:(2085,187) to (2282,187)
connection:(2085,187) to (2085,3)
connection:(2085,3) to (2085,187)
connection:(2085,3) to (2282,3)
connection:(2085,3) to (2085,187)
connection:(2085,3) to (2282,3)
connection:(2085,187) to (2282,187)
connection:(2085,187) to (2085,3)
connection:(2282,187) to (2282,397)
connection:(2282,187) to (2085,187)
connection:(2282,397) to (1494,397)

上面的代码用于两个三角形排列形成一个正方形(坐标),两边相互接触,如下所示:

triangleception

我的功能如下:

    class GeoDataGraph : Dictionary<IntPoint, GeoDataGraphPoint>
    {
        public void resetGens()
        {
            foreach(var v in base.Values)
            {
                v.generation = -9999;
            }
        }
        public static Island GetHolesInIsland(Island input)
        {
            GeoDataGraph graph = new GeoDataGraph();
            for (int i = 0; i < input.area.Count-1; i = i + 2)
            {
                var p1 = new IntPoint(input.area[i].X, input.area[i].Y);
                var p2 = new IntPoint(input.area[i + 1].X, input.area[i + 1].Y);
                if (!graph.ContainsKey(p1)) graph.Add(p1, new GeoDataGraphPoint(p1));
                if (!graph.ContainsKey(p2)) graph.Add(p2, new GeoDataGraphPoint(p2));
                graph[p1].AddConnection(graph[p2]);
            }
            IntPoint min = new IntPoint(int.MaxValue, int.MaxValue);
            List<IntPoint> minCycle = null;
            List<List<IntPoint>> cycles = new List<List<IntPoint>>();
            while (graph.Count != 0)
            {
                var first = graph.First();
                var NewCycles = first.Value.GetCycles(1);
                graph.resetGens();
                if (NewCycles.Count == 0)
                {
                    graph.Remove(first.Key);
                    Console.WriteLine("point" + first.Key + "is uncycled");
                }
                cycles.AddRange(NewCycles);
                foreach (var cycle in NewCycles)
                {
                    foreach (var cycleNode in cycle)                                                                                                                
                    {
                        graph.Remove(cycleNode);
                        if (min.X > cycleNode.X || min.Y > cycleNode.Y)
                        {
                            minCycle = cycle;
                            min = cycleNode;
                        }
                    }
                }
            }

            cycles.Remove(minCycle);
            if (minCycle == null) { minCycle = new List<IntPoint>();
            foreach(IntPoint a in input.area) {
                    Console.Write(a);
                } }

            input.holes = cycles;
            input.area = minCycle;
            return input;
        }
    }
}

其中Island contains.area包含按连接对排序的点列表。

基本算法很简单:以递归方式访问节点,连接到它的每个节点,直到检测到一个循环,然后返回该节点并在出路上追加任何节点,直到再次找到循环的开始。一旦找到连接到起始节点的每个节点,就删除循环(因为我们检查了连接到循环的每个节点,我们不应该删除曾经做过的循环)并在下一个节点上重新开始,如果一个节点包含没有循环删除它。我怀疑在这一步可能会出现问题,但我不确定。 我知道我做错了什么会导致奇怪的相互依赖,导致看似无关的多边形出错?

1 个答案:

答案 0 :(得分:1)

我认为一个问题是您使用p.generation != gen-1忽略连接节点的方式。当您使用深度优先搜索时,您将标记所有节点直到最深度,并且当回溯时我认为它可能错过一些节点或探索节点两次。

作为一般建议我可以说:不要自己重新发明轮子但使用已知的算法。

首先问自己你想做什么。循环次数可以是指数级的。所以问题是你是否真的需要所有的周期。

如果答案是肯定的,并且您想在无向图中找到所有周期,则可以获得更多信息here

如果你真的不需要所有的cyles,那么你所寻找的就是strongly connected components