查找距离原点

时间:2016-06-29 23:20:57

标签: python algorithm graph-algorithm


我试图从距离原点给定距离的图表中找到所有路径组合。

事实上,魔兽世界(军团)的新扩展将把神器系统引入游戏中 它是一个可以升级的功能,每个级别为您提供1个等级,您可以为树中的每个等级花费1个点。
您可以在WowHead上找到每个工件的计算器,我将以此为例:http://legion.wowhead.com/artifact-calc/rogue/subtlety/

基本上,该计划的目标是:
"我给出一个等级,让我们说7,然后它返回给我来自图表的每个路径组合,我不得不花7点到达那里(即7个独特节点的列表)&#34 ;

当我看到计算器时,我认为它可以通过将其转换为图表来解决,所以我做了一个来帮助我完成: Graph

在图表上,我不得不对计算器进行一些调整,例如,获得3个等级的每个特征必须表示为3个相互连接的节点。 此外,对于解锁2种继续方式的特征,我不得不将它们表示为4个节点来模拟"模拟"要通过的3个节点要求。 (但我们会在那之后看到仍然是一个问题而且它并没有真正解决问题)

然后从那里我试图找到列出所有可能性的好方法,所以我通过互联网进行了大量研究,以找到解决问题的最佳方法。
我首先尝试使用广度优先搜索来解决它,但距离每个节点的原点的距离并没有多大帮助。 然后我尝试使用详尽的搜索 我目前正在使用在那里发布的代码的修改:"查找来自给定图表的所有路径"在CodeReview @ StackEchange上(不能发布2个以上的链接)

def paths(graph, v, lmax):
    """Generate the maximal cycle-free paths with a given maximum length lmax in 
    graph starting at v. Graph must be a mapping from vertices to collections of
    neighbouring vertices.

    >>> g = {1: [2, 3], 2: [3, 4], 3: [1], 4: []}
    >>> sorted(paths(g, 1, 3))
    [[1, 2, 3], [1, 2, 4], [1, 3]]
    >>> sorted(paths(g, 3, 4))
    [[3, 1, 2, 4]]

    Credit to Gareth Rees from StackExchange for the original code.
    """
    path = [v]                  # path traversed so far
    seen = {v}                  # set of vertices in path
    def search():
        dead_end = True
        if len(seen) < lmax:
            for neighbour in graph[path[-1]]:
                if neighbour not in seen:
                    dead_end = False
                    seen.add(neighbour)
                    path.append(neighbour)
                    yield from search()
                    path.pop()
                    seen.remove(neighbour)
        if dead_end:
            yield list(path)
    yield from search()

然后我创建一个函数来对结果进行排序,并只显示具有所需长度的结果。

def artifact(graph, maxrank, start):
    for i in range(1, maxrank+1):
        print("---------")
        print("Rank: " + str(i))
        print("---------")
        # Get all the Paths at "i" Rank
        RawPaths = sorted(paths(g, start, i), key=len)
        # Remove paths that doesn't satisfact our rank requirement and sort it
        ValidPaths = [sorted(j) for j in RawPaths if len(j) == i]
        # Remove duplicates
        UniquePaths = sorted([list(j) for j in set(map(tuple, ValidPaths))])
        # Display the Paths
        for j in range(len(UniquePaths)):
            PathString = "";
            for k in range(len(UniquePaths[j])):
                PathString += str(UniquePaths[j][k]) + " "
            print(PathString)
        print("")

从那里,我从图中构建了一部分节点的相邻节点(邻居)列表。我不能发布超过2个链接,但子图结束于之前链接的图形的8/7/32/31个节点。

g = {
  1: [2, 38],
  2: [1, 3],
  3: [2, 4],
  4: [3, 5],
  5: [4, 6],
  6: [5, 7, 8],
  7: [6],
  8: [6],
 31: [33],
 32: [33],
 33: [31, 32, 34],
 34: [33, 35],
 35: [34, 36],
 36: [35, 37],
 37: [36, 38],
 38: [1, 37]
}

然后我调用了我的功能:

artifact(g, 8, 1)

