任何替代的DFS(非常慢)deepcopy的?(Any alternative to a (very

2019-07-28 22:36发布

我有一个包含81个顶点(每个顶点是顶点类的实例)一个很好的曲线图(列表)。 每个顶点有20个邻居。 每个顶点具有多个可能值(从1到9)其中,由于一些初始约束该问题将在平均4或5。我实现了一个简单的DFS在这个曲线图中,其采取的节点具有较少可能的值,的foreach值建立仅具有可能值的彼此“deepcopied”图形,最后再次通入DFS的“deepcopied”图表递归。 这个问题是关于速度; cProfiling我的代码,我发现该641秒我的Mac需要解决这个问题的635是由copy.deepcopy使用。 是否有此问题的任何变通办法? 这里是我的DFS:

def dfs(graph):
    global initial_time_counter

    if all(len(i.possible_values)==1 for i in graph):
        sys.exit("Done in: %s" % (time.time()-initial_time_counter))

    #find out the non-solved vertex with minimum possible values
    min_vertex=sorted(filter(lambda x: len(x.possible_values)>1,graph),
                      key=lambda x: len(x.possible_values))[0]

    for value in min_vertex.possible_values:

        sorted_graph_copy=sorted(copy.deepcopy(graph), key=lambda x: len(x.possible_values))
        min_vertex_copy=filter(lambda x: len(x.possible_values)>1,sorted_graph_copy)[0]
        sorted_graph_copy.remove(min_vertex_copy)

        if min_vertex_copy.try_value(value): #Can this vertex accept value -> value?
            min_vertex_copy.set_value(value) #Yes, set it.
            sorted_graph_copy.append(min_vertex_copy) #Append it to the graph.
            dfs(sorted_graph_copy) #Run the DFS again.
    return False

PS为最聪明的你可能已经意识到这个问题通常被称为数独。 请注意,我不是在寻找特定的数独的答案,以抽象的方式来分析这个问题。

[编辑]

同样的问题,用顶点的纯字符串表示走近,就把<0.75秒来解决。 我张贴供参考整个代码,如果任何人经历在今后类似的问题:

import sys,time

def srange():
    return [[x,y] for x in range(9) for y in range(9)]

def represent_sudoku(sudoku):
    print "\n".join(["|".join([str(elem) for elem in line]) for line in sudoku])

#Hard sudoku
sudoku=[[4, 0, 0, 0, 0, 0, 8, 0, 5], [0, 3, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 7, 0, 0, 0, 0, 0], [0, 2, 0, 0, 0, 0, 0, 6, 0], [0, 0, 0, 0, 8, 0, 4, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 6, 0, 3, 0, 7, 0], [5, 0, 0, 2, 0, 0, 0, 0, 0], [1, 0, 4, 0, 0, 0, 0, 0, 0]]

represent_sudoku(sudoku)

def get_nbs(x,y,sudoku,also_incomplete=False):
    line_nbs=sum([elem for elem in sudoku[y] if ((elem!=[0] and len(elem)==1) or also_incomplete)],[])

    column_nbs=sum([sudoku[xline][x] for xline in range(9) if ((sudoku[xline][x]!=[0] and len(sudoku[xline][x])==1) or also_incomplete)],[])


    area_nbs=[[j for j in i[(x/3)*3:(x/3)*3+3] if ((j!=[0] and len(j)==1) or also_incomplete)] for i in sudoku[(y/3)*3:(y/3)*3+3]]

    area_nbs=sum(sum(area_nbs,[]),[])    

    if not also_incomplete:
        return list(set(line_nbs+column_nbs+area_nbs))

    return line_nbs+column_nbs+area_nbs

for x,y in srange():
    sudoku[y][x]=[sudoku[y][x]]

def base_cleanup(sudoku):
    while 1:
        something_changed=False
        for x,y in srange():
            if sudoku[y][x]==[0] or len(sudoku[y][x])>1:
                possible_values=range(1,10) if sudoku[y][x]==[0] else sudoku[y][x]
                sudoku[y][x]=list(set(possible_values)-set(get_nbs(x,y,sudoku)))
                if sudoku[y][x]==[]:
                    return False
                something_changed=True if possible_values!=sudoku[y][x] else False
            else:
                sudoku[y][x]=sudoku[y][x]
        if not something_changed:
            break
    return sudoku


def dfs(graph):
    global s

    if graph==False:
        return False

    if all(sum([[len(elem)==1 for elem in line] for line in graph],[])):
        represent_sudoku(graph)
        sys.exit("Done in: %s" % (time.time()-s))

    enumerated_filtered_sudoku=filter(lambda x: len(x[1])>1, enumerate(sum(graph,[])))
    sorted_enumerated_sudoku=sorted(enumerated_filtered_sudoku,key=lambda x: len(x[1]))
    min_vertex=sorted_enumerated_sudoku[0]

    possible_values=[value for value in min_vertex[1]]

    for value in possible_values:        
        graph_copy=[[elem for elem in line] for line in graph]

        y,x=elements_position[min_vertex[0]]

        if not any(value==i for i in get_nbs(x,y,graph_copy)):
            graph_copy[y][x]=[value]
            if base_cleanup(graph_copy)!=False:
                graph_copy=base_cleanup(graph_copy)
                if graph_copy:
                    dfs(graph_copy)

    return False

sudoku = base_cleanup(sudoku)

elements_position = {i:srange()[i] for i in range(81)}
s = time.time()

dfs(sudoku)

Answer 1:

deepcopy的可能不是简单的拷贝大概是因为所有进入检测环路的努力相同的数据量,会慢很多 。 如果复制自己的图形以避免环路(容易,因为你知道网络的拓扑结构),而不是委托给deepcopy的一种方式,它很可能给你一个严重的提速。 我得到了50%的加速拷贝一个非常简单的数据结构的逐元素(使用内涵),如果复杂的了更大的节约不会让我感到吃惊。

当然,还有待获得更大的加速,如果你能避免在每一步使你的整个状态的完整副本。 例如,因为你第一次搜索的深度,你可以你的方法切换到维持撤消堆栈:只是记录你从选择的选择的每一个列表,你走回头路恢复它们。



Answer 2:

cPickle的比deepcopy的速度更快:

线#点击时间每命中%时间线内容

15                                           @profile
16                                           def test():
17       100       967139   9671.4     95.0      b = deepcopy(a)
18                                               #c = copy(a)
19                                               #d = ujson.loads(ujson.dumps(a))
20                                               #e = json.loads(json.dumps(a))
21                                               #f = pickle.loads(pickle.dumps(a, -1))
22       100        50422    504.2      5.0      g = cPickle.loads(cPickle.dumps(a, -1))


Answer 3:

你需要全图复制? 通常你只打算在搜索的任何步骤来修改它的一小部分,所以它可能是更有效地使用不可变的数据结构和重建仅需要什么。 这不适合循环工作,但我怀疑你的图形是一个列表?

我解决了Clojure中类似的问题,这对于不可改变的结构的原生支持,并管理以这种方式来获得合理的效率。 但我不知道蟒蛇任何一成不变的数据结构库(有一个写入时复制列表包的地方 - 那会是足够了吗?)

[只是为了澄清一个简单的例子-你是对元组是不可变的,因此,如果有过这样做元组的树: (1, (2, 3), (4, (5, 6)))然后你可能会产生这样的新树(1, (2, 99), (4, (5, 6)))通过创建只有两个元组-您可以复制(4, (5, 6))而不做深复制。 什么Clojure的有,什么我不知道的为蟒,是更复杂的结构(如哈希表),是遵循同样的原则。 它使DFS很容易,因为你不必担心在所有的改变“上游”的价值观。]

ps的,只有通过做你正在做什么,看到所涉及的费用,我了解弱势族群的做法的风采......



Answer 4:

我不完全了解的数独细节,但如果您使用图形的单个副本什么,并给每个节点的类,其中包括:

1)相邻的顶点2)名单“参观”,可用于跟踪什么已经看到国旗和什么也没有

你的深度优先搜索仍然可以是递归的,但不是打包组成图的修改子集,你只需注明“参观”的一个节点,继续,直到有无处可去。 如果你的图是连通的,看起来它应该工作...



文章来源: Any alternative to a (very slow) deepcopy in a DFS?