最短距离算法 Python

发布于 2024-12-19 10:03:21 字数 2250 浏览 5 评论 0原文

我想创建一个简单的广度优先搜索算法,它返回最短路径。

演员信息字典将演员映射到该演员出现的电影列表:

actor_info = { "act1" : ["movieC", "movieA"], "act2" : ["movieA", "movieB"], 
     "act3" :["movieA", "movieB"], "act4" : ["movieC", "movieD"], 
     "act5" : ["movieD", "movieB"], "act6" : ["movieE"], 
     "act7" : ["movieG", "movieE"], "act8" : ["movieD", "movieF"], 
     "KevinBacon" : ["movieF"], "act10" : ["movieG"], "act11" : ["movieG"] }

与此相反,将电影映射到其中出现的演员列表:

movie_info = {'movieB': ['act2', 'act3', 'act5'], 'movieC': ['act1', 'act4'], 
      'movieA': ['act1', 'act2', 'act3'], 'movieF': ['KevinBacon', 'act8'], 
      'movieG': ['act7', 'act10', 'act11'], 'movieD': ['act8', 'act4', 'act5'], 
      'movieE': ['act6', 'act7']}

因此对于调用,

shortest_dictance("act1", "Kevin Bacon", actor_info, movie_info)

我应该得到 3 因为 act1 出现在 movieC 中,Act4 出现在 movieD 中,Act8 出现在 >电影F与凯文培根。所以最短距离是 3。

到目前为止,我有这样的情况:

def shotest_distance(actA, actB, actor_info, movie_info):
   '''Return the number of movies required to connect actA and actB. 
   If theres no connection return -1.'''

    # So we keep 2 lists of actors:
    #   1.The actors that we have already investigated.
    #   2.The actors that need to be investigated because we have found a 
    #      connection beginning at actA. This list must be 
    #      ordered, since we want to investigate actors in the order we 
    #      discover them.
    #      -- Each time we put an actor in this list, we also store
    #         her distance from actA.
    investigated = []
    to_investigate = [actA]
    distance = 0
    while actB not in to_investigate and to_investigate!= []:
        for actor in to_investigate:
            to_investigated.remove(actA)
            investigated.append(act)

            for movie in actor_info[actor]:
                for co_star in movie_info[movie]:
                    if co_star not in (investigated and to_investigate):
                        to_investigate.append(co_star)

 ....
 ....

 return d    

我无法找到适当的方法来跟踪代码每次迭代发现的距离。此外,该代码似乎在时间上效率很低。

I wanted to create a simple breadth first search algorithm, which returns the shortest path.

An actor information dictionary maps and actor to the list of movies the actor appears in:

actor_info = { "act1" : ["movieC", "movieA"], "act2" : ["movieA", "movieB"], 
     "act3" :["movieA", "movieB"], "act4" : ["movieC", "movieD"], 
     "act5" : ["movieD", "movieB"], "act6" : ["movieE"], 
     "act7" : ["movieG", "movieE"], "act8" : ["movieD", "movieF"], 
     "KevinBacon" : ["movieF"], "act10" : ["movieG"], "act11" : ["movieG"] }

The inverse of this maps movies to the list of actors appearing in them:

movie_info = {'movieB': ['act2', 'act3', 'act5'], 'movieC': ['act1', 'act4'], 
      'movieA': ['act1', 'act2', 'act3'], 'movieF': ['KevinBacon', 'act8'], 
      'movieG': ['act7', 'act10', 'act11'], 'movieD': ['act8', 'act4', 'act5'], 
      'movieE': ['act6', 'act7']}

so for a call

shortest_dictance("act1", "Kevin Bacon", actor_info, movie_info)

I should get 3 since act1 appears in movieC with Act4 who appears in movieD with Act8 who appears in movie F with KevinBacon. So the shortest distance is 3.

So far I have this:

def shotest_distance(actA, actB, actor_info, movie_info):
   '''Return the number of movies required to connect actA and actB. 
   If theres no connection return -1.'''

    # So we keep 2 lists of actors:
    #   1.The actors that we have already investigated.
    #   2.The actors that need to be investigated because we have found a 
    #      connection beginning at actA. This list must be 
    #      ordered, since we want to investigate actors in the order we 
    #      discover them.
    #      -- Each time we put an actor in this list, we also store
    #         her distance from actA.
    investigated = []
    to_investigate = [actA]
    distance = 0
    while actB not in to_investigate and to_investigate!= []:
        for actor in to_investigate:
            to_investigated.remove(actA)
            investigated.append(act)

            for movie in actor_info[actor]:
                for co_star in movie_info[movie]:
                    if co_star not in (investigated and to_investigate):
                        to_investigate.append(co_star)

 ....
 ....

 return d    

