【问题标题】:std::vector not sorted as intended by std::sortstd::vector 未按 std::sort 预期排序
【发布时间】:2019-12-27 15:49:32
【问题描述】:

我想根据实数向量对整数向量进行排序。 例如。我有一个整数向量 (1,2,3) 和一个对应的实数向量 (0.3,0.2,0.1)。现在我想对整数进行排序,以便与最小实数相对应的整数排在第一位,与第二小的实数相对应的整数排在第二位,依此类推,即整数向量应该像 (3,2,1) 一样排序. 代码是:

int main()
{
    const unsigned int M = 5;
    const unsigned int N = 10;
    Eigen::MatrixXd samples = Eigen::MatrixXd::Random(M,N);
    std::vector<unsigned int> indices(N);
    std::iota(indices.begin(),indices.end(),0);
    std::shuffle(indices.begin(),indices.end(),std::default_random_engine(1.0));
    const unsigned int S = 4;
    std::vector<unsigned int> A(indices.begin(),indices.begin()+S);
    std::vector<unsigned int> B(indices.begin()+S,indices.end());

    std::default_random_engine generator;
    std::uniform_int_distribution<unsigned int> distribution(0,N-1);
    const unsigned int index = distribution(generator);

    std::vector<double> distances(A.size());
    for(unsigned int l=0; l<A.size(); ++l)
    {
        distances[l] = (samples.col(index)-samples.col(A[l])).norm();
    } 
    std::sort(A.begin(),A.end(),[&distances](unsigned int i, unsigned int j){return 
    distances[i]<distances[j];});

    distances.resize(B.size());
    for(unsigned int i=0; i<B.size(); ++i)
    {
        distances[i] = (samples.col(index)-samples.col(B[i])).norm();
    }
    std::cout << "distances:" << std::endl;
    for(unsigned int i=0; i<B.size(); ++i)
    {
        std::cout << distances[i] << " ";
    }
    std::cout << std::endl;
    std::cout << "indices before sorting:" << std::endl;
    for(unsigned int i=0; i<B.size(); ++i)
    {
        std::cout << B[i] << " ";
    }
    std::cout << std::endl;
    std::sort(B.begin(),B.end(),[&distances](unsigned int i, unsigned int j){return 
    distances[i]<distances[j];});
    std::cout << "indices after sorting:" << std::endl;
    for(unsigned int i=0; i<B.size(); ++i)
    { 
        std::cout << B[i] << " ";
    } 
    std::cout << std::endl;

    return 0;
}

输出是:

距离:

2.42122 0.940923 1.45279 1.81009 1.96321 1.76887

排序前的索引:

2 5 4 9 8 7

排序后的索引:

7 8 9 2 5 4

为什么输出不如下?

距离:

2.42122 0.940923 1.45279 1.81009 1.96321 1.76887

排序前的索引:

2 5 4 9 8 7

排序后的索引:

5 4 7 9 8 2

【问题讨论】:

  • 按照什么逻辑你期望排序后的索引是这样的?
  • 鉴于您描述的问题,您认为我们真的需要Eigen::MatrixXd&lt;random&gt; 中的所有内容吗?请构造一个minimal reproducible example,重点关注minimal部分。
  • @Fureeish:是的,代码可以缩短。
  • @Öö Tiib:我错误地认为 std::sort 中的整数 i 和 j 指的是向量 A 和 B 的索引,而不是它们的内容。

标签: c++ sorting


【解决方案1】:

您对不同容器的不同索引感到困惑。

根据您的代码,distance[0] 确实包含“索引”0 的距离,而是索引 B[0] 的距离,即索引 2。

尝试将其与您的排序功能一起使用:

auto sort_lambda = [&samples, &index] (unsigned int l, unsigned int r) {
  auto l_value = (samples.col(index)-samples.col(l)).norm();
  auto r_value = (samples.col(index)-samples.col(r)).norm();
  return l_value < r_value;
};

至于你有一个索引向量和一个值向量的一般情况:你可以创建一个对应关系图,并使用它从某个索引中获取值:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <unordered_map>
#include <vector>

int main()
{
    using vi = std::vector<int>;
    using vf = std::vector<float>;

    vi indices = {2, 4, 8, 1};
    vf values = {1.5, 0.5, 2.5, 4.5};

    std::unordered_map<int, float> correspondences;
    std::transform(begin(indices), end(indices), begin(values),
        std::inserter(correspondences, end(correspondences)),
        [](int i, float v) { return std::make_pair(i, v); });
    std::sort(begin(indices), end(indices),
        [&correspondences](int l, int r) {
            return correspondences.at(l) < correspondences.at(r);
        });
    // Note that now using values is basically impossible because it wasn't sorted as well ... you'd need to sort it now, too!
    std::copy(begin(indices), end(indices), std::ostream_iterator<int>(std::cout, ", "));
}

(ideone link)

如果索引和/或值具有更复杂的类型,那么您可能希望使用std::reference_wrapper 来避免在构建correspondences 映射时进行复制。

当您需要经常这样做时,将值和索引分开可能没有多大意义!


更一般地说,允许“待排序”向量的重复值:

#include <algorithm>
#include <functional>
#include <iterator>
#include <vector>



template<typename A, typename B>
void sort_according_to(std::vector<A> & to_sort, std::vector<B> const & ref)
{
    using pair_t = std::pair<A, std::reference_wrapper<const B>>;
    std::vector<pair_t> work;
    work.reserve(to_sort.size());
    std::transform(
        std::make_move_iterator(begin(to_sort)),
        std::make_move_iterator(end(to_sort)),
        begin(ref),
        std::back_inserter(work),
        [](A&& a, B const & b) {
            return std::make_pair(std::move(a), std::cref(b));
        });
    std::sort(
        begin(work), end(work),
        [](pair_t const & l, pair_t const & r) {
            return l.second.get() < r.second.get();
        });
    std::transform(
        begin(work), end(work),
        begin(to_sort),
        [](pair_t & p) {
            return std::move(p.first);
        });
}



#include <iostream>


int main()
{
    std::vector<int> ints = {1, 1, 2, 2, 3};
    std::vector<float> floats = {0.1, 1.1, 0.2, 2.2, -3.14};

    sort_according_to(ints, floats);

    std::copy(
        begin(ints), end(ints),
        std::ostream_iterator<int>(std::cout, ", "));

}

(它doesn't make copies but moves数据代替)

【讨论】:

  • 感谢您的详细解答。在第一个 std::transform 函数的最后一段代码摘录中,是否有必要同时应用 std::make_move_iterator 和 std::move?
  • 是的,我想是的。移动迭代器首先可以从向量元素移动,而移动确保std::make_paira 视为右值引用
猜你喜欢
  • 1970-01-01
  • 2011-10-29
  • 2021-10-28
  • 1970-01-01
  • 2020-09-07
  • 1970-01-01
  • 2010-09-19
  • 2012-05-26
  • 2010-12-11
相关资源
最近更新 更多