但是使用这个列表,我遇到了一个重大问题。实际上,算法一直持续到结束,但它没有做任何回溯以达到所需的等级(即,在经过1 - 38 - 37 - 36 - 35 - 34 - 33 - 31之后,它不会回到2 - 3 - 如果我让他们说要花10分。)
通过将38,37,...分支或38添加为邻居2,我能够为此子图解决它。

所以我的名单变成了:

g = {
  1: [2, 38],
  2: [1, 3, 38],
  3: [2, 4, 38],
  4: [3, 5, 38],
  5: [4, 6, 38],
  6: [5, 7, 8, 38],
  7: [6, 38],
  8: [6, 38],
 31: [2, 33],
 32: [2, 33],
 33: [2, 31, 32, 34],
 34: [2, 33, 35],
 35: [2, 34, 36],
 36: [2, 35, 37],
 37: [2, 36, 38],
 38: [1, 2, 37]
}

然后我能够获得我想要的图的这一部分。 现在我试图将我的推理扩展到整个图表。但由于主要的两个问题,我没有成功: - 当我向一个方向前进时,表示具有3个等级的特征的4个节点正在工作,但是如果我已经填满整个分支然后我尝试返回,则计算第4个节点。 (我仍然可以在神器功能中创建一些东西来移除第四个节点,但我认为这不是一个处理它的好方法,应该找到一个聪明的方法来处理它。
- 我用来链接前两个分支的技巧不能直接应用于整个图形。例如,按照我所做的,我会向29s邻居添加32,因为当我来自35时,32可从29访问。但是,如果我来自28并且没有添加27到路径,那么32通常不能从29开始到达。 然后我的路径变得无效。

我不确定我是否可以这样解决,希望你能帮助我。此外,我感觉我的回溯搜索方式并不完美。 我也这么想:
一旦我在分支结束时,我会回到之前的分离并从那里开始探索。但是,由于该节点已经被探索过,所以它不会走另一条路并停在那里。

最后,我可以通过任何其他方式来处理我的问题,我不想特别想用图表来解决它。
也许还有另一种方法可以有效地解决它(比如逐步构建图形并有多个等级可以花费并逐渐花费它们,而节点可能会被解锁?)。

提前感谢您的帮助,对不起我的英语错误,我是法语:)

2 个答案:

答案 0 :(得分:0)

IIUC,你有一个未加权的有向图G,你正在寻找具有以下2个属性的G的所有子图H的集合:

  1. 从给定的原点顶点到H中的每个顶点都有一条路径(这意味着H本身至少是弱连接的)
  2. 边缘的总数(或重量)恰好是给定的k。
  3. 这是一个简单的算法,你可以维护所有子图的集合,其总长度恰好为i,并且在每次迭代时将它们增长到所有子图的集合,其总长度恰好为i + 1:

    1. 从集合S中的单个子图开始,仅包含原点顶点。
    2. 对于i从0到k:
      • 不变量:S包含与原点弱连接的G的所有子图,其总长度恰好为i。
      • 设置S&#39; = {}。
      • 对于S中的每个子图H:
        • 对于V(H)中的u和V(H)外的v的每个边(u,v):
          • 形成一个子图H&#39;由H组成,加上边缘(u,v)。
          • 添加H&#39;到集S&#39;。 (如果这个图表H&#39;已经在S&#39;中,则什么都不做。这种情况经常发生。这就是为什么最好使用适合集合的数据结构 for S&#39;自动处理此问题,例如哈希表或二进制搜索树。)
      • 设置S = S&#39;。
    3. 当算法终止时,S将包含符合上述要求1和2的所有子图。在最坏的情况下,它将花费m倍于输出中不同子图的数量,其中m是图中边的数量。请注意,输出中可能存在大量子图 - 考虑n个顶点上的完整图形,并注意有多个更多可能的子图,而不是来自n的k个项的组合,并且后者已经很大了。

答案 1 :(得分:-1)

如果您打算只采用一条路径,那么您可以尝试使用Dijkstra's Algorithm来获取从初始节点到所有其他节点的距离。然后迭代它们以返回具有所需距离的那些。

如果您想尝试分支路径,这可能会变得更加困难。我的直觉是在其他节点上运行Dijkstra并使用一些动态编程,但我敢打赌,这是一种更简单的方法;我的解决计算机科学问题的能力不高。