【问题标题】:Fast Hamiltonian cycle calculation快速哈密顿循环计算
【发布时间】:2014-10-27 20:54:53
【问题描述】:

假设有一个由以下顶点组成的有向图:

"ABC", "ABD", "ACB", "ACD", "ADB", "ADC", "BAC", "BAD",
"BCA", "BCD", "BDA", "BDC", "CAB", "CAD", "CBA", "CBD",
"CDA", "CDB", "DAB", "DAC", "DBA", "DBC", "DCA", "DCB"

这些是 4 个不同字母的 3 个字母排列。 (total = 4*3*2=24) 顶点的名称也描述了它们之间的边。如果源的最后两个字符等于目标的前两个字符,则任何两个顶点相互连接,例如

ABC -> BCD

或

DCB -> CBA

该图与 De Burjin 或 Kautz 的非常相似,但又不一样。它是强连接的,我知道它有哈密顿循环。

为了解决这个问题,我不是算法专家,我只是浏览了最新的 boost 图形库并找到了枚举所有循环的 hawick_unique_circuits() 函数,这是我的示例代码:

#include <iostream>
#include <cstdint>
#include <vector>
#include <string>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/hawick_circuits.hpp>
#include "combination.hpp" // from http://howardhinnant.github.io/combinations.html

using namespace std;
using namespace boost;

typedef boost::adjacency_list<vecS, vecS, directedS, no_property, property<edge_weight_t, uint32_t> > TGraph;

TGraph m_Graph;

vector<string> m_StrVertexList;

void CreateStringVertexList(vector<string>& vl, uint32_t n, uint32_t k)
{
    vl.clear();

    if ((k > 0) && (n > k))
    {
        string code = "A";

        while (--n)
        {
            code += code.back() + 1;
        }   

        // for_each_permutation from Howard Hinnant
        // http://howardhinnant.github.io/combinations.html
        for_each_permutation(code.begin(), code.begin() + k, code.end(), 
                             [&](string::iterator first, string::iterator last)->bool{ vl.push_back(string(first, last)); return(false); });
    }
}

void AddEdgesFromStringVertex(TGraph& g, const vector<string>& vl)
{
    uint32_t connection_len = vl.begin()->size() - 1;

    g.clear();

    for (uint32_t f = 0; f < vl.size(); f++)
    for (uint32_t t = 0; t < vl.size(); t++)
    {
        if ((f != t) &&
            (vl[f].substr(1, connection_len) == vl[t].substr(0, connection_len)))
        {
            add_edge(f, t, 1, g);
        }
    }
}

class hawick_visitor
{
    public:
    void cycle(const vector<TGraph::vertex_descriptor>& circuit, const TGraph& graph) const
    {
        if (circuit.size() == m_StrVertexList.size())
        {
            for (auto ii = circuit.begin(); ii != circuit.end(); ++ii)
            {
                cout << m_StrVertexList[*ii] << " -> ";
            }

            cout << endl;
        }
    }
};

void Circuits(const TGraph& g)  
{
    hawick_unique_circuits(g, hawick_visitor());

    cout << "- end of hawick_unique_circuits() -" << endl;
}

void main(void)
{
    //CreateStringVertexList(m_StrVertexList, 10, 4);

    CreateStringVertexList(m_StrVertexList, 4, 3);
    AddEdgesFromStringVertex(m_Graph, m_StrVertexList);

    Circuits(m_Graph);
}

hawick_visitor 类只是检查找到的循环是否与 Graph 的顶点相同。如果有,这意味着我们找到了我们需要的哈密顿循环之一。

它非常适用于 24 个顶点,即从 4 个唯一字符中选择的 3 个字符,这是输出之一:

ABC -> BCA -> CAD -> ADB -> DBC -> BCD -> CDA -> DAC -> 
       ACB -> CBD -> BDC -> DCB -> CBA -> BAC -> ACD -> CDB -> 
       DBA -> BAD -> ADC -> DCA -> CAB -> ABD -> BDA -> DAB -> ABC