I can't figure the appropriate way to keep track of the distances discovered each of iteration of the code. Also the code seems to be very ineffecient time wise.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

拔了角的鹿 2024-12-26 10:03:21

首先创建一个图来连接所有节点,然后运行最短路径代码(可能有一个高效的图库来完成此操作,而不是下面提到的函数,尽管如此,这个库很优雅),然后找出所有节点的数量最短路径的电影名称。

for i in movie_info:
    actor_info[i] = movie_info[i]

def find_shortest_path(graph, start, end, path=[]):
    path = path + [start]
    if start == end:
        return path
    if not start in graph:
        return None
    shortest = None
    for node in graph[start]:
        if node not in path:
            newpath = find_shortest_path(graph, node, end, path)
            if newpath:
                if not shortest or len(newpath) < len(shortest):
                    shortest = newpath
    return shortest

L = find_shortest_path(actor_info, 'act1', 'act2')
print len([i for i in L if i in movie_info])

find_shortest_path 来源: http://www.python.org/doc/essays/graphs/

Firstly create one graph out of this to connect all the nodes and then run the shortest_path code(There could be an efficient graph library to do this instead of the function mentioned below, nevertheless this one is elegant) and then find out all the number of movie names from the shortest path.

for i in movie_info:
    actor_info[i] = movie_info[i]

def find_shortest_path(graph, start, end, path=[]):
    path = path + [start]
    if start == end:
        return path
    if not start in graph:
        return None
    shortest = None
    for node in graph[start]:
        if node not in path:
            newpath = find_shortest_path(graph, node, end, path)
            if newpath:
                if not shortest or len(newpath) < len(shortest):
                    shortest = newpath
    return shortest

L = find_shortest_path(actor_info, 'act1', 'act2')
print len([i for i in L if i in movie_info])

find_shortest_path Source: http://www.python.org/doc/essays/graphs/

九厘米的零° 2024-12-26 10:03:21

这看起来可行。它跟踪当前的一组电影。对于每一步,它都会查看所有尚未考虑(“看过”)的单步电影。

actor_info = { "act1" : ["movieC", "movieA"], "act2" : ["movieA", "movieB"], 
     "act3" :["movieA", "movieB"], "act4" : ["movieC", "movieD"], 
     "act5" : ["movieD", "movieB"], "act6" : ["movieE"], 
     "act7" : ["movieG", "movieE"], "act8" : ["movieD", "movieF"], 
     "KevinBacon" : ["movieF"], "act10" : ["movieG"], "act11" : ["movieG"] }

movie_info = {'movieB': ['act2', 'act3', 'act5'], 'movieC': ['act1', 'act4'], 
      'movieA': ['act1', 'act2', 'act3'], 'movieF': ['KevinBacon', 'act8'], 
      'movieG': ['act7', 'act10', 'act11'], 'movieD': ['act8', 'act4', 'act5'], 
      'movieE': ['act6', 'act7']}

def shortest_distance(actA, actB, actor_info, movie_info):
    if actA not in actor_info:
        return -1  # "infinity"
    if actB not in actor_info:
        return -1  # "infinity"
    if actA == actB:
        return 0

    dist = 1
    movies = set(actor_info[actA])
    end_movies = set(actor_info[actB])
    if movies & end_movies:
        return dist

    seen = movies.copy()
    print "All movies with", actA, seen
    while 1:
        dist += 1
        next_step = set()
        for movie in movies:
            for actor in movie_info[movie]:
                next_step.update(actor_info[actor])
        print "Movies with actors from those movies", next_step
        movies = next_step - seen 
        print "New movies with actors from those movies", movies
        if not movies:
            return -1 # "Infinity"

        # Has actorB been in any of those movies?
        if movies & end_movies:
            return dist

        # Update the set of seen movies, so I don't visit them again
        seen.update(movies)

if __name__ == "__main__":
    print shortest_distance("act1", "KevinBacon", actor_info, movie_info)

输出是

All movies with act1 set(['movieC', 'movieA'])
Movies with actors from those movies set(['movieB', 'movieC', 'movieA', 'movieD'])
New movies with actors from those movies set(['movieB', 'movieD'])
Movies with actors from those movies set(['movieB', 'movieC', 'movieA', 'movieF', 'movieD'])
New movies with actors from those movies set(['movieF'])
3

Here's a version,它返回构成最小连接的电影列表(无连接则为 None,如果 actA 和 actB 相同则返回空列表。)

def connect(links, movie):
    chain = []
    while movie is not None:
        chain.append(movie)
        movie = links[movie]
    return chain

