Python中的前瞻算法的生成器理解

时间:2014-08-22 15:14:34

标签: python algorithm generator list-comprehension igraph

我昨天打电话询问如何在Python中展望未来。我的问题是遍历所有可能的边缘以添加到网络,并且对于添加了边缘的每个可能的网络,查看要添加的所有可能边缘,等等(n深度)。最后,将深度为n的所有网络与根网络进行比较,并实际添加最佳的第一步(添加最佳的第一条边以在深度n处获得最佳结果)。添加第一条边后,再次进行深度搜索,依此类推,直到找到一个好的网络。就像一个移动的窗口,我可以说(请参阅lookahead algorithm in Python以获得对该问题的更全面的解释)。

不幸的是,对于问题的清晰度,代码需要igraph,可在此处获取:http://igraph.org/python/#downloads

@Peter Gibson及时回答,引导我完成了Generator理解的逻辑,并帮助我制作了这段代码:

from igraph import * # http://igraph.org/python/

def delta(g,gOld): # evaluates the improvement of the graph from one generation to the next 
   print "delta"
   return g.diameter()-gOld.diameter()

def possible_new_edges(G):
    print "Possible new edges"
    allPossibleNewEdges = []
    for n1 in range(50):
        for n2 in range(n1,50):
            if G.are_connected(G.vs[n1],G.vs[n2]) == False and n1 != n2:
                allPossibleNewEdges.append(G.vs[n1],G.vs[n2])
    return allPossibleNewEdges

def add_optimal_edge(graph, n=3):
    print "Add optimal edge"

    paths = [[graph]] # start off with just one graph path, which contains a single graph
    for generation in range(n):
        print "Generation:", generation

        # path[-1] is the latest graph for each generation
        paths = (path + path[-1].add_edge(e) for path in paths for e in path[-1].possible_new_edges())
    # select best path by comparison of final generation against original graph 
    best = max(paths, lambda path: comp_delta(path[-1],graph)) 
    return best[1] # returns the first generation graph

graph = Graph.Erdos_Renyi(50, .15, directed=False, loops=False) # create a random root graph of density 0.15

add_optimal_edge(graph)

发电机简洁大方。对于我笨拙的Python风格,让我们说一点太优雅了,我需要了解一些让它工作的东西。代码运行时出现此错误:

return best[1] # returns the first generation graph
TypeError: 'generator' object has no attribute '__getitem__'

我认为这是因为发电机使用了错误的功能......

所以,我的问题是:在这样的发生器中使用函数的正确方法是什么?我需要调用possible_new_edges()和delta(),我需要传递它们(图表?)以及如何操作?

非常感谢!

1 个答案:

答案 0 :(得分:2)

尝试your gist中的代码,我发现了一些阻止代码运行的相当小的错误。我在下面列出了固定代码。但是,这并没有真正解决问题。那是因为你的算法需要考虑真正大量的潜在图表,这在任何合理的时间内都无法做到。

在我的测试中,向前迈出一步非常有效,但是看两个步骤需要很长时间(至少十分钟,我从未等待它完成),三个步骤可能需要几天时间。这是因为您的possible_new_edges函数返回了超过一千个可能的边。每个都将添加到初始图表的副本中。然后,对于每个后续步骤,该过程将在上一步骤的每个扩展图形上重复。这导致图形呈指数级爆炸,因为您必须按照1000**n图表的顺序评估某些内容,以查看哪个是最佳的。

因此,要获得实际结果,您仍需要改变一些事情。我不太了解图论或你的问题域,不足以暗示什么。

无论如何,这里是“工作”代码的更改部分(删除了原始注释,以便我对已更改内容的说明更清晰):

def possible_new_edges(G):

    print("Possible new edges")

    allPossibleNewEdges = []
    for n1 in range(50):
        for n2 in range(n1,50):
            if G.are_connected(G.vs[n1],G.vs[n2]) == False and n1 != n2:
                allPossibleNewEdges.append((G.vs[n1],G.vs[n2]))        # append a tuple
    return allPossibleNewEdges

def add_optimal_edge(graph, n=3):

    print("Add optimal edge")

    paths = [[graph]]
    for generation in range(n):

        print("Generation:", generation)

        paths = (path + [path[-1] + e]   # use + to add an edge, and to extend the path
                 for path in paths
                 for e in possible_new_edges(path[-1]))   # call this function properly
    best = max(paths, key=lambda path: comp_delta(path[-1],graph)) 
    return best[1]

如果循环中的生成器表达式让您感到困惑,则可能有助于将其替换为列表推导(通过用方括号替换最外面的括号)。然后,您可以检查循环内的paths列表(并执行打印其len()之类的操作)。代码的逻辑是相同的,生成器表达式只是推迟计算扩展结果,直到max函数开始迭代paths以找到最佳得分结果。

使用列表推导肯定适用于n=1,但是当你尝试n=2时,你可能会开始耗尽内存(你当然会选择n=3或更多)。上面的版本不会耗尽​​内存(因为生成器表达式一次只展开O(n)个图形),但这并不意味着它运行得足够快以在合理的时间内检查数十亿个图形。