我有一个代码来生成图形中的循环,但是当点数更多时它会变慢

时间:2013-06-12 13:38:51

标签: c# optimization graph-theory

我的代码如下,但提供输出需要3分钟才有更快的技术来查找无向图中的周期:

当输入少于以下时


            {1, 2}, {1, 3}, {1, 4}, {2, 3},
            {3, 4}, {2, 6}, {4, 6}, {7, 8},
            {8, 9}, {9, 7}

它运行速度很快,否则当它输入太多时它会太慢

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CycleDetection
{
    class Program
    {
        //  Graph modelled as list of edges
        static int[,] graph =
            {
                {1, 2}, {1, 3}, {1, 4}, {2, 3},
                {3, 4}, {2, 6}, {4, 6}, {7, 8},
                {8, 9}, {9, 7}, {2, 3}, {1, 5}, {2, 4}, {4, 6},
                {7, 4}, {3, 5}, {4, 5}, {2, 8},
                {8, 1}, {9, 1}, {14,1}, {2,14}, {5, 1}, {7, 3}, {6, 2}, {10,1},
                {10, 4}, {10, 6}, {10, 3}, {10,2},
                {8, 3}, {1, 7}
            };

        static List<int[]> cycles = new List<int[]>();

        static void Main(string[] args)
        {
            for (int i = 0; i < graph.GetLength(0); i++)
                for (int j = 0; j < graph.GetLength(1); j++)
                {
                    findNewCycles(new int[] { graph[i, j] });
                }

            foreach (int[] cy in cycles)
            {
                string s = "" + cy[0];

                for (int i = 1; i < cy.Length; i++)
                    s += "," + cy[i];

                Console.WriteLine(s);
            }
            Console.ReadLine();
        }

        static void findNewCycles(int[] path)
        {
            int n = path[0];
            int x;
            int[] sub = new int[path.Length + 1];

            for (int i = 0; i < graph.GetLength(0); i++)
                for (int y = 0; y <= 1; y++)
                    if (graph[i, y] == n)
                    //  edge referes to our current node
                    {
                        x = graph[i, (y + 1) % 2];
                        if (!visited(x, path))
                        //  neighbor node not on path yet
                        {
                            sub[0] = x;
                            Array.Copy(path, 0, sub, 1, path.Length);
                            //  explore extended path
                            findNewCycles(sub);
                        }
                        else if ((path.Length > 2) && (x == path[path.Length - 1]))
                        //  cycle found
                        {
                            int[] p = normalize(path);
                            int[] inv = invert(p);
                            if (isNew(p) && isNew(inv))
                                cycles.Add(p);
                        }
                    }
        }

        static bool equals(int[] a, int[] b)
        {
            bool ret = (a[0] == b[0]) && (a.Length == b.Length);

            for (int i = 1; ret && (i < a.Length); i++)
                if (a[i] != b[i])
                {
                    ret = false;
                }

            return ret;
        }

        static int[] invert(int[] path)
        {
            int[] p = new int[path.Length];

            for (int i = 0; i < path.Length; i++)
                p[i] = path[path.Length - 1 - i];

            return normalize(p);
        }

        //  rotate cycle path such that it begins with the smallest node
        static int[] normalize(int[] path)
        {
            int[] p = new int[path.Length];
            int x = smallest(path);
            int n;

            Array.Copy(path, 0, p, 0, path.Length);

            while (p[0] != x)
            {
                n = p[0];
                Array.Copy(p, 1, p, 0, p.Length - 1);
                p[p.Length - 1] = n;
            }

            return p;
        }

        static bool isNew(int[] path)
        {
            bool ret = true;

            foreach (int[] p in cycles)
                if (equals(p, path))
                {
                    ret = false;
                    break;
                }

            return ret;
        }

        static int smallest(int[] path)
        {
            int min = path[0];

            foreach (int p in path)
                if (p < min)
                    min = p;

            return min;
        }

        static bool visited(int n, int[] path)
        {
            bool ret = false;

            foreach (int p in path)
                if (p == n)
                {
                    ret = true;
                    break;
                }

            return ret;
        }
    }
}

输出:-list of cycles

我使用visual studio 2010来运行

1 个答案:

答案 0 :(得分:3)

这样的事情怎么样?

int[,] graph =
            {
                {1, 2}, {1, 3}, {1, 4}, {2, 3},
                {3, 4}, {2, 6}, {4, 6}, {7, 8},
                {8, 9}, {9, 7}, {2, 3}, {1, 5}, {2, 4}, {4, 6},
                {7, 4}, {3, 5}, {4, 5}, {2, 8},
                {8, 1}, {9, 1}, {14,1}, {2,14}, {5, 1}, {7, 3}, {6, 2}, {10,1},
                {10, 4}, {10, 6}, {10, 3}, {10,2},
                {8, 3}, {1, 7}
            };
var g = graph.Cast<int>().ToArray();
var edges = Enumerable.Range(0, g.Length / 2) 
      .Select(i => Tuple.Create(g[i * 2], g[i * 2 + 1]));
var edgeLookup = edges.ToLookup(x => x.Item1);
var stack = new Stack<Tuple<int, int>>(edgeLookup.First());
var visited = new HashSet<Tuple<int, int>>();

while(stack.Count > 0)
{
    var current = stack.Pop();
    if(!visited.Add(current)) throw new Exception("cycle!!!");
    var newNodes = edgeLookup[current.Item2];
    foreach(var newNode in newNodes)
    {
        stack.Push(newNode);
    }
}