使用boost而不递归来遍历Voronoi图的边缘有什么好算法?
我知道它必须检查单元格中的无限边,然后检查它的邻居并从那里重复,但我更喜欢一种不需要递归的方法,因为我正在处理大量数据。
这可能没有递归吗?
修改,以获得更多说明:
这是获取所有边缘单元格的一种方法:
voronoi_diagram vd;
boost::polygon::construct_voronoi(in.begin(), in.end(), &vd);
std::vector<const voronoi_diagram::cell_type *> edge_cells;
for(const voronoi_diagram::edge_type & e : vd.edges())
if (e.is_infinite())
edge_cells.push_back(e.cell());
上述方法的问题在于它不会以任何特定顺序遍历边缘单元格,例如顺时针方向。
递归实现会执行类似于此(仓促编写和未经测试)的代码:
bool findNext(const voronoi_diagram::cell_type * c,
std::list<const voronoi_diagram::cell_type *> & list)
{
const voronoi_diagram::edge_type * e = c->incident_edge();
do
{
// Follow infinite edges, adding cells encountered along the way
if (e->is_infinite() && e->twin()->cell() != list.front() &&
e->twin()->cell() != list.back())
{
list.push_back(c);
return findNext(e->twin()->cell(), list);
}
else if (e->twin()->cell() == list.front())
{
list.push_back(c);
return true; // we reached the starting point, return
}
e = e->next();
} while (e != c->incident_edge());
return false;
}
// ...
std::list<const voronoi_diagram::cell_type *> edge_cells;
// ...
for(const voronoi_diagram::edge_type & e : vd.edges())
{
// find first infinite edge
if (e.is_infinite())
{
if (findNext(e.cell(), edge_cells))
break;
else
edge_cells.clear();
}
}
这将遍历Voronoi图的边缘,直到它追溯到第一个单元格,然后停止,一直填充堆栈。
非递归实现会为第二个示例建模,以顺时针或逆时针顺序生成边缘单元格列表,而不使用递归。
答案 0 :(得分:3)
您在findNext
中只有一次递归调用且return findNext(...)
,因此可以应用所谓的tail-call优化。您的编译器可能在-O3处执行此操作。但如果你不相信编译器这样做,你可以手工完成。下面是转换后的函数,不再递归:
bool findNext(const voronoi_diagram::cell_type * c,
std::list<voronoi_diagram::cell_type *> & list)
{
const voronoi_diagram::edge_type * e = c->incident_edge();
bool justCalled; // true when we repalce the tail call
do
{
justCalled = false;
// Follow infinite edges, adding cells encountered along the way
if (e->is_infinite() && e->twin()->cell() != list.front() &&
e->twin()->cell() != list.back())
{
list.push_back(c);
c = e->twin()->cell(); // reassigns function argument
e = c->incident_edge(); // replay the initiaization (before do loop)
justCalled = true; // force the loop to continue
continue; // jump to start of loop
// everything happens as if we called findNext(e->twin()->cell(), list);
else if (e->twin()->cell() == list.front())
{
list.push_back(c);
return true; // we reached the starting point, return
}
e = e->next();
} while (justCalled || e != c->incident_edge());
return false;
}
此函数与您编写的函数等效,因此您使用的函数相同,并且您确定不会涉及递归。 bool
标志是必需的,因为continue
跳转到循环的测试而不是它的主体see here,因此当我们更改参数并调用continue时循环甚至开始之前测试可能会失败。
这是一种通用技术,并非特定于图遍历,而是针对所有递归算法。当然,如果你有很多函数参数和大量的代码,那么转换很繁重,但在这种情况下我认为这是一个很好的匹配。
在更复杂的情况下,当递归不是尾调用时,您仍然可以通过维护自己的堆栈来“解除重新定义”任何函数。这样做的好处是,用优先级fifo替换堆栈结构可能会以递归可以(轻松)实现的更微妙的方式改变遍历顺序。