public bool IsPlanar(Graph graphArgument) { var enableCudafy = Settings.EnableCudafy; Settings.EnableCudafy = true; if (WorkerLog != null) { WorkerLog("Начало гамма-алгоритма"); } var graph = new Graph(graphArgument); Debug.Assert( graph.Children.All(pair => pair.Value .All(value => graph.Children.ContainsKey(value) && graph.Children[value].Contains(pair.Key))) ); if (WorkerBegin != null) { WorkerBegin(); } // Шаг первый - удаляем все листья и узлы степени 2 if (WorkerLog != null) { WorkerLog("Удаляем все листья и узлы степени 2"); } // листья представляют собой дерево и нарисовать его плоскую укладку тривиально. graph.RemoveAllTrees(); // Замечание. Если на ребра планарного графа нанести произвольное число вершин степени 2, // то он останется планарным; равным образом, если на ребра непланарного графа // нанести вершины степени 2, то он планарным не станет. graph.RemoveIntermedians(); // Шаг второй - граф нужно укладывать отдельно по компонентам связности. if (WorkerLog != null) { WorkerLog("Находим ВСЕ пути в графе длины не более размера графа + 1"); } Dictionary <int, PathDictionary> cachedAllGraphPaths = graph.GetAllGraphPaths(); var queue = new StackListQueue <Context> { graph.GetAllSubGraphs().Select(subgraph => new Context { SubGraphQueue = new StackListQueue <Graph> { subgraph }, CachedSubGraphPathsQueue = new StackListQueue <Dictionary <int, PathDictionary> > { Graph.GetSubgraphPaths(subgraph.Vertices, cachedAllGraphPaths) }, }) }; Debug.WriteLine("start "); foreach (Context context in queue) { while (context.SubGraphQueue.Any()) { Graph subGraph = context.SubGraphQueue.Dequeue(); if (WorkerLog != null) { WorkerLog("Проверка связанной компоненты " + subGraph); } Dictionary <int, PathDictionary> cachedSubGraphPaths = context.CachedSubGraphPathsQueue.Dequeue(); // На вход подаются графы, обладающие следующими свойствами: // граф связный; // граф имеет хотя бы один цикл; // граф не имеет мостиков, т. е. ребер, после удаления которых // граф распадается на две компонеты связности. if (WorkerLog != null) { WorkerLog( "Находим мосты после удаления которых граф распадается на несколько компонет связности"); } var vertices = new StackListQueue <Vertex>(subGraph.Vertices); var bridges = new StackListQueue <Vertex>(); for (int i = 0; i < vertices.Count; i++) { Vertex dequeue = vertices.Dequeue(); IEnumerable <Graph> subsubgraphs = subGraph.GetSubgraph(vertices).GetAllSubGraphs(); if (subsubgraphs.Count() > 1) { bridges.Add(dequeue); } vertices.Enqueue(dequeue); } Debug.Assert(bridges.Count != vertices.Count); if (bridges.Any()) { // Если в графе есть мосты, то их нужно разрезать, провести отдельно плоскую укладку // каждой компоненты связности, а затем соединить их мостами. // Здесь может возникнуть трудность: в процессе укладки концевые вершины моста могут // оказаться внутри плоского графа. Нарисуем одну компоненту связности, // и будем присоединять к ней другие последовательно. // Каждую новую компоненту связности будем рисовать в той грани, в которой лежит // концевая вершина соответствующего моста. Так как граф связности мостами компонент // связности является деревом, мы сумеем получить плоскую укладку. if (WorkerLog != null) { WorkerLog( "В графе есть мосты, их нужно разрезать, провести отдельно плоскую укладку, а затем соединить их мостами."); } if (WorkerLog != null) { WorkerLog("Мосты: " + string.Join(",", bridges)); } IEnumerable <Vertex> exceptBridges = vertices.Except(bridges); IEnumerable <Graph> subsubgraphs = subGraph.GetSubgraph(exceptBridges).GetAllSubGraphs(); Debug.WriteLine("subsubgraphs = " + subsubgraphs.Count()); context.SubGraphQueue.Enqueue( subsubgraphs.Select(subgraph => subGraph.GetSubgraph(subgraph.Vertices.Union(bridges)))); context.CachedSubGraphPathsQueue.Enqueue( subsubgraphs.Select( subgraph => Graph.GetSubgraphPaths(subgraph.Vertices.Union(bridges), cachedSubGraphPaths))); continue; } if (WorkerLog != null) { WorkerLog("Находим ЛЮБОЙ МАКСИМАЛЬНОЙ ДЛИНЫ простой цикл в графе"); } Circle circle = null; for (int i = cachedSubGraphPaths.Keys.Max(); i > 3; i--) { foreach (var pair in cachedSubGraphPaths.Where(pair => pair.Key == i)) { foreach ( var key in subGraph.Vertices.Select(vertex => new KeyValuePair <Vertex, Vertex>(vertex, vertex)) ) { if (pair.Value.ContainsKey(key) && pair.Value[key].Any()) { foreach (Path path in pair.Value[key]) { circle = new Circle(path.GetRange(0, path.Count - 1)); if (Circle.IsSimple(circle)) { break; } circle = null; } if (circle != null) { break; } } if (circle != null) { break; } } if (circle != null) { break; } } if (circle != null) { break; } } if (circle == null && !context.Edges.Any()) { // граф — дерево и нарисовать его плоскую укладку тривиально. // Поскольку мы ещё не начинали рисовать, то значит всё проверено continue; } // Инициализация алгоритма производится так: выбираем любой простой цикл; // и получаем две грани: Γ1 — внешнюю и Γ2 — внутреннюю if (circle != null && !context.Edges.Any()) { context.Edges.Add(new Edge(circle)); } if (circle != null) { context.Edges.Add(new Edge(circle)); context.Builded.Add(context.Edges.Last()); } // Если циклов нет, то надо проверить, что данное дерево // можно вписать в уже построенный граф Debug.WriteLine("SubGraph " + subGraph); Debug.WriteLine("builded " + context.Builded); Debug.WriteLine("edges:" + string.Join(Environment.NewLine, context.Edges.Select(e => e.ToString()))); //// Каждый сегмент S относительно уже построенного графа G′ представляет собой одно из двух: //// ребро, оба конца которого принадлежат G′, но само оно не принадлежит G′; //// связную компоненту графа G – G′, дополненную всеми ребрами графа G, //// один из концов которых принадлежит связной компоненте, //// а второй из графа G′. VertexSortedCollection buildedVertices = context.Builded.Vertices; Dictionary <int, PathDictionary> fromTo = Graph.GetFromToPaths(buildedVertices, buildedVertices, cachedSubGraphPaths); var paths = new PathCollection(fromTo .SelectMany(pair => pair.Value) .SelectMany(pair => pair.Value) .Where(Path.IsNoVertix) .Where(Path.IsNoCircle) ); Debug.WriteLine("paths " + paths); //var secondGraph = new Graph(subGraph.Except(context.Builded)); //if (secondGraph.Any()) //{ // IEnumerable<Graph> collection = secondGraph.GetAllSubGraphs(); // context.SubGraphQueue.Enqueue(collection); // context.CachedSubGraphPathsQueue.Enqueue( // collection.Select(subgraph => Graph.GetSubgraphPaths(subgraph.Vertices, cachedSubGraphPaths))); //} paths.ReplaceAll(paths.Distinct()); Debug.WriteLine("paths " + paths); paths.RemoveAll(context.Builded.Contains); Debug.WriteLine("paths " + paths); Debug.WriteLine("builded " + context.Builded); Debug.WriteLine("edges:" + string.Join(Environment.NewLine, context.Edges.Select(e => e.ToString()))); while (paths.Any()) { paths.RemoveAll(context.Builded.Contains); Debug.WriteLine("paths " + paths); if (!paths.Any()) { continue; } if (Settings.EnableCudafy) { try { while (paths.Any(Path.IsLong)) { // Находим для всех путей их перечечения с уже построенным графом // Разбиваем пути в найденных точках пересечения с уже построенным графом // Если точек пересечения не найдено, то выходим из цикла int[,] matrix; int[] indexes; lock (CudafySequencies.Semaphore) { CudafySequencies.SetSequencies( paths.Select( path => path.GetRange(1, path.Count - 2) .Select(vertex => vertex.Id) .ToArray()) .ToArray(), context.Builded.Vertices.Select( vertex => new StackListQueue <int>(vertex.Id).ToArray()) .ToArray() ); CudafySequencies.Execute("CountIntersections"); // подсчитываем число пересечений matrix = CudafySequencies.GetMatrix(); } lock (CudafyMatrix.Semaphore) { CudafyMatrix.SetMatrix(matrix); CudafyMatrix.ExecuteRepeatZeroIndexOfNonZero(); // находим индексы ненулевых элементов в строках indexes = CudafyMatrix.GetIndexes(); } Dictionary <int, int> dictionary = indexes.Select( (value, index) => new KeyValuePair <int, int>(index, value)) .Where(pair => pair.Value >= 0) .ToDictionary(pair => pair.Key, pair => pair.Value); if (!dictionary.Any()) { break; } Debug.Assert(dictionary.All(pair => pair.Key >= 0)); Debug.Assert(dictionary.All(pair => pair.Value >= 0)); Debug.Assert(dictionary.All(pair => pair.Key < paths.Count)); Debug.Assert(dictionary.All(pair => pair.Value < context.Builded.Vertices.Count)); var dictionary2 = new StackListQueue <KeyValuePair <Path, Vertex> >( dictionary.Select( pair => new KeyValuePair <Path, Vertex>(new Path(paths[pair.Key]), new Vertex(context.Builded.Vertices[pair.Value]))) ); var list = new StackListQueue <int>(dictionary.Select(pair => pair.Key).Distinct()); list.Sort(); Debug.Assert(dictionary2.All(pair => pair.Key.Count > 1)); for (int i = list.Count; i-- > 0;) { paths.RemoveAt(list[i]); } paths.AddRangeExcept( new PathCollection( dictionary2.SelectMany(pair => pair.Key.SplitBy(pair.Value) .Where(Path.IsNoVertix) .Where(Path.IsNoCircle)) .Distinct())); paths.ReplaceAll(paths.Distinct()); paths.RemoveAll(context.Builded.Contains); } } catch (Exception ex) { if (WorkerLog != null) { WorkerLog(ex.ToString()); } paths.ReplaceAll( paths.SelectMany(context.Builded.Split) .Where(Path.IsNoVertix) .Where(Path.IsNoCircle) ); paths.ReplaceAll(paths.Distinct()); paths.RemoveAll(context.Builded.Contains); } }