En utilisant C++ et, espérons-le, la bibliothèque standard, je souhaite trier une séquence d'échantillons par ordre croissant, mais je souhaite également garder en mémoire les index d'origine des nouveaux échantillons.
Par exemple, j'ai un ensemble, un vecteur ou une matrice d'échantillons A : [5, 2, 1, 4, 3]
. Je veux trier ces pour être B : [1,2,3,4,5]
, mais je veux aussi me rappeler les index originaux des valeurs, afin que je puisse obtenir un autre ensemble qui serait: C : [2, 1, 4, 3, 0 ]
- qui correspond à l'index de chaque élément dans 'B', dans l'original 'A'.
Par exemple, dans Matlab, vous pouvez faire:
[a,b]=sort([5, 8, 7])
a = 5 7 8
b = 1 3 2
Quelqu'un peut-il voir un bon moyen de faire cela?
Utilisation de lambdas C++ 11
#include <iostream>
#include <vector>
#include <numeric> // std::iota
#include <algorithm> // std::sort
template <typename T>
vector<size_t> sort_indexes(const vector<T> &v) {
// initialize original index locations
vector<size_t> idx(v.size());
iota(idx.begin(), idx.end(), 0);
// sort indexes based on comparing values in v
sort(idx.begin(), idx.end(),
[&v](size_t i1, size_t i2) {return v[i1] < v[i2];});
return idx;
}
Vous pouvez maintenant utiliser le vecteur d’index renvoyé dans des itérations telles que
for (auto i: sort_indexes(v)) {
cout << v[i] << endl;
}
De toute évidence, vous pouvez également choisir de fournir votre propre vecteur d'index, votre fonction de tri, votre comparateur ou de réorganiser automatiquement v dans la fonction sort_indexes à l'aide d'un vecteur supplémentaire.
Vous pouvez trier std :: pair au lieu de simplement ints - first int correspond aux données d'origine, second int à l'index d'origine. Ensuite, fournissez un comparateur qui effectue uniquement le tri sur le premier int. Exemple:
Your problem instance: v = [5 7 8]
New problem instance: v_prime = [<5,0>, <8,1>, <7,2>]
Triez la nouvelle instance à l'aide d'un comparateur tel que:
typedef std::pair<int,int> mypair;
bool comparator ( const mypair& l, const mypair& r)
{ return l.first < r.first; }
// forgetting the syntax here but intent is clear enough
Le résultat de std :: sort sur v_prime, à l'aide de ce comparateur, devrait être:
v_prime = [<5,0>, <7,2>, <8,1>]
Vous pouvez extraire les index en parcourant le vecteur, en récupérant .second de chaque paire std ::.
J'ai écrit la version générique de la sorte d'index.
template <class RAIter, class Compare>
void argsort(RAIter iterBegin, RAIter iterEnd, Compare comp,
std::vector<size_t>& indexes) {
std::vector< std::pair<size_t,RAIter> > pv ;
pv.reserve(iterEnd - iterBegin) ;
RAIter iter ;
size_t k ;
for (iter = iterBegin, k = 0 ; iter != iterEnd ; iter++, k++) {
pv.Push_back( std::pair<int,RAIter>(k,iter) ) ;
}
std::sort(pv.begin(), pv.end(),
[&comp](const std::pair<size_t,RAIter>& a, const std::pair<size_t,RAIter>& b) -> bool
{ return comp(*a.second, *b.second) ; }) ;
indexes.resize(pv.size()) ;
std::transform(pv.begin(), pv.end(), indexes.begin(),
[](const std::pair<size_t,RAIter>& a) -> size_t { return a.first ; }) ;
}
L'utilisation est la même que celle de std :: sort, sauf qu'un conteneur d'index reçoit des index triés . Testing:
int a[] = { 3, 1, 0, 4 } ;
std::vector<size_t> indexes ;
argsort(a, a + sizeof(a) / sizeof(a[0]), std::less<int>(), indexes) ;
for (size_t i : indexes) printf("%d\n", int(i)) ;
vous devriez obtenir 2 1 0 3 . pour les compilateurs sans prise en charge de c ++ 0x, remplacez l'expression lamba par un modèle de classe:
template <class RAIter, class Compare>
class PairComp {
public:
Compare comp ;
PairComp(Compare comp_) : comp(comp_) {}
bool operator() (const std::pair<size_t,RAIter>& a,
const std::pair<size_t,RAIter>& b) const { return comp(*a.second, *b.second) ; }
} ;
et réécrire std :: sort as
std::sort(pv.begin(), pv.end(), PairComp(comp)()) ;
vector<pair<int,int> >a;
for (i = 0 ;i < n ; i++) {
// filling the original array
cin >> k;
a.Push_back (make_pair (k,i)); // k = value, i = original index
}
sort (a.begin(),a.end());
for (i = 0 ; i < n ; i++){
cout << a[i].first << " " << a[i].second << "\n";
}
Maintenant, a
contient à la fois nos valeurs et leurs index respectifs dans les éléments triés.
a[i].first = value
à i
'e.
a[i].second = idx
dans le tableau initial.
Je suis tombé sur cette question et j'ai compris que le tri direct des itérateurs serait un moyen de trier les valeurs et de garder une trace des indices; Il n'est pas nécessaire de définir un conteneur supplémentaire de pair
s of (valeur, index), ce qui est utile lorsque les valeurs sont des objets volumineux. Les itérateurs fournissent l'accès à la valeur et à l'index:
/*
* a function object that allows to compare
* the iterators by the value they point to
*/
template < class RAIter, class Compare >
class IterSortComp
{
public:
IterSortComp ( Compare comp ): m_comp ( comp ) { }
inline bool operator( ) ( const RAIter & i, const RAIter & j ) const
{
return m_comp ( * i, * j );
}
private:
const Compare m_comp;
};
template <class INIter, class RAIter, class Compare>
void itersort ( INIter first, INIter last, std::vector < RAIter > & idx, Compare comp )
{
idx.resize ( std::distance ( first, last ) );
for ( typename std::vector < RAIter >::iterator j = idx.begin( ); first != last; ++ j, ++ first )
* j = first;
std::sort ( idx.begin( ), idx.end( ), IterSortComp< RAIter, Compare > ( comp ) );
}
comme pour l'exemple d'utilisation:
std::vector < int > A ( n );
// populate A with some random values
std::generate ( A.begin( ), A.end( ), Rand );
std::vector < std::vector < int >::const_iterator > idx;
itersort ( A.begin( ), A.end( ), idx, std::less < int > ( ) );
maintenant, par exemple, le cinquième plus petit élément du vecteur trié aurait la valeur **idx[ 5 ]
et son index dans le vecteur d'origine serait distance( A.begin( ), *idx[ 5 ] )
ou simplement *idx[ 5 ] - A.begin( )
.
C'est plus facile qu'il n'y paraît.
Supposons que le vecteur donné est
A=[2,4,3]
Créer un nouveau vecteur
V=[0,1,2] // indicating positions
Trier V et pendant le tri au lieu de comparer les éléments de V, comparer les éléments correspondants de A
//Assume A is a given vector with N elements
vector<int> V(N);
int x=0;
std::iota(V.begin(),V.end(),x++); //Initializing
sort( V.begin(),V.end(), [&](int i,int j){return A[i]<A[j];} );
Belle solution de @Lukasz Wiklendt! Bien que dans mon cas j'avais besoin de quelque chose de plus générique, je l'ai un peu modifié:
template <class RAIter, class Compare>
vector<size_t> argSort(RAIter first, RAIter last, Compare comp) {
vector<size_t> idx(last-first);
iota(idx.begin(), idx.end(), 0);
auto idxComp = [&first,comp](size_t i1, size_t i2) {
return comp(first[i1], first[i2]);
};
sort(idx.begin(), idx.end(), idxComp);
return idx;
}
Exemple: recherchez des index triant un vecteur de chaînes par longueur, à l'exception du premier élément qui est un mannequin.
vector<string> test = {"dummy", "a", "abc", "ab"};
auto comp = [](const string &a, const string& b) {
return a.length() > b.length();
};
const auto& beginIt = test.begin() + 1;
vector<size_t> ind = argSort(beginIt, test.end(), comp);
for(auto i : ind)
cout << beginIt[i] << endl;
impressions:
abc
ab
a
Créez une fonction std::pair
puis triez la paire:
version générique:
template< class RandomAccessIterator,class Compare >
auto sort2(RandomAccessIterator begin,RandomAccessIterator end,Compare cmp) ->
std::vector<std::pair<std::uint32_t,RandomAccessIterator>>
{
using valueType=typename std::iterator_traits<RandomAccessIterator>::value_type;
using Pair=std::pair<std::uint32_t,RandomAccessIterator>;
std::vector<Pair> index_pair;
index_pair.reserve(std::distance(begin,end));
for(uint32_t idx=0;begin!=end;++begin,++idx){
index_pair.Push_back(Pair(idx,begin));
}
std::sort( index_pair.begin(),index_pair.end(),[&](const Pair& lhs,const Pair& rhs){
return cmp(*lhs.second,*rhs.second);
});
return index_pair;
}
Si cela est possible, vous pouvez construire le tableau de positions à l'aide de la fonction de recherche, puis trier le tableau.
Ou peut-être pouvez-vous utiliser une carte où la clé serait l'élément, et les valeurs une liste de sa position dans les tableaux à venir (A, B et C)
Cela dépend des utilisations ultérieures de ces tableaux.
Pour ce type de questionStorez les données du tableau initial dans une nouvelle donnée, puis recherchez le premier élément du tableau trié dans le tableau dupliqué, et cet indice doit être stocké dans un vecteur ou un tableau.
input array=>a
duplicate array=>b
vector=>c(Stores the indices(position) of the orignal array
Syntax:
for(i=0;i<n;i++)
c.Push_back(binarysearch(b,n,a[i]));`
Ici, binarysearch est une fonction qui prend le tableau, la taille du tableau, l'élément de recherche et renvoie la position de l'élément recherché.
Les éléments du vecteur sont-ils uniques? Si tel est le cas, copiez le vecteur, triez l’une des copies avec STL Sort , puis vous pourrez trouver l’index de chaque élément dans le vecteur d’origine.
Si le vecteur est censé gérer les éléments en double, je pense que vous feriez mieux de mettre en œuvre votre propre routine de tri.
Il existe un autre moyen de résoudre ce problème en utilisant une carte:
vector<double> v = {...}; // input data
map<double, unsigned> m; // mapping from value to its index
for (auto it = v.begin(); it != v.end(); ++it)
m[*it] = it - v.begin();
Cela supprimera cependant des éléments non uniques. Si ce n'est pas acceptable, utilisez une carte multiple:
vector<double> v = {...}; // input data
multimap<double, unsigned> m; // mapping from value to its index
for (auto it = v.begin(); it != v.end(); ++it)
m.insert(make_pair(*it, it - v.begin()));
Pour sortir les indices, parcourez la carte ou la multi-carte:
for (auto it = m.begin(); it != m.end(); ++it)
cout << it->second << endl;
Eh bien, ma solution utilise la technique des résidus. Nous pouvons placer les valeurs en cours de tri dans les 2 octets supérieurs et les index des éléments - dans les 2 octets inférieurs:
int myints[] = {32,71,12,45,26,80,53,33};
for (int i = 0; i < 8; i++)
myints[i] = myints[i]*(1 << 16) + i;
Puis, triez le tableau myints
comme d'habitude:
std::vector<int> myvector(myints, myints+8);
sort(myvector.begin(), myvector.begin()+8, std::less<int>());
Après cela, vous pouvez accéder aux indices des éléments via residuum. Le code suivant imprime les index des valeurs triées par ordre croissant:
for (std::vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it)
std::cout << ' ' << (*it)%(1 << 16);
Bien sûr, cette technique ne fonctionne que pour les valeurs relativement petites du tableau original myints
(c'est-à-dire celles pouvant correspondre aux 2 octets supérieurs de int
). Mais il présente l’avantage supplémentaire de distinguer les valeurs identiques de myints
: leurs indices seront imprimés dans le bon ordre.
Il y a beaucoup de façons. Une solution assez simple consiste à utiliser un vecteur 2D.
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<vector<double>> val_and_id;
val_and_id.resize(5);
for (int i = 0; i < 5; i++) {
val_and_id[i].resize(2); // one to store value, the other for index.
}
// Store value in dimension 1, and index in the other:
// say values are 5,4,7,1,3.
val_and_id[0][0] = 5.0;
val_and_id[1][0] = 4.0;
val_and_id[2][0] = 7.0;
val_and_id[3][0] = 1.0;
val_and_id[4][0] = 3.0;
val_and_id[0][1] = 0.0;
val_and_id[1][1] = 1.0;
val_and_id[2][1] = 2.0;
val_and_id[3][1] = 3.0;
val_and_id[4][1] = 4.0;
sort(val_and_id.begin(), val_and_id.end());
// display them:
cout << "Index \t" << "Value \n";
for (int i = 0; i < 5; i++) {
cout << val_and_id[i][1] << "\t" << val_and_id[i][0] << "\n";
}
return 0;
}
Voici la sortie:
Index Value
3 1
4 3
1 4
0 5
2 7
Envisagez d'utiliser std::multimap
comme suggéré par @Ulrich Eckhardt. Juste que le code pourrait être rendu encore plus simple.
Donné
std::vector<int> a = {5, 2, 1, 4, 3}; // a: 5 2 1 4 3
Pour trier le temps moyen d'insertion
std::multimap<int, std::size_t> mm;
for (std::size_t i = 0; i != a.size(); ++i)
mm.insert({a[i], i});
Pour récupérer des valeurs et des index d'origine
std::vector<int> b;
std::vector<std::size_t> c;
for (const auto & kv : mm) {
b.Push_back(kv.first); // b: 1 2 3 4 5
c.Push_back(kv.second); // c: 2 1 4 3 0
}
La raison pour préférer un std::multimap
à un std::map
est d'autoriser des valeurs égales dans les vecteurs d'origine. Veuillez également noter que, contrairement à std::map
, operator[]
n'est pas défini pour std::multimap
.