Djikstra 的实现似乎与理论复杂性不符

Implementation of Djikstra doesn't seem to match theoretical complexity

我正在实施另一个 Djikstra 的最短路径算法。我在小型数据集上使用了 运行 它,据我所知它做事很好。

目标是使用它来近似网格上的测地线。因此,例如下图中的绿线是连接 2 个红点的测地线的近似值:

该图像中的网格有 409 个顶点和 2304 个边。渐近复杂性将给出: 409 + 2304 * log(409) \大约 14 000 步。

我的算法 运行 需要 830678 步来计算您看到的解决方案。我知道实际步数与 t 的渐近符号不匹配,但数量级应该更接近。此外,我的算法的 运行time 似乎随着输入的增长而稳步增加。例如,对于渐近复杂度预测 30,000 次迭代的输入,算法 运行 进行数百万次迭代,直到我杀死它(即它可以继续运行)。几乎就好像复杂度是 运行 超几何级数或指数级。

我想看看我在哪里搞砸了,以至于我得到了正确的输出(测试)但错误 运行时间。

代码:

template <typename T>
std::pair<std::vector<uint>, std::vector<double>> Djikstra(
    const std::vector<T>& node_list,
    std::vector<T> (*GetNeighbours)(const T& t),
    uint (*GetId)(const T& t),
    double (*GetDistance)(const T& t1, const T& t2),
    uint start)
{
    std::vector<double> node_distance_map(node_list.size(), std::numeric_limits<double>::max());
    std::vector<uint> node_parent_map(node_list.size());
    std::vector<bool> node_visited_map(node_list.size(), false);

    typedef std::pair<double, uint> NodeInfo;
    std::priority_queue<NodeInfo, std::vector<NodeInfo>, std::greater<NodeInfo>> queue;

    node_distance_map[start] = 0;
    node_parent_map[start] = start;
    queue.push({0, start});

    uint current_node_index = start;
    uint count = 0;
    while(!queue.empty())
    {
        auto[current_distance, current_node_index] = queue.top();
        queue.pop();
        auto current_node = node_list[current_node_index];

        node_visited_map[current_node_index] = true;

        auto neighbours = GetNeighbours(current_node);
        assert(neighbours.size() > 0);
        double shortest_distance = std::numeric_limits<double>::max();
        auto& shortest_neighbour = neighbours[0];
        for(auto& neighbour : neighbours)
        {
            uint neighbour_id = GetId(neighbour);
            // Skip any node that has already been visited
            if(node_visited_map[neighbour_id]) continue;
            double distance = GetDistance(current_node, neighbour);
            double total_distance = distance + current_distance;

            // Overwrite prior distances of the current distance is shorter
            queue.push({total_distance, neighbour_id});
            if(total_distance < node_distance_map[neighbour_id])
            {
                node_parent_map[neighbour_id] = current_node_index;
                node_distance_map[neighbour_id] = total_distance;
            }
        }
        if(count++ % 1'000'000 == 0) std::cout << "At: " << count << std::endl;
    }
    std::cout << "Final: " << count << std::endl;

    return {node_parent_map, node_distance_map};
}

编辑:

这是表示图表的 DS:

struct Node
{
    typedef std::shared_ptr<Node> NodePtr;
    uint id;
    vector<std::shared_ptr<Node>> neighbours;
    Eigen::Vector3d position;
};

std::vector<Node::NodePtr> NodeGetNeighbours(const Node::NodePtr& v)
{
    return v->neighbours;
}

uint NodeGetId(const Node::NodePtr& v)
{
    return v->id;
}

double NodeGetDistance(const Node::NodePtr& v1, const Node::NodePtr& v2)
{
    return (v1->position - v2->position).norm();
}

queue.push() 语句放入 if.

if (total_distance < node_distance_map[neighbour_id])
{
    node_parent_map[neighbour_id] = current_node_index;
    node_distance_map[neighbour_id] = total_distance;
    queue.push({ total_distance, neighbour_id });
}

如果不这样做,您仍然会得到正确的答案,但是优先级队列的大小会随着您的操作方式不断增加,因为即使 total_distance >= node_distance_map[neighbour_id] 它仍在推送这个新的 pair .事实上,要实现最快的 Dijkstra 实现,您必须使用 decreasePriority 方法创建一个优先级队列。但是为此,需要知道存储对象的队列中的 index,您希望降低其优先级。这样你就可以保证优先级队列中的节点不会超过 N 个。