在恒定的O(1)时间内连接2个STL向量

时间:2013-07-25 16:01:07

标签: c++ stl point-cloud-library

我将给出一些关于我为什么要这样做的背景信息,但最终可以忽略上下文,因为它主要是一个经典的计算机科学和C ++问题(以前肯定会被问到,但是一对粗略搜索没有发现任何东西......)

我正在使用(大型)实时流点云,并且我需要从多个传感器中获取2/3/4点云并将它们粘在一起以创建一个大点云。我实际上需要一个结构中的所有数据,而通常当人们只是可视化点云时,他们可以将它们分别送入查看器中。

我正在使用Point Cloud Library 1.6,仔细观察它的PointCloud class(如果您感兴趣,请在<pcl/point_cloud.h>下)将所有数据点存储在STL向量中。

现在我们又回到了香草CS的土地......

PointCloud有一个+ =运算符,用于将一个点云的内容添加到另一个点云。到现在为止还挺好。但是这种方法效率很低 - 如果我理解正确的话,它1)调整目标矢量的大小,然后2)运行另一个矢量中的所有点,然后复制它们。

这在我看来就像O(n)时间复杂度的情况,通常可能不会太糟糕,但在每个云实时处理至少300K点时是个坏消息。

向量不需要进行排序或分析,它们只需要在内存级别“粘在一起”,因此程序知道一旦它到达第一个向量的末尾,它就必须跳转到开始第二个的位置。换句话说,我正在寻找O(1)向量合并方法。在STL中有没有办法做到这一点?或者它更像是std :: list#splice?

之类的领域

注意:这个课程是PCL的一个非常基础的部分,所以'非侵入性手术'更可取。如果需要对类本身进行更改(例如,从向量更改为列表或保留内存),则必须根据对PCL其余部分的影响进行考虑,这可能是影响深远。

更新:我已经在PCL的GitHub回购中提出了一个问题,以便与图书馆作者就下面的建议进行讨论。一旦有某种解决办法,我会接受相关的建议作为答案。

7 个答案:

答案 0 :(得分:8)

向量是一个列表,它表示一个序列,但附加要求元素必须存储在连续的内存中。你不能只将捆绑两个向量(其缓冲区不会是连续的)放入一个向量中而不移动对象。

答案 1 :(得分:6)

此问题在使用String Rope类之前已经多次解决。

基本方法是创建一个存储指向点云的指针的新容器类型。这就像std :: deque,除了你的将拥有可变大小的块。除非你的云块成为标准尺寸?

使用这个新容器,迭代器从第一个块开始,继续到最后,然后进入下一个块。在具有可变大小的块的这种容器中进行随机访问需要二进制搜索。实际上,这样的数据结构可以写成B +树的扭曲形式。

答案 2 :(得分:5)

没有矢量等效的拼接 - 没有,特别是因为内存布局要求,这可能是它首先被选中的原因。

也没有连接矢量的常时方法。

我可以想到一种(脆弱的)方法在常量时间内连接原始数组,但这取决于它们在开始和结束时在页面边界上对齐,然后重新映射他们是相邻的。这很难概括。

还有另一种方法可以使看起来像一个连接的向量,并且使用一个像deque一样工作的包装器容器,并为它们提供统一的迭代器和operator[]。我不知道点云库是否足够灵活,可以使用它。 (Jamin的建议主要是使用像这样的东西而不是向量,而Zan大概是我想到的。)

答案 3 :(得分:3)

不,你不能通过一个简单的链接连接两个向量,你实际上必须复制它们。

然而!如果在元素类型中实现move-semantics,则可能会获得显着的速度提升,具体取决于元素包含的内容。如果您的元素不包含任何非平凡类型,这将无济于事。 此外,如果你事先有你的向量预留方式所需的内存,那么这也有助于加快速度,不需要调整大小(这会导致不需要的大量新分配,可能需要对内存大小进行碎片整理,然后一个巨大的memcpy)。

除此之外,您可能希望在链表和向量之间创建某种混合,列表的每个'元素'都是具有10k元素的向量,因此您只需要每10k元素跳转一次列表链接,但它可以让你动态地变得更容易,并使你的串联变得轻而易举。

std::list<std::vector<element>> forIllustrationOnly; //Just roll your own custom type.

index = 52403;

listIndex = index % 1000
vectorIndex = index / 1000

forIllustrationOnly[listIndex][vectorIndex] = still fairly fast lookups
forIllustrationOnly[listIndex].push_back(vector-of-points) = much faster appending and removing of blocks of points.

答案 4 :(得分:2)

使用矢量不会获得此缩放行为,因为使用矢量时,您无法绕过复制。并且您无法在固定时间内复制任意数量的数据。

我不知道PointCloud,但是如果你可以使用其他列表类型,例如一个链表,这种行为很有可能。您可能会发现链接列表实现在您的环境中有效,并且可以将第二个列表简单地粘贴到第一个列表的末尾,如您所想。

答案 5 :(得分:1)

http://www.boost.org/doc/libs/1_54_0/libs/range/doc/html/range/reference/utilities/join.html

处查看Boost范围关节

这将需要2个范围并加入它们。假设你有vector1和vector 2。

你应该可以写

auto combined = join(vector1,vector2).

然后您可以根据需要与算法等结合使用。

答案 6 :(得分:0)

没有O(1)副本的矢量,,你应该检查:

  • 元素类型是否可以轻易复制? (又名memcpy
  • Iff,是我的vector实现利用这个事实,还是愚蠢地循环遍历所有300k元素,为每个元素执行一个简单的赋值(或者更糟糕的是copy-ctor-call)?

我所看到的是,虽然memcpy和循环赋值都具有O(n)复杂度,但利用memcpy的解决方案可以更快,更快。

所以,问题可能是因为矢量实现对于普通类型来说是次优的。