但是当我尝试解决从 10 个唯一字符中选择的 5040 个名为 4 个字符的顶点时,此函数永远不会返回。应该有比 hawick_unique_circuits() 更好的算法来做到这一点。因为我知道有人在不到一分钟的时间内对 10,000 个顶点进行类似的计算,但我不知道怎么做。任何想法都非常感谢。

这是我需要解决的有 5040 个顶点的图:

【问题讨论】:

    标签: graph-theory hamiltonian-cycle


    【解决方案1】:

    图表中的哈密顿循环:http://figshare.com/articles/Hamiltonian_Cycle/1228800

    如何在 C# 图中找到哈密顿循环:

    第一个文件:

    using System;
    using System.Collections.Generic;
    
    namespace Graph
    {
        partial class Program
        {
            static List<string> vertices;
            static void Main(string[] args)
            {
                List<int>[] graph = GetGraph();
                List<int> HamiltonianCycle = Algorithm(graph);
                string a = Translate(HamiltonianCycle);
                Console.Write(a);
                Console.ReadKey();
            }
            static List<int>[] GetGraph()
            {
                List<string> list = new List<string>(){"A","B","C","D","E","F","G","H","I","J"};
                vertices = new List<string>();
                for(int a=0;a<10;++a)
                    for(int b=0;b<10;++b)
                        for(int c=0;c<10;++c)
                            for(int d=0;d<10;++d)
                {
                    if(a==b || a== c || a==d || b == c || b == d|| c==d)
                        continue;
                    string vertex = list[a] + list[b] + list[c] + list[d];
                    vertices.Add(vertex);
                }
                List<int>[] graph = new List<int>[vertices.Count];
                for(int i=0;i<graph.Length;++i)
                    graph[i] = new List<int>();
                foreach(string s1 in vertices)
                    foreach(string s2 in vertices)
                        if(s1 != s2)
                            if(s1[s1.Length-3] == s2[0] && s1[s1.Length-2] == s2[1] && s1[s1.Length-1] == s2[2])
                {
                    int v1 = vertices.IndexOf(s1);
                    int v2 = vertices.IndexOf(s2);
                    graph[v1].Add(v2);
                }
                return graph;
            }
            static string Translate(List<int> HamiltonianCycle)
            {
                string a = "";
                foreach(int b in HamiltonianCycle)
                    a += vertices[b] + " -> ";
                return a;
            }
        }
    }
    

    第二个文件:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Graph
    {
        partial class Program
        {
            static List<int>[] graph, oppositeGraph;
            static List<int> HamiltonianCycle;
            static bool endOfAlgorithm;
            static int level, v1, v2;
            static List<int> Algorithm(List<int>[] graphArgument)
            {
                graph = SaveGraph(graphArgument);
                HamiltonianCycle = new List<int>();
                endOfAlgorithm = false;
                level = 0;
                RemoveMultipleEdgesAndLoops(graph); //3.1
                CreateOppositeGraph(graph);
                bool HamiltonianCycleCantExist = AnalyzeGraph(new List<Edge>()); //6.1.a
                ReverseGraph();
                if (!HamiltonianCycleCantExist)
                    FindHamiltonianCycle(GetNextVertex()); //5.3
                HamiltonianCycle.Reverse();
                return HamiltonianCycle;
            }
            static void ReverseGraph()
            {
                graph = SaveGraph(oppositeGraph);
                CreateOppositeGraph(graph);
            }
            static void FindHamiltonianCycle(int a)
            {
                if (!endOfAlgorithm)
                {
                    ++level;
                    if (HamiltonianCycleFound())
                        endOfAlgorithm = true;
                    SortList(a); //5.4
                    while (graph[a].Count > 0 && !endOfAlgorithm)
                    {
                        List<Edge> removedEdges = new List<Edge>();
                        int chosenVertex = graph[a][0];
                        graph[a].Remove(chosenVertex);
                        List<int>[] currentGraph = SaveGraph(graph);
                        #region 6.2
                        foreach (int b in graph[a])
                        {
                            removedEdges.Add(new Edge(a, b));
                            oppositeGraph[b].Remove(a);
                        }
                        graph[a].Clear();
                        #endregion
                        graph[a].Add(chosenVertex);
                        v1 = a;
                        v2 = chosenVertex;
                        bool HamiltonianCycleCantExist = AnalyzeGraph(removedEdges); //6.1.b
                        if (!HamiltonianCycleCantExist)
                        {
                            FindHamiltonianCycle(GetNextVertex()); //5.5
                            RestoreGraphs(currentGraph); //6.4
                        }
                        else
                        {
                            foreach (Edge e in removedEdges) //6.3
                            {
                                graph[e.from].Add(e.to);
                                oppositeGraph[e.to].Add(e.from);
                            }
                            RemoveEdge(new Edge(a, chosenVertex), graph, oppositeGraph);
                        }
                    }
                    if (!endOfAlgorithm)
                    {
                        --level;
                        if (level == 0)
                            endOfAlgorithm = true;
                    }
                }
            }
            static bool HamiltonianCycleFound()
            {
                foreach (List<int> list in graph)
                    if (list.Count != 1)
                        return false;
                HamiltonianCycle = GetHamiltonianCycle(graph);
                return true;
            }
            static List<int> GetHamiltonianCycle(List<int>[] graphArgument)
            {
                List<int> cycle = new List<int>() { 0 };
                while (true)
                {
                    if (cycle.Count == graphArgument.Length && graphArgument[cycle.Last()].Contains(cycle[0]))
                        return cycle;
                    if (cycle.Contains(graphArgument[cycle.Last()][0]))
                        return new List<int>();
                    else
                        cycle.Add(graphArgument[cycle.Last()][0]);
                }
            }
            static int GetNextVertex()
            {
                List<int> correctOrder = GetCorrectOrder(graph);
                foreach (int a in correctOrder)
                    if (graph[a].Count != 1)
                        return a;
                return 0;
            }
            static bool AnalyzeGraph(List<Edge> removedEdges)
            {
                bool HamiltonianCycleCantExist = false;
                int a;
                do
                {
                    a = removedEdges.Count;
                    HamiltonianCycleCantExist = RemoveUnnecessaryEdges(graph, oppositeGraph, removedEdges, false);
                    if (!HamiltonianCycleCantExist)
                        HamiltonianCycleCantExist = RemoveUnnecessaryEdges(oppositeGraph, graph, removedEdges, true);
                }
                while (a != removedEdges.Count && !HamiltonianCycleCantExist);
                if (!HamiltonianCycleCantExist)
                    HamiltonianCycleCantExist = GraphIsDisconnected(graph);
                return HamiltonianCycleCantExist;
            }
            static bool RemoveUnnecessaryEdges(List<int>[] graphArgument, List<int>[] oppositeGraphArgument, List<Edge> removedEdges, bool oppositeGraph)
            {
                bool HamiltonianCycleCantExist = false;
                for (int a = 0; a < graphArgument.Length; ++a)
                {
                    if (graphArgument[a].Count == 0 //4.1
                        || (graphArgument[a].Count == 1 && SearchForCycleAmongVerticesOfDegreeEqual1(graphArgument, a)) //4.2.1
                        || (graphArgument[a].Count > 1 && SearchForCycleAmongVerticesOfDegreeGreaterThan1(a, graphArgument, oppositeGraphArgument))) //4.2.2
                        return true;
                    List<Edge> edges = new List<Edge>();
                    #region 3.2
                    if (graphArgument[a].Count == 1 && oppositeGraphArgument[graphArgument[a][0]].Count != 1)
                    {
                        foreach (int c in oppositeGraphArgument[graphArgument[a][0]])
                            if (c != a)
                                if (!oppositeGraph)
                                    edges.Add(new Edge(c, graphArgument[a][0]));
                                else
                                    edges.Add(new Edge(graphArgument[a][0], c));
                    }
                    #endregion
                    #region 3.4
                    if (graphArgument[a].Count == 1 && graphArgument[graphArgument[a][0]].Contains(a))
                    {
                        if (!oppositeGraph)
                            edges.Add(new Edge(graphArgument[a][0], a));
                        else
                            edges.Add(new Edge(a, graphArgument[a][0]));
                    }
                    #endregion
                    foreach (Edge edge in edges)
                    {
                        removedEdges.Add(edge);
                        if (!oppositeGraph)
                            RemoveEdge(edge, graphArgument, oppositeGraphArgument);
                        else
                            RemoveEdge(edge, oppositeGraphArgument, graphArgument);
                    }
                }
                return HamiltonianCycleCantExist;
            }
            static bool SearchForCycleAmongVerticesOfDegreeEqual1(List<int>[] graphArgument, int a)
            {
                if(!(a==v1 || a == v2))
                    return false;
                List<int> cycle = new List<int>() { a };
                while (true)
                    if (graphArgument[cycle.Last()].Count == 1 && cycle.Count < graphArgument.Length)
                        if (cycle.Contains(graphArgument[cycle.Last()][0]))
                            return true;
                        else
                            cycle.Add(graphArgument[cycle.Last()][0]);
                        else
                            return false;
            }
            static bool SearchForCycleAmongVerticesOfDegreeGreaterThan1(int a, List<int>[] graphArgument, List<int>[] oppossiteGraphArgument)
            {
                if (!ListsAreEqual(graphArgument[a], oppossiteGraphArgument[a], true))
                    return false;
                int b = 1;
                for (int c = 0; c < graphArgument.Length && graphArgument.Length - c > graphArgument[a].Count - b; ++c)
                {
                    if (c == a)
                        continue;
                    if (ListsAreEqual(graphArgument[c], graphArgument[a], false) && ListsAreEqual(graphArgument[c], oppossiteGraphArgument[c], true))
                        ++b;
                    if (b == graphArgument[a].Count)
                        return true;
                }
                return false;
            }
            static bool ListsAreEqual(List<int> firstList, List<int> secondList, bool EqualCount)
            {
                if (EqualCount && firstList.Count != secondList.Count)
                    return false;
                foreach (int a in firstList)
                    if (!secondList.Contains(a))
                        return false;
                return true;
            }
            static void SortList(int a)
            {
                List<int> correctOrder = GetCorrectOrder(oppositeGraph);
                for (int b = 1; b < graph[a].Count; ++b)
                    for (int c = 0; c < graph[a].Count - 1; ++c)
                        if (correctOrder.IndexOf(graph[a][c]) > correctOrder.IndexOf(graph[a][c + 1]))
                {
                    int n = graph[a][c];
                    graph[a][c] = graph[a][c + 1];
                    graph[a][c + 1] = n;
                }
            }
            static List<int> GetCorrectOrder(List<int>[] graphArgument) //5.1
            {
                Dictionary<int, int> vertices = new Dictionary<int, int>();
                List<int> order = new List<int>();
                for (int a = 0; a < graphArgument.Length; ++a)
                    vertices.Add(a, graphArgument[a].Count);
                IEnumerable<int> v = from pair in vertices orderby pair.Value ascending select pair.Key;
                foreach (int a in v)
                    order.Add(a);
                return order;
            }
            static void RemoveEdge(Edge e, List<int>[] graphArgument, List<int>[] oppositeGraphArgument)
            {
                graphArgument[e.from].Remove(e.to);
                oppositeGraphArgument[e.to].Remove(e.from);
            }
            static void RemoveMultipleEdgesAndLoops(List<int>[] graphArgument)
            {
                for (int a = 0; a < graphArgument.Length; ++a)
                {
                    graphArgument[a] = graphArgument[a].Distinct().ToList();
                    graphArgument[a].Remove(a);
                }
            }
            static void CreateOppositeGraph(List<int>[] graphArgument)
            {
                oppositeGraph = new List<int>[graphArgument.Length];
                for (int a = 0; a < graphArgument.Length; ++a)
                    oppositeGraph[a] = new List<int>();
                for (int a = 0; a < graphArgument.Length; ++a)
                    foreach (int b in graphArgument[a])
                        oppositeGraph[b].Add(a);
            }
            static void RestoreGraphs(List<int>[] graphArgument)
            {
                graph = new List<int>[graphArgument.Length];
                for (int a = 0; a < graphArgument.Length; ++a)
                {
                    graph[a] = new List<int>();
                    graph[a].AddRange(graphArgument[a]);
                }
                CreateOppositeGraph(graph);
            }
            static List<int>[] SaveGraph(List<int>[] graphArgument)
            {
                List<int>[] savedGraph = new List<int>[graphArgument.Length];
                for (int a = 0; a < graphArgument.Length; ++a)
                {
                    savedGraph[a] = new List<int>();
                    savedGraph[a].AddRange(graphArgument[a]);
                }
                return savedGraph;
            }
            static bool GraphIsDisconnected(List<int>[] graphArgument)
            {
                Stack<int> stack = new Stack<int>();
                Color[] colors = new Color[graphArgument.Length];
                colors[0] = Color.Gray;
                stack.Push(0);
                while (stack.Count > 0)
                {
                    int a = stack.Pop();
                    foreach (int b in graphArgument[a])
                    {
                        if (colors[b] == Color.White)
                        {
                            colors[b] = Color.Gray;
                            stack.Push(b);
                        }
                    }
                    colors[a] = Color.Black;
                }
                foreach (Color c in colors)
                    if (c != Color.Black)
                        return true;
                return false;
            }
        }
        class Edge
        {
            public int from, to;
            public Edge(int f, int t)
            {
                from = f;
                to = t;
            }
        }
        enum Color { White, Gray, Black };
    }
    

    我发现哈密伦循环与我的算法的修改版本:http://arxiv.org/abs/1405.6347 所做的修改是:

    • 在对面图中搜索算法
    • 如果图断开连接,则测试算法
    • 算法未测试“唯一邻居”规则
    • 算法搜索非哈密顿循环,仅从创建当前访问边的顶点开始 - 仅在函数 SearchForCycleAmongVerticesOfDegreeEqual1 中

    【讨论】:

    • 这太棒了!我确认了输出。我要研究你的算法。
    【解决方案2】:

    嗯,计算哈密顿循环实际上是 NP 完全问题。所以没有快速(即多项式时间)算法。

    您可以在这里找到更多信息:http://mathworld.wolfram.com/HamiltonianCycle.html

    一些蒙特卡洛算法可能会在这里工作(并且可能不会给你总是正确的答案) - 所以我会在那里搜索,但不要指望奇迹。 仍然是 NP-完全问题。

    【讨论】:

    • 我相信这取决于图形类型。例如 De Bruijn 图,解决方案是确定性的并且非常快,请参见此处:stackoverflow.com/questions/4008603/…
    • 不,您混淆了两种类型的路径:欧拉路径和哈密顿路径。在链接的帖子中,提到了欧拉路径,它是 P. Hamiltonian,但是不容易计算。
    • 不,它只是访问每个顶点一次看到en.wikipedia.org/wiki/De_Bruijn_sequence
    • "De Bruijn 序列可以通过在 k 个符号上采用 n 维 De Bruijn 图的哈密顿路径来构造(或等效地,一个 (n − 1) 维 De Bruijn 的欧拉循环)图)"
    • 好吧,我不确定(我对 De Bruijn 序列的了解几乎为零),但我认为对你来说最好的方法是:尽量避免哈密顿路径并找到等效的欧拉路径。跨度>
    猜你喜欢
    • 1970-01-01
    • 2012-07-01
    • 2021-06-24
    • 2013-04-13
    • 2021-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多