当前位置: 首页 > 知识库问答 >
问题:

如何使用此评估(BFS)避免堆栈溢出

冯枫
2023-03-14

我已经构造了一个NFA,我正在运行这个方法来评估机器,看看一个表达式是否有效。这适用于小的正则表达式,但是当我的正则表达式的大小以及NFA的大小变得太大时,这个搜索会向我抛出一个堆栈溢出。我很确定这是因为我已经实现了一个BFS,正在使用递归,并且可能没有很好地处理我的基本案例。

这个方法接受一个表达式和一个节点(从NFA的起始节点开始)。首先,它检查表达式的长度是否为零,如果我在接受节点(节点上的布尔值)中,然后返回true。如果表达式长度为零,但当前节点不是accept节点,则返回false。

如果这两个都不求值,那么我将得到当前节点可以使用“e”(epsilon)转换到达的所有节点的列表,并求值它们。

如果没有“E”节点,那么我从输入表达式中删除第一个字符,生成表达式的一个缩短的子字符串(删除表达式的前部),然后使用删除的字符和缩减的表达式查找该节点可以到达的节点列表。

如果这两个都不命中,则返回false

一个基本的正则表达式是(ab)*A,一个求值表达式的例子是aaaa,它在每一次求值时都被简化,AAAA->AAA->AA->A->

    private boolean evaluate(autoNode node, String expression)
{

    if(expression.length()==0 && node.getAccept())
    {
        return true;
    }
    else if(expression.length()==0 && !node.getAccept())
    {
        return false;
    }

    String evalExp = expression.charAt(0)+""; //The first character in the expression
    String redExp = expression.substring(1, expression.length()); 

    //for each epsilon transition, evaluate it
    if(node.getTransSet().contains("e"))
    {
        //if this node has an "e" transition then...
        ArrayList<autoNode> EpsilonTransMap = node.getPathMap("e");
        //The above ArrayList is a list of all the nodes that this node can reach
        //using the "e" / epsilon transition
        for(autoNode nodes : EpsilonTransMap)
        {               
            if(evaluate(nodes, expression))
            {
                return true;
            }
        }
    }
    //for each transition on that key evaluate it
    if(node.getTransSet().contains(evalExp))
    {
        //if this node has a transition from the front of the expression then...
        ArrayList<autoNode> TransitionKeyMap = node.getPathMap(evalExp);
        //The above ArrayList is a list of all the nodes that this node can reach
        //on a transition equal to the "key" removed from the front of the expression String
        for(autoNode nodes : TransitionKeyMap)
        {
            if(evaluate(nodes, redExp))
            {
                return true;
            }
        }
    }

    return false;
}

我意识到我可能因为使用bfs搜索而不是DFS而导致了自己的问题。我想知道是否有人可以帮助我修复这一点,并避免堆栈溢出,使太多的事情同时进行。因为虽然(ab)*A可以很好地评估...

((aa)+(bb)+(cc)+)(ba)(ca)

创建一个相当大的NFA,在计算“a”时导致堆栈溢出

任何不会导致我完全放弃这个方法的东西都是很好的,也是很感激的。

共有1个答案

宋高扬
2023-03-14

其实这里没有DFS或BFS,但这并不重要。我想不能使用带有字母“e”的正则表达式也不重要。

重要的是,每当到达epsilon转换的循环时,就会出现堆栈溢出。例如:

evalue(n1,“AA”)找到从n1到n2的epsilon转换,并递归:

求值(n2,“aa”)发现从n2到n1的epsilon跃迁并递归:

评估(n1,“AA”)。如此循环,直到堆栈溢出。

你有很多方法可以解决这个问题...但是,即使您修复了它,这仍然是一个相当糟糕的评估NFA的算法--它可能会在状态数上花费指数级的时间!

编辑--所以,下面是使用伪代码进行NFA评估的正确方法:

