Devrais-je utiliser
std::sort(numbers.begin(), numbers.end(), std::greater<int>());
ou
std::sort(numbers.rbegin(), numbers.rend()); // note: reverse iterators
trier un vecteur par ordre décroissant? Y a-t-il des avantages ou des inconvénients avec l'une ou l'autre approche?
En fait, la première est une mauvaise idée. Utilisez soit le second one, soit ceci:
struct greater
{
template<class T>
bool operator()(T const &a, T const &b) const { return a > b; }
};
std::sort(numbers.begin(), numbers.end(), greater());
Ainsi, votre code ne se cassera pas silencieusement quand quelqu'un décidera numbers
devrait contenir long
ou long long
au lieu de int
.
Utilisez le premier:
std::sort(numbers.begin(), numbers.end(), std::greater<int>());
C'est explicite de ce qui se passe - moins de risque de mauvaise lecture rbegin
comme begin
, même avec un commentaire. C'est clair et lisible et c'est exactement ce que vous voulez.
En outre, le second peut être moins efficace que le premier compte tenu de la nature des itérateurs inversés, bien que vous deviez le profiler pour en être sûr.
Avec c ++ 14, vous pouvez faire ceci:
std::sort(numbers.begin(), numbers.end(), std::greater<>());
Et ça?
std::sort(numbers.begin(), numbers.end());
std::reverse(numbers.begin(), numbers.end());
Au lieu d'un foncteur proposé par Mehrdad, vous pourriez utiliser une fonction Lambda.
sort(numbers.begin(), numbers.end(), [](const int a, const int b) {return a > b; });
Selon ma machine, trier un vecteur long long
de [1..3000000] à l’aide de la première méthode prend environ 4 secondes, tandis que l’utilisation de la seconde prend environ deux fois plus de temps. Cela dit quelque chose, évidemment, mais je ne comprends pas pourquoi non plus. Pensez simplement que cela serait utile.
Même chose rapportée ici .
Comme l'a dit Xeo, avec -O3
, ils utilisent à peu près le même temps pour terminer.
La première approche concerne:
std::sort(numbers.begin(), numbers.end(), std::greater<>());
Vous pouvez utiliser la première approche pour obtenir plus d'efficacité que la seconde.
La complexité temporelle de la première approche est inférieure à celle de la seconde.
L'approche la plus courte est:
std::sort(v.rbegin(), v.rend());
bool comp(int i, int j) { return i > j; }
sort(numbers.begin(), numbers.end(), comp);
Je ne pense pas que vous devriez utiliser l'une des méthodes de la question car elles prêtent à confusion, et la seconde est fragile, comme le suggère Mehrdad.
Je préconise les éléments suivants, car ils ressemblent à une fonction de bibliothèque standard et expliquent clairement son intention:
#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>());
}
Vous pouvez utiliser le premier ou essayer le code ci-dessous qui est tout aussi efficace
sort(&a[0], &a[n], greater<int>());
Utilisez tout. Ils sont presque les mêmes.
Comme d'habitude, il y a des avantages et des inconvénients.
Utilisez std::reverse_iterator
:
operator>()
std::greater<int>()
Utilisez std::greater
lorsque:
En ce qui concerne les performances, les deux méthodes sont tout aussi efficaces. J'ai essayé le repère suivant:
#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;
}
Avec cette ligne de commande:
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
Les horaires sont les mêmes. Valgrind signale le même nombre de manquements dans le cache.