【问题标题】:How do I retrieve the first found element with the lowest value within a vector如何检索向量中第一个找到的具有最小值的元素
【发布时间】:2018-04-19 10:10:56
【问题描述】:

我已经实现了 A*。但是,当它运行时,当向量中的所有节点具有相等的 f 分数时,它作为 BFS 运行。

有许多简单的优化或实现细节会显着影响 A* 实现的性能。第一个需要注意的细节是,在某些情况下,优先级队列处理关系的方式会对性能产生重大影响。如果平局被打破,队列以 LIFO 方式运行,A* 的行为类似于在等成本路径中进行深度优先搜索(避免探索多个同等最优解决方案)
[维基百科-A*]

我想知道是否有办法修改我现有的程序(提供了 sn-p)以不仅检索最低元素,而且检索第一个最低元素。

void Search::aStar()
{
    searchMethod = "ASTAR";
    generateMap();

    // Draw Window and wall
    guiOpen("VisX", 800, 600);
    guiShowWall();

    std::vector<Node> nodeHistory;
    std::vector<std::reference_wrapper<Node>> openSet;

    // Get starting point, add it to the queue and set as visited
    Coord start = getStartPos();
    Node &root = mapMaze[start.x][start.y];
    openSet.push_back(root);
    root.setVisitFlag(true);

    root.setGScore(0);

    while (!openSet.empty())
    {
        // Put the minimium fscore element to the front 
        auto result =  std::min_element(openSet.begin(), openSet.end(), lowestFScore());
        int minElementPos = std::distance(std::begin(openSet), result);
        std::swap(openSet[minElementPos], openSet.front());

        Node &current = openSet.front();

        // Re-assign pending flag to visited
        current.setPendingVisit(false);
        current.setVisitFlag(true);

        // Update the GUI display
        guiRefresh(current);

        openSet.erase(openSet.begin());

        // Add to list of visited nodes 
        nodeHistory.push_back(current);

        if (current.isFinish())
        {
            std::cout << "[Informed] A*: Found Finish"
                      << "\nNote: Speed of search has been slowed down by GUI display."
                      << std::endl;

            // Construct path & update GUI with path
            constructPath(nodeHistory);
            guiShowConstructedPath();

            guiClose();
            break;
        }

        // Add each valid edges node to the queue
        for (int i = 0; i < EDGE_AMOUNT; i++)
        {
            if (current.isValidEdge(i))
            {
                Node &neighbor = mapMaze[current.getEdge(i).x][current.getEdge(i).y];
                // If not a wall and has been visited, ignore
                if (neighbor.isNotWall() && !(neighbor.isNotVisited())) continue;

                // If not in openset, add it and set flag
                if (neighbor.isNotWall() && neighbor.isNotVisited() && neighbor.isNotPendingVisit())
                {
                    // Add to queue and set flag
                    openSet.push_back(neighbor);
                    neighbor.setPendingVisit(true);

                    // Update the GUI display
                    guiRefresh(neighbor);
                }

                // Calculate gScore, and see if it is better than neigbours current score.
                #define MOVEMENT_COST (1)
                int tentativeGScore = current.getGScore() + MOVEMENT_COST;
                if (tentativeGScore >= neighbor.getGScore()) continue;

                // This path is the best until now. Record it!
                neighbor.setParent(current);
                neighbor.setGScore(tentativeGScore);
                int fScore = neighbor.getGScore() + neighbor.getHScore();
                neighbor.setFScore(fScore);
            }
        }
    }
}
struct lowestFScore
{
    bool operator()(const Node& lhs, const Node& rhs) const
    {
        return lhs.getFScore() < rhs.getFScore();
    }
};

【问题讨论】:

  • @Holt 我已更新问题以包含完整的 A* 实现
  • 您的报价涉及优先级队列,您的代码没有使用优先级队列,这可能是它很慢的原因。你不能优化你不使用的东西。
  • @Holt 我尝试实现优先级队列,但是它不允许动态优先级。所以我在stackoverflow.com/questions/2921349/…使用了Mark B的答案。
  • @Holt 但我想知道如何修改现有程序以获得第一个最低元素。
  • 您已经有了min_element 的第一个最低元素,因为您将元素添加到向量的后面。您只需将min_element 与反向迭代器一起使用。但是在查看这种优化之前,您应该重构您的代码:即使没有动态优先级,具有重复元素的基本优先级队列可能比您的代码更快,为什么您将 min 与第一个元素交换然后擦除第一个元素,而你可以只需擦除当前位置的元素?

标签: c++ search vector a-star


【解决方案1】:

std中有一个priority_queue。

这是一个参考:http://en.cppreference.com/w/cpp/container/priority_queue

我不确定它是否是你需要的:

#include <queue>
std::priority_queue<int, std::vector<int>, std::greater<int>> pq;
pq.push(1);
int min_elem = pq.top(); pq.pop();

【讨论】:

  • Afaik 优先级队列实际上在 tie 的情况下表现得像一个 LIFO 结构,或者它可能没有定义,但它绝对不像 FIFO...
【解决方案2】:

将您的Nodes 包装成这样的结构:

struct OpenNode
{
    const Node &node;
    const unsigned int order;
};

并定义你的openSet 喜欢:

std::vector<OpenNode> openSet;

在 while 循环之前将 unsigned int counter 初始化为 0 并进行以下更改:

// Add before while loop
unsigned int counter = 0;
// ...
Node &current = openSet.front().node;
// ...
openSet.push_back({neighbor, counter++});

最后改编lowestScore:

struct lowestFScore
{
    bool operator()(const OpenNode& lhs, const OpenNode& rhs) const
    {
        auto lScore = lhs.node.getFScore();
        auto rScore = rhs.node.getFScore();
        if (lScore == rScore)
        {
            // Bigger order goes first
            return lhs.order > rhs.order;
        }
        return lScore < rScore;
    }
};

按照建议,您可能希望将openSet 切换为std::priority_queue,这样可以更快地检索最少的元素。您应该能够使用相同的比较逻辑。

【讨论】:

    猜你喜欢
    • 2011-02-22
    • 2012-12-17
    • 2018-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-13
    • 2019-11-06
    相关资源
    最近更新 更多