【问题标题】:Concatenate 2 STL vectors in constant O(1) time以恒定的 O(1) 时间连接 2 个 STL 向量
【发布时间】:2013-07-25 15:32:25
【问题描述】:

我会给出一些关于我为什么要这样做的背景,但最终可以忽略背景,因为它主要是一个经典的计算机科学和 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 存储库中提交了一个问题,以便与库作者就以下建议进行讨论。一旦确定了采用哪种方法,我将接受相关建议作为答案。

【问题讨论】:

  • 据我所知这是不可能的。为什么不将向量保留到您喜欢的大容量?
  • 我知道 C++、C 和 C#。但是什么是 CS?
  • 我认为 OP 是指计算机科学。
  • @user1475135:向量很少调整大小,即使使用+=。如果它实际上是std::vector,那么别担心,这不是你的问题。
  • 有可能简单地通过在创建时推回N点然后将它们全部删除来以非侵入方式保留内存。

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


【解决方案1】:

向量不是列表,它代表一个序列,但附加要求元素必须存储在连续的内存中。您不能在不移动对象的情况下将两个向量(其缓冲区不连续)捆绑成一个向量。

【讨论】:

    【解决方案2】:

    这个问题之前已经解决过很多次了,比如使用 String Rope 类。

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

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

    【讨论】:

      【解决方案3】:

      没有与 splice 等效的向量 - 不存在,特别是因为内存布局要求,这可能是最初选择它的原因。

      也没有固定时间的方法来连接向量。

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

      还有另一种方法可以使看起来像的东西成为一个串联的向量,那就是使用一个像双端队列一样工作的包装容器,并在它们之上提供一个统一的迭代器和operator[]。不过,我不知道点云库是否足够灵活。 (Jamin 的建议本质上是使用类似 而不是 的向量,而 Zan 的建议大致是我的想法)。

      【讨论】:

        【解决方案4】:

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

        但是!如果您在元素类型中实现移动语义,您可能会获得显着的速度提升,具体取决于您的元素包含的内容。如果您的元素不包含任何重要的类型,这将无济于事。 此外,如果您提前准备好矢量保留所需的内存,那么这也有助于加快速度,因为不需要调整大小(这会导致不希望的巨大新分配,可能必须以该内存大小进行碎片整理,然后一个巨大的内存)。

        除此之外,您可能希望在链接列表和向量之间创建某种混合,列表的每个“元素”都是具有 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.
        

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

            【解决方案6】:

            http://www.boost.org/doc/libs/1_54_0/libs/range/doc/html/range/reference/utilities/join.html查看 Boost range 联合

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

            你应该会写

            auto combined = join(vector1,vector2).
            

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

            【讨论】:

              【解决方案7】:

              矢量永远没有 O(1) 副本,但是,您应该检查:

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

              我所看到的是,虽然 memcpy 和 assignment-for-loop 都具有 O(n) 复杂度,但利用 memcpy 的解决方案可以快得多。

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2013-11-04
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2014-11-14
                • 2016-11-13
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多