【问题标题】:Improve searching through unsorted list通过未排序的列表改进搜索
【发布时间】:2017-03-16 14:33:43
【问题描述】:

我的代码花费了 40% 的时间来搜索未排序的向量。更具体地说,搜索函数my_search 重复接收长度为N 的单个未排序向量,其中N 可以取10 到100,000 之间的任何值。与每个元素相关的权重变化相对较小(例如 [0.8, 0.81, 0.85, 0.78, 0.8, 0.7, 0.84, 0.82, ...])。

算法my_search 首先将每个对象的所有权重相加,然后对N 元素(与向量的长度一样多)进行平均采样并替换。该算法与

非常相似
int sum_of_weight = 0;
for(int i=0; i<num_choices; i++) {
   sum_of_weight += choice_weight[i];
}
int rnd = random(sum_of_weight);
for(int i=0; i<num_choices; i++) {
  if(rnd < choice_weight[i])
    return i;
  rnd -= choice_weight[i];
}

来自this post

我可以在搜索之前对向量进行排序,但需要花费 O(N log N) 的时间(取决于使用的排序算法),我怀疑(但可能是错误的,因为我没有尝试过)我会获得更多时间,尤其是在权重变化不大的情况下。

另一种解决方案是存储一系列点之前有多少重量的信息。例如,在对向量求和时,每 N/10 个元素,我可以存储已经求和了多少权重的信息。然后,我可以先将rnd 与这 10 个断点进行比较,然后只在向量总长度的十分之一中进行搜索。

  • 这是一个好的解决方案吗?
  • 我描述的过程有名称吗?
  • 如何根据N 估算要存储的正确断点数?
  • 有更好的解决方案吗?

【问题讨论】:

  • 如果重复搜索同一个列表,排序命中的 N log (N) 会越来越有吸引力。假设您排序一次并缓存。
  • @user4581301 OP 说他反复收到未排序的向量
  • @smac89 OP 没有说明未排序的向量是否重复。 OP 有两种选择:强加顺序或线性搜索。
  • 更好的解决方案是创建第二个长度为 N 的数组,并将每个部分和存储在该数组中。然后,您可以使用二分查找来查找部分总和超过rnd 的索引。实际上,如果您只关心索引,那么您甚至不需要第二个数组。只需修改输入数组,使其包含部分和。
  • choice_weight 的元素是什么类型的?如果是 double/float,则应将 sum_of_weight 定义为相同类型(但不是整数)。

标签: c++ algorithm sorting search


【解决方案1】:

log(N)解决方案

{
    std::vector<double> sums;
    double sum_of_weight = 0;
    for(int i=0; i<num_choices; i++) {
       sum_of_weight += choice_weight[i];
       sums.push_back(sum_of_weight);
    }

    std::vector<double>::iterator high = std::upper_bound(sums.begin(), sums.end(), random(sum_of_weight));

    return std::distance(sums.begin(), high);
}

基本上与您对解决问题的更好方法的想法相同,但不是仅存储十分之一的元素,而是存储所有元素并使用二分搜索查找最接近您的值的元素的索引。


分析

即使这个解决方案是O(logN),你真的必须问问自己是否值得。是否值得创建一个额外的向量,从而累积额外​​的时钟周期来存储向量中的内容、向量调整大小所需的时间、调用函数执行二进制搜索所需的时间等?

当我在写上面的内容时,我意识到你可以使用 deque 来代替,这几乎可以消除因必须调整大小和复制向量内容而对性能造成的影响,而不会影响 O(1) 查找向量。

所以我想问题仍然存在,是否值得将元素复制到另一个容器中,然后只进行 O(logN) 搜索?

结论

TBH,我认为您从这次优化中收获不大。事实上,我认为您获得了 O(logN) 的开销。

【讨论】:

  • 我有点不清楚 if 语句的作用。你能详细说明一下吗?当high指向向量sums的第一个元素时,那么索引std::distance(sums.begin(), high) - 1不应该是-1吗?谢谢!
  • 好的,我看到了修复负索引问题的编辑。我仍然不太明白这个 if 语句的必要性。我觉得 upper_bound 返回了一个迭代器,指向的值刚好高于绘制的随机值,这正是我们正在寻找的,不是吗?
  • 谢谢:)。我可以修改其余代码以一直使用选择权重的累积总和,而无需太多计算时间成本。因此,您的解决方案最终在性能上产生了相当大的差异。谢谢!
  • @Remi.b 没问题。在分析中,我试图让您注意早期优化的一些危险,但我很高兴您找到了一种方法,让这实际上值得付出努力
  • 我突然有点糊涂了。 upper_bound 是在进行二分搜索吗?它似乎没有,需要实现它(参见here)。但是在修改代码后我确实看到了更好的性能,这让我感到困惑。再次感谢
猜你喜欢
  • 2015-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多