【问题标题】:Is there a way to make this shortest path algorithm faster?有没有办法让这个最短路径算法更快?
【发布时间】:2019-03-23 17:10:06
【问题描述】:

使用 CGAL 库,我正在尝试实现 Shortest Path 方法。

我有点成功,但是绘制路径所花费的时间几乎不能接受,在 Release 中运行最多需要 1.5 秒。

我知道输入可能非常大,有 50000 个面孔,但这是我必须处理的。

更详细地了解我正在尝试做的事情是能够通过单击两个不同的位置并从它们生成路径来沿着网格表面绘制 样条线,就像在图片:

我的类型定义是:

typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Surface_mesh<Kernel::Point_3> Triangle_mesh;
typedef CGAL::Surface_mesh_shortest_path_traits<Kernel, Triangle_mesh> Traits;
// default property maps
typedef boost::property_map<Triangle_mesh,
    boost::vertex_external_index_t>::type  Vertex_index_map;
typedef boost::property_map<Triangle_mesh,
    CGAL::halfedge_external_index_t>::type Halfedge_index_map;
typedef boost::property_map<Triangle_mesh,
    CGAL::face_external_index_t>::type     Face_index_map;
typedef CGAL::Surface_mesh_shortest_path<Traits> Surface_mesh_shortest_path;
typedef boost::graph_traits<Triangle_mesh> Graph_traits;
typedef Graph_traits::vertex_iterator vertex_iterator;
typedef Graph_traits::halfedge_iterator halfedge_iterator;
typedef Graph_traits::face_iterator face_iterator;

我的代码如下所示:

Traits::Barycentric_coordinates src_face_location = { { p1.barycentric[2], p1.barycentric[0], p1.barycentric[1] } };
face_iterator src_face_it = faces(map->m_cgal_mesh).first;
std::advance(src_face_it, src_faceIndex);

map->m_shortest_paths->remove_all_source_points();
map->m_shortest_paths->add_source_point(*src_face_it, src_face_location);

Traits::Barycentric_coordinates dest_face_location = { { p2.barycentric[2], p2.barycentric[0], p2.barycentric[1] } };
face_iterator dest_face_it = faces(map->m_cgal_mesh).first;
std::advance(dest_face_it, dest_faceIndex);

std::vector<Traits::Point_3> cgal_points;
auto r = map->m_shortest_paths->shortest_path_points_to_source_points(*dest_face_it, dest_face_location, std::back_inserter(cgal_points));

points.resize(cgal_points.size(), 3);

for (int i = 0; i < cgal_points.size(); ++i) {
    auto const& p = cgal_points[i];
    points.row(i) = RowVector3d(p.x(), p.y(), p.z());
}

占总时间99%的进程在这一行:

auto r = map->m_shortest_paths->shortest_path_points_to_source_points(*dest_face_it, dest_face_location, std::back_inserter(cgal_points));

对如何提高性能有任何想法吗?

【问题讨论】:

  • 对于你可能想要的工作代码codereview.stackexchange.com
  • 这很复杂,因为他们甚至没有 CGAL 的标签。
  • @JesperJuhl:不——这里没问题。从根本上说,这是一个算法问题。这些是这里的主题。我们正在寻找的其他东西也存在:当前代码和问题描述(=慢)。这甚至是一个有趣的问题。你总是有一个凸物体吗?加速通用算法的一个常见技巧是利用更有限的输入空间,我怀疑凸性很重要。
  • @sloriot 是的。输入总是凸的。我没有看到任何替代解决方案来避免重建数据结构,因为源点每次都在变化。
  • 您是否考虑过推出自己的最短路径实现?在这种情况下,重新发明轮子可能正是医生所要求的,因为 CGAL 是通用的且功能丰富的,而您的情况很简单,并且您具有特殊的知识。此外,最短路径算法通常很容易实现。我的 C 开发人员想说,这可以不那么冗长。

标签: c++ algorithm path-finding cgal


【解决方案1】:

CGAL 文档指出,当您在 2D 平面上展开网格时,最短路径始终是直线。 最短路径算法的输入是具有重心坐标的顶点或平面。您可以将这些输入坐标映射到映射到网格上的 2D 纹理。 在纹理的起点和终点之间画一条红线。 您将不得不深入研究如何将顶点输入坐标转换为纹理中的绝对 XY 坐标。 还要记住,最短的路径可能在网格的背面。根据纹理的映射方式,您可能需要绘制多于 1 条线。

【讨论】:

    【解决方案2】:

    从文档中可以很清楚地看到。您需要先致电build_sequence_tree我的建议是你在用户点击目的地之前把这个性能命中放在某个地方 - 这可能是在首先选择源时,所以当用户开始点击时感觉不到。如果您能找到一种在后台安全运行它的方法,那就更好了。

    2.1.3 构建内部序列树

    最短路径查询的耗时操作包括 构建用于进行查询的内部数据结构。这 数据结构称为序列树。它将被建造 当第一个最短路径查询完成时自动 只要源点集就可用于任何后续查询 不改变。每次更改源点集时 需要重建序列树(如果已经构建)。请注意,它可以 也可以通过调用手动构建 Surface_mesh_shortest_path::build_sequence_tree()。

    https://doc.cgal.org/latest/Surface_mesh_shortest_path/index.html

    另外,看起来算法在最坏情况多项式时间内运行。正如其他人所指出的,如果您知道您的问题在所有情况下都是凸的,那么它可能会被优化。

    【讨论】:

    • 不清楚这是否可能,请参阅上面的评论:我没有看到任何替代解决方案来避免重建数据结构,因为源点每次都在变化。
    • @FrederikDeRuyck 实际上很清楚,OP 写道:“通过点击两个不同的地方”
    • 我的意思是发帖人在 OP 评论中写道,源点每次都在变化。我认为这意味着每次都需要重建序列树,但不清楚,因为 OP 没有提到这一点。
    • @FrederikDeRuyck 但即便如此,关键是在选择源时可能会对性能造成影响 - 然后有效地减去用户选择目标所花费的时间。如果它可以在后台运行,那么他可能正在寻找一种解决方案,只有在用户真正快速点击时才会注意到它。
    猜你喜欢
    • 2020-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-03
    • 2020-06-25
    相关资源
    最近更新 更多