如何从Python中的加权图计算路径的长度?

时间:2017-12-28 10:24:59

标签: python graph

我正在做这个项目,我遇到了找出哪条路径长度最短的问题。设置是超市。你从入口处开始,然后走出现金牌。点A,B,C,D是超市中各个部分中的点,如烘烤等。在输入端'经过'你可以给出你想要传递的分数。

#e = entrance
#k = cash desk
graph1 = {'e':{'a':1, 'b':5, 'c':6, 'd':6},
    'a':{'e':2, 'b':4, 'c':5, 'd':5, 'k':7},
    'b':{'e':5, 'a':5, 'c':3, 'd':5, 'k':6},
    'c':{'e':6, 'a':5, 'b':3, 'd':4, 'k':5},
    'd':{'e':6, 'a':3, 'b':5, 'c':4, 'k':2},
    'k':{'a':7, 'b':6, 'c':5, 'd':2}
}

start = input('start')
end = input('end')
required = input('via').split(',')

def find_all_paths(graph, start, end, path=[]):
  path = path + [start]
  if start == end:
      return [path]
  if not start in graph:
      return []
  paths = []
  for node in graph[start]:
      if node not in path:
          newpaths = find_all_paths(graph, node, end, path)
          for newpath in newpaths:
              paths.append(newpath)
  return paths

def exists_in_graph(graph, nodes):
  for node in nodes:
    if not node in graph:
      return False
  return True

allPaths = find_all_paths(graph1, start, end)
allPathsTroughNodes = list(filter(lambda x: exists_in_graph(x, required), 
allPaths))
print(allPathsTroughNodes)

输出:

start e
end k
via a,c,d
[['e', 'a', 'b', 'c', 'd', 'k'], ['e', 'a', 'b', 'd', 'c', 'k'], ['e', 'a', 
'c', 'b', 'd', 'k'], ['e', 'a', 'c', 'd', 'b', 'k'], ['e', 'a', 'c', 'd', 
'k'], ['e', 'a', 'd', 'b', 'c', 'k'], ['e', 'a', 'd', 'c', 'b', 'k'], ['e', 
'a', 'd', 'c', 'k'], ['e', 'b', 'a', 'c', 'd', 'k'], ['e', 'b', 'a', 'd', 
'c', 'k'], ['e', 'b', 'c', 'a', 'd', 'k'], ['e', 'b', 'c', 'd', 'a', 'k'], 
['e', 'b', 'd', 'a', 'c', 'k'], ['e', 'b', 'd', 'c', 'a', 'k'], ['e', 'c', 
'a', 'b', 'd', 'k'], ['e', 'c', 'a', 'd', 'b', 'k'], ['e', 'c', 'a', 'd', 
'k'], ['e', 'c', 'b', 'a', 'd', 'k'], ['e', 'c', 'b', 'd', 'a', 'k'], ['e', 
'c', 'd', 'a', 'b', 'k'], ['e', 'c', 'd', 'a', 'k'], ['e', 'c', 'd', 'b', 
'a', 'k'], ['e', 'd', 'a', 'b', 'c', 'k'], ['e', 'd', 'a', 'c', 'b', 'k'], 
['e', 'd', 'a', 'c', 'k'], ['e', 'd', 'b', 'a', 'c', 'k'], ['e', 'd', 'b', 
'c', 'a', 'k'], ['e', 'd', 'c', 'a', 'b', 'k'], ['e', 'd', 'c', 'a', 'k'], 
['e', 'd', 'c', 'b', 'a', 'k']]

但是我不知道如何计算每个找到的路径的长度以及如何从中找到最短的路径。

1 个答案:

答案 0 :(得分:1)

您需要在构建路径时累积路径长度。可以修改现有代码来做到这一点,但它会变得混乱。更简洁的方法是将您的函数转换为生成器,以便在找到它们时生成路径,而不是将它们存储在路径列表中。

我们可以将生成器输出传递给sorted函数,以获取按其长度排序的路径列表。

import sys

graph1 = {
    'e': {'a': 1, 'b': 5, 'c': 6, 'd': 6},
    'a': {'e': 2, 'b': 4, 'c': 5, 'd': 5, 'k': 7},
    'b': {'e': 5, 'a': 5, 'c': 3, 'd': 5, 'k': 6},
    'c': {'e': 6, 'a': 5, 'b': 3, 'd': 4, 'k': 5},
    'd': {'e': 6, 'a': 3, 'b': 5, 'c': 4, 'k': 2},
    'k': {'a': 7, 'b': 6, 'c': 5, 'd': 2}
}

#start = input('start ')
#end = input('end ')
#required = input('via ').split(',')
#if required == ['']:
    #required = []

# Hard-code some input data to make it easier to test the code
start, end = 'e', 'k'
required = []

def find_all_paths(graph, start, end, path=None, pathlen=0):
    if path is None:
        path = []
    path = path + [start]
    if start == end:
        yield pathlen, path
    if not start in graph:
        yield [], 0
        return

    for node, val in graph[start].items():
        if node not in path:
            yield from find_all_paths(graph, node, end, path, pathlen + val)

def exists_in_graph(graph, nodes):
    for node in nodes:
        if not node in graph:
            return False
    return True

if not exists_in_graph(graph1, [start, end] + required):
    print('Bad data!')
    sys.exit()