def shortest_distance(actA, actB, actor_info, movie_info):
    if actA not in actor_info:
        return None  # "infinity"
    if actB not in actor_info:
        return None  # "infinity"
    if actA == actB:
        return []

    # {x: y} means that x is one link outwards from y
    links = {}

    # Start from the destination and work backward
    for movie in actor_info[actB]:
        links[movie] = None
    dist = 1
    movies = links.keys()

    while 1:
        new_movies = []
        for movie in movies:
            for actor in movie_info[movie]:
                if actor == actA:
                    return connect(links, movie)
                for other_movie in actor_info[actor]:
                    if other_movie not in links:
                        links[other_movie] = movie
                        new_movies.append(other_movie)
        if not new_movies:
            return None # Infinity
        movies = new_movies

if __name__ == "__main__":
    dist = shortest_distance("act1", "KevinBacon", actor_info, movie_info)
    if dist is None:
        print "Not connected"
    else:
        print "The Kevin Bacon Number for act1 is", len(dist)
        print "Movies are:", ", ".join(dist)

输出如下:

The Kevin Bacon Number for act1 is 3
Movies are: movieC, movieD, movieF

This looks like it works. It keeps track of a current set of movies. For each step, it looks at all of the one-step-away movies which haven't already been considered ("seen").

actor_info = { "act1" : ["movieC", "movieA"], "act2" : ["movieA", "movieB"], 
     "act3" :["movieA", "movieB"], "act4" : ["movieC", "movieD"], 
     "act5" : ["movieD", "movieB"], "act6" : ["movieE"], 
     "act7" : ["movieG", "movieE"], "act8" : ["movieD", "movieF"], 
     "KevinBacon" : ["movieF"], "act10" : ["movieG"], "act11" : ["movieG"] }

movie_info = {'movieB': ['act2', 'act3', 'act5'], 'movieC': ['act1', 'act4'], 
      'movieA': ['act1', 'act2', 'act3'], 'movieF': ['KevinBacon', 'act8'], 
      'movieG': ['act7', 'act10', 'act11'], 'movieD': ['act8', 'act4', 'act5'], 
      'movieE': ['act6', 'act7']}

def shortest_distance(actA, actB, actor_info, movie_info):
    if actA not in actor_info:
        return -1  # "infinity"
    if actB not in actor_info:
        return -1  # "infinity"
    if actA == actB:
        return 0

    dist = 1
    movies = set(actor_info[actA])
    end_movies = set(actor_info[actB])
    if movies & end_movies:
        return dist

    seen = movies.copy()
    print "All movies with", actA, seen
    while 1:
        dist += 1
        next_step = set()
        for movie in movies:
            for actor in movie_info[movie]:
                next_step.update(actor_info[actor])
        print "Movies with actors from those movies", next_step
        movies = next_step - seen 
        print "New movies with actors from those movies", movies
        if not movies:
            return -1 # "Infinity"

        # Has actorB been in any of those movies?
        if movies & end_movies:
            return dist

        # Update the set of seen movies, so I don't visit them again
        seen.update(movies)

if __name__ == "__main__":
    print shortest_distance("act1", "KevinBacon", actor_info, movie_info)

The output is

All movies with act1 set(['movieC', 'movieA'])
Movies with actors from those movies set(['movieB', 'movieC', 'movieA', 'movieD'])
New movies with actors from those movies set(['movieB', 'movieD'])
Movies with actors from those movies set(['movieB', 'movieC', 'movieA', 'movieF', 'movieD'])
New movies with actors from those movies set(['movieF'])
3

Here's a version which returns a list of movies making up the minimum connection (None for no connection, and an empty list if the actA and actB are the same.)

def connect(links, movie):
    chain = []
    while movie is not None:
        chain.append(movie)
        movie = links[movie]
    return chain

def shortest_distance(actA, actB, actor_info, movie_info):
    if actA not in actor_info:
        return None  # "infinity"
    if actB not in actor_info:
        return None  # "infinity"
    if actA == actB:
        return []

    # {x: y} means that x is one link outwards from y
    links = {}

    # Start from the destination and work backward
    for movie in actor_info[actB]:
        links[movie] = None
    dist = 1
    movies = links.keys()

    while 1:
        new_movies = []
        for movie in movies:
            for actor in movie_info[movie]:
                if actor == actA:
                    return connect(links, movie)
                for other_movie in actor_info[actor]:
                    if other_movie not in links:
                        links[other_movie] = movie
                        new_movies.append(other_movie)
        if not new_movies:
            return None # Infinity
        movies = new_movies

if __name__ == "__main__":
    dist = shortest_distance("act1", "KevinBacon", actor_info, movie_info)
    if dist is None:
        print "Not connected"
    else:
        print "The Kevin Bacon Number for act1 is", len(dist)
        print "Movies are:", ", ".join(dist)

Here's the output:

The Kevin Bacon Number for act1 is 3
Movies are: movieC, movieD, movieF
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文