获得向量<float> </float>中k个最高值的索引的有效方法

时间:2014-01-09 15:03:53

标签: c++ time-complexity

如何从std::map<int, float>创建vector<float>,以便地图包含来自向量的k个最高值,其中键是向量中的值的索引。

一种天真的方法是遍历向量(O(n)),提取和擦除(O(n))最高元素k次(O(k)),导致O(k * n)的复杂性^ 2),我认为这是次优的。

更好的是复制(O(n))并删除最小值,直到大小为k。这将导致O(n ^ 2)。还是多项式......

有什么想法吗?

4 个答案:

答案 0 :(得分:2)

以下应该做的工作:

#include <cstdint>
#include <algorithm>
#include <iostream>
#include <map>
#include <tuple>
#include <vector>

// Compare: greater T2 first.
struct greater_by_second
{
    template <typename T1, typename T2>
    bool operator () (const std::pair<T1, T2>& lhs, const std::pair<T1, T2>& rhs)
    {
        return std::tie(lhs.second, lhs.first) > std::tie(rhs.second, rhs.first);
    }
};


std::map<std::size_t, float> get_index_pairs(const std::vector<float>& v, int k)
{
    std::vector<std::pair<std::size_t, float>> indexed_floats;

    indexed_floats.reserve(v.size());
    for (std::size_t i = 0, size = v.size(); i != size; ++i) {
        indexed_floats.emplace_back(i, v[i]);
    }
    std::nth_element(indexed_floats.begin(),
                     indexed_floats.begin() + k,
                     indexed_floats.end(), greater_by_second());
    return std::map<std::size_t, float>(indexed_floats.begin(), indexed_floats.begin() + k);
}

让我们测试一下:

int main(int argc, char *argv[])
{
    const std::vector<float> fs {45.67f, 12.34f, 67.8f, 4.2f, 123.4f};

    for (const auto& elem : get_index_pairs(fs, 2)) {
        std::cout << elem.first << " " << elem.second << std::endl;
    }
    return 0;
}

输出:

2 67.8
4 123.4

答案 1 :(得分:1)

您可以保留到目前为止k个最高值的列表,并为向量中的每个值更新它,这会将您降低到O(n * log k)(假设每次更新的日志k为最高值列表)或者,对于天真的列表,O(kn​​)。

你可能接近O(n),但假设k可能很小,可能不值得努力。

答案 2 :(得分:0)

也许我没有得到它,但如果增量方法不是一个选项,为什么不使用 std::sort std::partial_sort

那应该是o(n log k),并且因为k很可能很小,所以实际上是o(n)。

编辑:感谢Mike Seymour的更新。 编辑(之二):

这个想法是使用中间向量进行排序,然后将其放入地图中。试图减少计算的顺序只能用于大量数据,所以我猜复制时间(在o(n)中)可能会在背景噪声中丢失。

编辑(之二):

这实际上是所选答案所做的,没有理论解释:)。

答案 3 :(得分:0)

您的最佳解决方案将具有 O(n + k * log(k))的复杂度,因为对k元素进行排序可以减少到此,并且您将不得不查看每个元素至少一次。

我想到了两种可能的解决方案:

  1. 迭代向量,同时将所有元素添加到有界(大小k)优先级队列/堆中,同时保留其索引。

  2. 使用包含原始索引(即std::vector<std::pair<float, std::size_t>>)创建矢量副本,并使用std::nth_element使用仅比较第一个元素的比较器将k个最高值移动到前面。然后将这些元素插入到目标地图中。具有讽刺意味的是,最后一步在总体复杂性中增加了k * log(k),而nth_element是线性的(但会置换你的指数)。