使用智能指针时递归函数中的分段错误

时间:2016-03-18 11:26:37

标签: c++ c++11 recursion dynamic-programming smart-pointers

我在调用

时遇到分段错误
auto n1=std::make_shared<Node>(n,n->x+i,n->y+j); 

几次递归调用后。奇怪的是,它始终处于同一时间点。谁能发现问题? 这是一个动态编程问题的实现,在这里我正在积累路径的成本。我已经简化了成本函数,但在这个例子中问题仍然存在。

void HorizonLineDetector::dp(std::shared_ptr<Node> n)
{
    n->cost= 1 + n->prev->cost;
    //Check if we reached the last column(done!)
    if (n->x==current_edges.cols-1)
    {
        //Save the info in the last node if it's the cheapest path
        if (last_node->cost > n->cost)
        {
            last_node->cost=n->cost;
            last_node->prev=n;
        }
    }
    else
    {
        //Check for neighboring pixels to see if they are edges, launch dp with all the ones that are
        for (int i=0;i<2;i++)
        {
            for (int j=-1;j<2;j++)
            {
                if (i==0 && j==0) continue;
                if (n->x+i >= current_edges.cols || n->x+i < 0 ||
                    n->y+j >= current_edges.rows || n->y+j < 0) continue;
                if (current_edges.at<char>(n->y+j,n->x+i)!=0)
                {
                    auto n1=std::make_shared<Node>(n,n->x+i,n->y+j);
                    //n->next.push_back(n1);
                    nlist.push_back(n1);
                    dp(n1);
                }
            }
        }
    }
}
class Node
{
    public:
        Node(){}
        Node(std::shared_ptr<Node> p,int x_,int y_){prev=p;x=x_;y=y_;lost=0;}
        Node(Node &n1){x=n1.x;y=n1.y;cost=n1.cost;lost=n1.lost;prev=n1.prev;}//next=n1.next;}
        std::shared_ptr<Node> prev;             //Previous and next nodes
        int cost;               //Total cost until now
        int lost;               //Number of steps taken without a clear path
        int x,y;
        Node& operator=(const Node &n1){x=n1.x;y=n1.y;cost=n1.cost;lost=n1.lost;prev=n1.prev;}//next=n1.next;}
        Node& operator=(Node &&n1){x=n1.x;y=n1.y;cost=n1.cost;lost=n1.lost;prev=n1.prev;n1.prev=nullptr;}//next=n1.next;n1.next.clear();}
};

Error

Callstack

1 个答案:

答案 0 :(得分:0)

您的代码看起来像一个病态路径搜索,因为它几乎检查每条路径,并且不会跟踪它已经检查过的路径,您可以通过多种方式进行检查。

这将构建等于最长路径的长度的递归深度,然后是下一个最长的路径,并且...到最短的路径。即,像O(像素数)深度。

这很糟糕。并且,由于调用堆栈深度有限,会使您崩溃。

简单的解决方案是将dp修改为dp_internal,并让dp_internal返回vector个节点来处理 next 。然后编写dp,调用dp_internal并重复其返回值。

std::vector<std::shared_ptr<Node>>
HorizonLineDetector::dp_internal(std::shared_ptr<Node> n)
{
  std::vector<std::shared_ptr<Node>> retval;

...

            if (current_edges.at<char>(n->y+j,n->x+i)!=0)
            {
                auto n1=std::make_shared<Node>(n,n->x+i,n->y+j);
                //n->next.push_back(n1);
                nlist.push_back(n1);
                retval.push_back(n1);
            }

...

  return retval;
}

然后dp变为:

void HorizonLineDetector::dp(std::shared_ptr<Node> n)
{
  std::vector<std::shared_ptr<Node>> nodes={n};
  while (!nodes.empty()) {
    auto node = nodes.back();
    nodes.pop_back();
    auto new_nodes = dp_internal(node);
    nodes.insert(nodes.end(), new_nodes.begin(), new_nodes.end());
  }
}

但是(A)当排队节点的数量变得非常大时,这可能会崩溃,而且(B)这只是对递归 - 原因 - 崩溃的补丁,并不会使你的算法变得更少。

使用A *。

这涉及跟踪您访问过的节点以及接下来要处理的当前路径成本节点。

然后,您可以使用启发式算法来确定下一步要处理的内容。如果你在某种网格上,启发式是使用尽可能短的距离,如果没有任何障碍。

添加到达节点到进程的开销,加上从该节点到目标的启发式距离。找到至少总计的节点到进程。处理一个:将其标记为已访问,并将其所有相邻节点添加到要处理的节点列表中。

永远不要将节点添加到要处理的节点列表中(因为这是多余的工作)。

获得解决方案后,修剪节点列表以针对当前路径值大于或等于您的解决方案的任何节点进行处理。如果你知道你的启发式是一个强大的启发式(它不可能更快地到达目的地),你甚至可以根据启发式和当前成本的总和来修剪。同样,如果要被本段修剪,请不要添加到要处理的节点列表。

结果是你的算法在相对较直的线上搜索目标,然后向外扩展,试图找到绕过任何障碍的方法。如果存在相对直接的路线,则使用它并且甚至不会触及宇宙的其余部分。

您可以做很多关于A *的优化,甚至是不依赖于启发式的替代解决方案。但是从A *开始。