【问题标题】:How to get a sorted subvector out of a sorted vector, fast如何快速从已排序的向量中获取已排序的子向量
【发布时间】:2011-05-17 13:44:19
【问题描述】:

我有这样的数据结构:

struct X {
  float value;
  int id;
};

一个向量(大小N(想想100000),按排序(在程序执行期间保持不变):

std::vector<X> values;

现在,我想写一个函数

void subvector(std::vector<X> const& values, 
               std::vector<int> const& ids, 
               std::vector<X>& out /*, 
               helper data here */);

用传递的 ids 给出的 的排序子集填充 out 参数(大小 M N(大约是N的0.8倍),fast(内存不是问题,会重复做,所以建立lookuptables (函数参数中的 helper 数据)或仅执行一次的其他操作完全可以)。

到目前为止我的解决方案:
构建包含 id -> 在 values 中的偏移量的可查找表 lut(准备,所以运行时恒定)
创建std::vector&lt;X&gt; tmp,大小为N,填充无效的ID(线性N
对于每个 id,将 values[lut[id]] 复制到 tmp[lut[id]]M 中的线性)
循环tmp,将项目复制到out(线性N

这在 N 中是线性的(因为它比 M 大),但是临时变量和重复复制让我很烦。有没有比这更快的方法?请注意,M 将接近 N,因此 O(M log N) 的事情是不利的。

编辑:http://ideone.com/xR8Vp 是上述算法的示例实现,以使所需的输出清晰并证明它在线性时间内是可行的 - 问题是关于避免临时变量或以其他方式加速它的可能性,非线性的东西不会更快:)。

【问题讨论】:

  • tmp 的目的是什么?它最初是从哪里来的?为什么不直接在 out 中构建输出而不使用任何中间临时对象?
  • 另外,您要构建的内容在您的问题中没有得到很好的描述。最初,您似乎说您需要大小为M 的输出。然而,您的算法在所有情况下都尝试构建大小为 N 的输出。那么,在完成之后,你想在out 数组中得到什么?
  • 关于“tmp 来自哪里” - 我创建了它。关于“为什么我不直接在out 中构建它” - 我不知道事先将元素放在哪里,我不知道子向量中的位置。不,我的输出是大小M,它在 N 中只是线性的,因为我测试了 tmp 中的每个元素。是的,id 值是唯一的。
  • 第二个向量按id 排序并使用equal_rangecopy 和最后sort 按值应该给你M log N 复杂度。
  • 这是我错过的一件事 - M 将非常接近 N,所以这将是不利的(对于非常稀疏的 id,这将是有利的)

标签: c++ sorting vector large-data


【解决方案1】:

您可以尝试的另一种方法是使用哈希表而不是向量来查找 id:

void subvector(std::vector<X> const& values, 
               std::unordered_set<int> const& ids, 
               std::vector<X>& out) {

    out.clear();
    out.reserve(ids.size());
    for(std::vector<X>::const_iterator i = values.begin(); i != values.end(); ++i) {
        if(ids.find(i->id) != ids.end()) {
            out.push_back(*i);
        }
    }
}

这是线性时间,因为unordered_set::find 是恒定的预期时间(假设我们在散列整数时没有问题)。但是我怀疑它在实践中可能不如您最初使用向量描述的方法那么快。

【讨论】:

  • 谢谢,这看起来很有趣。将针对矢量版本进行基准测试。
【解决方案2】:

由于您的向量已排序,并且您希望它的子集以相同的方式排序,我假设我们可以直接切出您想要的块而不重新排列它。

为什么不直接使用 find_if() 两次。一次找到您想要的范围的开始,一次找到范围的结束。这将为您提供子向量的开始和结束迭代器。使用这些迭代器构造一个新向量。向量constructor 重载之一需要两个迭代器。

那个或partition 算法应该可以工作。

【讨论】:

  • 不确定这是否可行。如果我正确阅读了问题,则 OP 将数组按 value 排序并希望按 id 进行选择。
  • 是的,并且 id 不是连续的(并且没有必要排序)。
【解决方案3】:

如果我正确理解了您的问题,您实际上会尝试创建一个线性时间排序算法(取决于数字 M 的输入大小)。 这是不可能的。

您当前的方法是对可能的值进行排序。 这需要线性时间到可能值 N 的数量(理论上,考虑到地图搜索需要 O(1) 时间)。

您可以做的最好的事情是使用快速排序方法(O(MlogM) fe quicksort, mergesort 等)对 M 的小值进行排序(您从地图中找到),并可能对较大的值进行线性搜索M 的值。 例如,如果 N 为 100000,M 为 100,则仅使用排序算法会快得多。

我希望你能明白我说的话。如果您仍有问题,我会尽力回答:)

编辑:(评论) 我将进一步解释我的意思。 假设您知道您的数字范围为 1 到 100。 您将它们排序在某个地方(实际上它们是“自然”排序的),并且您希望以排序形式获得它们的子集。 如果有可能比 O(N) 或 O(MlogM) 更快,排序算法将只使用这种方法进行排序。

F.e.通过拥有一组数字 {5,10,3,8,9,1,7},知道它们是已排序的一组数字 {1,2,3,4,5,6,7,8 ,9,10} 您仍然无法比 O(N) (N = 10) 或 O(MlogM) (M = 7) 更快地对它们进行排序。

【讨论】:

  • 不,我不想创建线性排序时间算法 - 我想从已经排序的向量中获取值,因此不需要进行排序。有关我在 OP 中概述的算法的示例实现,请参阅 ideone.com/SNHVq
猜你喜欢
  • 1970-01-01
  • 2018-09-09
  • 2012-02-19
  • 2018-05-03
  • 1970-01-01
  • 2022-08-14
  • 2013-03-28
  • 1970-01-01
相关资源
最近更新 更多