如何修改大数的 de bruijn 算法?

How to modify de bruijn algorithm for large numbers?

提问人:niico 提问时间:11/11/2022 最后编辑:niico 更新时间:11/11/2022 访问量:48

问:

我有以下代码用于 de Bruijn 序列输出。 它适用于小数字,但如果 number = 18,则会抛出错误:

RecursionError: maximum recursion depth exceeded in comparison

我明白这与什么有关,如何在没有递归的情况下在这里编写 dfs 函数?

seen = set()
edges = []

def dfs(node, k, A):
    for i in range(k):
        str = node + A[i]
        if (str not in seen):
            seen.add(str)
            dfs(str[1:], k, A)
            edges.append(i)

def  de_bruijn(n, k, A):
    seen.clear()
    edges.clear()

    start_node = A[0] * (n - 1)
    dfs(start_node, k, A)
    s = ""
    l = int(math.pow(k, n))
    for i in range(l):
        s += A[edges[i]]
    s += start_node
    return s


n = 18
k = 3
A = '012'
print(de_bruijn(n ,k, A))
算法 序列 深度优先搜索

评论


答:

-1赞 btilly 11/11/2022 #1

超过递归限制不会让你做太多这样的事,因为问题会耗尽内存,但你必须把所有东西都变成一个显式的堆栈。这是您的代码:

def dfs(node, k, A):
    for i in range(k):
        str = node + A[i]
        if (str not in seen):
            seen.add(str)
            dfs(str[1:], k, A)
            edges.append(i)

现在的想法是,我们将有一个行动。它将具有类型和值。类型正在进入或退出函数和循环。每个操作都会执行一些代码。因此,新函数将具有以下结构:

def dfs (node, k, A):
    stack = [('enter_function`, (node, k, A))]
    while 0 < len(stack)
        type, value = stack.pop()
        if type == 'enter_function':
            ...
        elif type == 'exit_function':
            ...
        elif type == 'enter_loop':
            ...
        elif type == 'exit_loop':
            ...

现在我们在每个部分做什么?这是显示这个想法的工作代码。请注意,我们放在堆栈上的第一件事是第二件事。因此,我们总是必须在进入之前先设置出口。

def dfs (node, k, A):
    stack = [('enter_function', node)]
    i = 0
    while 0 < len(stack):
        #print(stack)
        action, value = stack.pop()
        if action == 'enter_function':
            node = value
            for i in range(k-1, -1, -1):
                 stack.append(('enter_loop', i))
        elif action == 'exit_function':
            (node, i) = value
            edges.append(i)
        elif action == 'enter_loop':
            i = value
            string = node + A[i]
            if string not in seen:
                seen.add(string)
                stack.append(('exit_function', (node, i)))
                stack.append(('enter_function', string[1:]))

顺便说一句,性能提示。通过附加来构建一个大字符串需要时间。构建一个大型数组,然后加入它要快得多。O(n^2)

def de_bruijn(n, k, A):
    seen.clear()
    edges.clear()

    start_node = A[0] * (n - 1)
    dfs(start_node, k, A)
    s = []
    l = int(math.pow(k, n))
    for i in range(l):
        s.append(A[edges[i]])
    s.append(start_node)
    return "".join(s)

评论

0赞 niico 11/11/2022
我真的不明白我们为什么要添加堆栈?因此,如果实现了函数,我无法修复函数中出现的错误
0赞 btilly 11/11/2022
@niico我修复了代码。添加堆栈的原因是递归函数隐式维护堆栈。要摆脱它,您需要明确它。递归函数交错循环控制和函数调用。因此,显式堆栈还必须处理循环。
0赞 btilly 11/11/2022
同样在我的计算机上,摆脱递归让它运行. 很慢。我很确定你会在运行之前用完内存。n=12n=13n=18
0赞 btilly 11/11/2022
@niico 我还为您添加了一个性能提示。