all_paths = sorted(find_all_paths(graph1, start, end))
for pathlen, path in all_paths:
    if exists_in_graph(path, required):
        print(path, pathlen)

<强>输出

['e', 'a', 'd', 'k'] 8
['e', 'a', 'k'] 8
['e', 'd', 'k'] 8
['e', 'a', 'b', 'k'] 11
['e', 'a', 'c', 'k'] 11
['e', 'b', 'k'] 11
['e', 'c', 'k'] 11
['e', 'a', 'b', 'd', 'k'] 12
['e', 'a', 'c', 'd', 'k'] 12
['e', 'b', 'd', 'k'] 12
['e', 'c', 'd', 'k'] 12
['e', 'a', 'b', 'c', 'k'] 13
['e', 'b', 'c', 'k'] 13
['e', 'a', 'b', 'c', 'd', 'k'] 14
['e', 'b', 'c', 'd', 'k'] 14
['e', 'a', 'c', 'b', 'k'] 15
['e', 'a', 'd', 'c', 'k'] 15
['e', 'c', 'b', 'k'] 15
['e', 'd', 'c', 'k'] 15
['e', 'a', 'c', 'b', 'd', 'k'] 16
['e', 'c', 'b', 'd', 'k'] 16
['e', 'd', 'a', 'k'] 16
['e', 'a', 'd', 'b', 'k'] 17
['e', 'b', 'a', 'd', 'k'] 17
['e', 'b', 'a', 'k'] 17
['e', 'd', 'b', 'k'] 17
['e', 'c', 'a', 'd', 'k'] 18
['e', 'c', 'a', 'k'] 18
['e', 'a', 'b', 'd', 'c', 'k'] 19
['e', 'a', 'd', 'b', 'c', 'k'] 19
['e', 'a', 'd', 'c', 'b', 'k'] 19
['e', 'b', 'd', 'c', 'k'] 19
['e', 'd', 'a', 'b', 'k'] 19
['e', 'd', 'a', 'c', 'k'] 19
['e', 'd', 'b', 'c', 'k'] 19
['e', 'd', 'c', 'b', 'k'] 19
['e', 'b', 'a', 'c', 'k'] 20
['e', 'b', 'c', 'a', 'd', 'k'] 20
['e', 'b', 'c', 'a', 'k'] 20
['e', 'b', 'd', 'a', 'k'] 20
['e', 'c', 'd', 'a', 'k'] 20
['e', 'a', 'c', 'd', 'b', 'k'] 21
['e', 'b', 'a', 'c', 'd', 'k'] 21
['e', 'c', 'a', 'b', 'k'] 21
['e', 'c', 'b', 'a', 'd', 'k'] 21
['e', 'c', 'b', 'a', 'k'] 21
['e', 'c', 'd', 'b', 'k'] 21
['e', 'd', 'a', 'b', 'c', 'k'] 21
['e', 'b', 'c', 'd', 'a', 'k'] 22
['e', 'c', 'a', 'b', 'd', 'k'] 22
['e', 'd', 'c', 'a', 'k'] 22
['e', 'b', 'd', 'a', 'c', 'k'] 23
['e', 'c', 'd', 'a', 'b', 'k'] 23
['e', 'd', 'a', 'c', 'b', 'k'] 23
['e', 'd', 'b', 'a', 'k'] 23
['e', 'b', 'a', 'd', 'c', 'k'] 24
['e', 'c', 'b', 'd', 'a', 'k'] 24
['e', 'd', 'c', 'a', 'b', 'k'] 25
['e', 'd', 'c', 'b', 'a', 'k'] 25
['e', 'b', 'd', 'c', 'a', 'k'] 26
['e', 'd', 'b', 'a', 'c', 'k'] 26
['e', 'd', 'b', 'c', 'a', 'k'] 26
['e', 'c', 'a', 'd', 'b', 'k'] 27
['e', 'c', 'd', 'b', 'a', 'k'] 27

调用find_all_paths生成器的另一种方法是

valid_paths = sorted((pathlen, path)
    for pathlen, path in find_all_paths(graph1, start, end)
        if exists_in_graph(path, required)
)
for pathlen, path in valid_paths:
    print(path, pathlen)

这样我们就可以在排除不需要的路径之前对其进行过滤。如果您只想要最短的路径,可以将sorted的来电替换为min

pathlen, path = min((pathlen, path)
    for pathlen, path in find_all_paths(graph1, start, end)
        if exists_in_graph(path, required)
)
print(path, pathlen)

<强>输出

['e', 'a', 'd', 'k'] 8

我对您的代码进行了一些其他小的更改。

如果用户在'via'提示符下没有输入任何内容,则required将设置为包含空字符串的列表,这会弄乱您的exists_in_graph测试,所以如果发生这种情况我们将required设置为空列表。

在您的find_all_paths版本中,您为path提供了空列表的默认值。如果你想多次调用find_all_paths,那将会搞砸,因为默认args是在创建函数时评估的,而不是在调用它时。因此,如果您在未提供find_all_paths arg的情况下再次致电path,它将继续使用最初使用的默认path列表,将不会使用新的清单。处理此问题的常用方法是使用默认值None,就像我在代码中所做的那样。有关此重要主题的详细信息,请参阅“Least Astonishment” and the Mutable Default Argument