boolean evaluate(Node nfa, String str)
{
    Set<Node> fromStates = new Set();
    fromStates.add(nfa);
    closeEpsilons(fromStates);

    for (char chr in str)
    {
        if (fromStates.size()==0)
            return false;

        //find all the states we can get to from
        //fromStates via chr

        Set<Node> toStates = new Set();
        for (Node fromState in fromStates)
        {
            //OP's code would say .getPathMap(chr) here
            for(Node toState in fromState.getTransitionTargets(chr))
            {
                if (!toStates.contains(toState))
                    toStates.add(toState);
            }
        }
        closeEpsilons(toStates);

        //process the rest of the string with the state set we just found
        fromStates = toStates;
    }

    //string is done.  see if anything accepts
    for(Node state in fromStates)
    {
        if (state.accepts())
        {
            return true;
        }
    }
    return false;
}

//expand a state set with all states is reaches via epsilons
void closeEpsilons(Set<Node> states)
{
    Queue<Node> processQueue = new Queue();
    processQueue.addAll(states);

    while(!processQueue.isEmpty())
    {
        Node fromState = processQueue.removeFirst();

        //OP's code would say "getPathMap("e") here
        for(Node toState in fromState.getEpsilonTargets())
        {
            if (!states.contains(toState))
            {
                //found a new state
                states.add(toState);
                //we'll have to search it for epsilons
                processQueue.add(toState);
            }
        }
    }
}
 类似资料:
  • 问题内容: 这是我在处理Django项目时出现的一个问题。关于表单验证。 在Django中,当您提交表单时,可以调用相应的表单对象以触发验证并返回布尔值。因此,通常在视图函数中有类似的代码: 不仅可以验证表单数据,还可以向表单对象添加错误消息,这些错误消息随后可以显示给用户。 在一页上,我同时使用两种形式,并且还希望仅当两种形式均包含有效数据时才保存数据。这意味着我必须在执行代码以保存数据之前在两

  • 我有一个执行快速排序的应用程序。在我开始给它一些更大的数字(我第一次得到它是10000000)之前,它工作得很好。我知道是由递归引起的,但我不明白为什么我的应用程序会因此而崩溃。如有任何建议,将不胜感激。这是我的密码:

  • 我有一个分布式任务队列,其中的任务如下所示: 这里有一个竞争条件:如果任务队列软件在完全相同的时间启动其中两个任务,它们都将从数据库中获得相同的<code>old_path</code>,并且竞争失败者的取消链接调用失败(将失败者的新路径从未来的取消链接中孤立出来)。 有没有办法让我构建它来绕过这场比赛?如果需要,我可以从当前设计中抛出几乎任何东西。具体来说,我使用的是PostgreSQL,Pyt

  • 问题内容: Context 当函数返回aTABLE或a时,例如以下示例函数: 可以通过多种方法访问结果: 1)将产生以下输出列: 2)select func(3)将仅产生ROW类型的一个输出列。 unc (1,3) (2,9) (3,27) i Ĵ — +- 1 | 3 2 | 9 3 | 27 select N, (func(N)).* from (select 2 as N union sel

  • 问题内容: 我想使用rxjava Observable处理翻新中的分页。我听了另一个问题的建议。 我有100多个页面需要获取,但是链在第20页左右失败,并且在logcat中使用以下日志停止了对可观察对象的任何进一步订阅 有人知道为什么会发生这种情况吗? 更新: 我知道这是由于递归而发生的,但是有没有更优雅的方式来处理带有翻新和rxjava的分页? 问题答案: 因此,鉴于我回答了您引用的原始问题,我

  • 我需要一些帮助。em以这种方式向活动添加片段。问题是每次调用openFragment时,它都会创建片段并添加。这是显而易见的。问:我做了什么修改,所以它只能添加一次片段。在下一次使用相同的片段标记调用时,它不会执行任何操作。 案例:第一次按下按钮添加片段并显示。我再按一次同样的按钮,它没有反应。