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 chaindef 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_moviesif __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 3Movies are: movieC, movieD, movieF