web-dev-qa-db-ja.com

ベクトルを降順に並べ替える

私は使うべきです

std::sort(numbers.begin(), numbers.end(), std::greater<int>());

または

std::sort(numbers.rbegin(), numbers.rend());   // note: reverse iterators

ベクトルを降順に並べ替えるには?どちらかの方法に利点や欠点はありますか?

285
fredoverflow

実際には、最初のものは悪い考えです。 2番目のものを使うか、これを使います:

struct greater
{
    template<class T>
    bool operator()(T const &a, T const &b) const { return a > b; }
};

std::sort(numbers.begin(), numbers.end(), greater());

こうすることで、誰かがnumberslongではなくintまたはlong longを保持すべきであると誰かが決めたときにあなたのコードが静かに壊れることはありません。

106
Mehrdad

最初のものを使う:

std::sort(numbers.begin(), numbers.end(), std::greater<int>());

何が起こっているのかはっきりしています - コメントがあってもrbeginbeginと誤解する可能性が少なくなります。それはあなたが望んでいるものと全く同じであることは明確で読みやすいです。

また、2番目のものは逆イテレータの性質を考えると最初のものよりも効率が悪いかもしれませんが、確実にプロファイルする必要があります。

62
Pubby

C++ 14ではこれを行うことができます。

std::sort(numbers.begin(), numbers.end(), std::greater<>());
52
mrexciting

これはどうですか?

std::sort(numbers.begin(), numbers.end());
std::reverse(numbers.begin(), numbers.end());
25
shoumikhin

Mehrdadが提案したようなファンクターの代わりに、Lambda関数を使用することができます。

sort(numbers.begin(), numbers.end(), [](const int a, const int b) {return a > b; });
20
Julian Declercq

私のマシンによると、最初の方法で[1..3000000]のlong longベクトルをソートするのに約4秒かかりますが、2番目の方法では約2倍の時間がかかります。それは明らかに何かを言います、しかし私も理由がわかりません。これが役に立つと思います。

ここでも同じことが報告されています。

Xeoが言ったように、-O3を使用すると、ほぼ同じ時間で終了します。

15
Ziyao Wei

最初の方法は次のとおりです。

    std::sort(numbers.begin(), numbers.end(), std::greater<>());

2番目の方法よりも効率がよくなるため、最初の方法を使用することができます。
最初のアプローチは、2番目のアプローチよりも時間の複雑さが少なくなります。

10
rashedcs

最短のアプローチは:

std::sort(v.rbegin(), v.rend());
8
Alexey
bool comp(int i, int j) { return i > j; }
sort(numbers.begin(), numbers.end(), comp);
5
user7069426

私はあなたが質問の両方の方法を混乱させているのであなたが質問のどちらかの方法を使うべきではないと思います、そしてMehrdadが示唆するように2番目の方法は壊れやすいです。

標準ライブラリ関数のように見え、その意図を明確にしているので、私は次のことを主張します。

#include <iterator>

template <class RandomIt>
void reverse_sort(RandomIt first, RandomIt last)
{
    std::sort(first, last, 
        std::greater<typename std::iterator_traits<RandomIt>::value_type>());
}
1

最初のものを使うか、以下のコードを試してみてください。

sort(a, a+n, greater<int>());
1
Krish Munot

TL; DR

任意を使用します。それらはほとんど同じです。

退屈な答え

いつものように、長所と短所があります。

std::reverse_iteratorを使用:

  • カスタムタイプをソートしていて、operator>()を実装したくない場合
  • std::greater<int>()を入力するのが面倒なとき

std::greaterは次の場合に使用します。

  • より明示的なコードが必要な場合
  • あいまいな逆反復子の使用を避けたい場合

パフォーマンスに関しては、どちらの方法も同等に効率的です。私は次のベンチマークを試しました:

#include <algorithm>
#include <chrono>
#include <iostream>
#include <fstream>
#include <vector>

using namespace std::chrono;

/* 64 Megabytes. */
#define VECTOR_SIZE (((1 << 20) * 64) / sizeof(int))
/* Number of elements to sort. */
#define SORT_SIZE 100000

int main(int argc, char **argv) {
    std::vector<int> vec;
    vec.resize(VECTOR_SIZE);

    /* We generate more data here, so the first SORT_SIZE elements are evicted
       from the cache. */
    std::ifstream urandom("/dev/urandom", std::ios::in | std::ifstream::binary);
    urandom.read((char*)vec.data(), vec.size() * sizeof(int));
    urandom.close();

    auto start = steady_clock::now();
#if USE_REVERSE_ITER
    auto it_rbegin = vec.rend() - SORT_SIZE;
    std::sort(it_rbegin, vec.rend());
#else
    auto it_end = vec.begin() + SORT_SIZE;
    std::sort(vec.begin(), it_end, std::greater<int>());
#endif
    auto stop = steady_clock::now();

    std::cout << "Sorting time: "
          << duration_cast<microseconds>(stop - start).count()
          << "us" << std::endl;
    return 0;
}

このコマンドラインで:

g++ -g -DUSE_REVERSE_ITER=0 -std=c++11 -O3 main.cpp \
    && valgrind --cachegrind-out-file=cachegrind.out --tool=cachegrind ./a.out \
    && cg_annotate cachegrind.out
g++ -g -DUSE_REVERSE_ITER=1 -std=c++11 -O3 main.cpp \
    && valgrind --cachegrind-out-file=cachegrind.out --tool=cachegrind ./a.out \
    && cg_annotate cachegrind.out

std::greater demostd::reverse_iterator demo

タイミングは同じです。 Valgrindは、同じ数のキャッシュミスを報告します。

0
ivaigult