Related
Using C++, and hopefully the standard library, I want to sort a sequence of samples in ascending order, but I also want to remember the original indexes of the new samples.
For example, I have a set, or vector, or matrix of samples A : [5, 2, 1, 4, 3]. I want to sort these to be B : [1,2,3,4,5], but I also want to remember the original indexes of the values, so I can get another set which would be:
C : [2, 1, 4, 3, 0 ] - which corresponds to the index of each element in 'B', in the original 'A'.
For example, in Matlab you can do:
[a,b]=sort([5, 8, 7])
a = 5 7 8
b = 1 3 2
Can anyone see a good way to do this?
Using C++ 11 lambdas:
#include <iostream>
#include <vector>
#include <numeric> // std::iota
#include <algorithm> // std::sort, std::stable_sort
using namespace std;
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
// using std::stable_sort instead of std::sort
// to avoid unnecessary index re-orderings
// when v contains elements of equal values
stable_sort(idx.begin(), idx.end(),
[&v](size_t i1, size_t i2) {return v[i1] < v[i2];});
return idx;
}
Now you can use the returned index vector in iterations such as
for (auto i: sort_indexes(v)) {
cout << v[i] << endl;
}
You can also choose to supply your original index vector, sort function, comparator, or automatically reorder v in the sort_indexes function using an extra vector.
You could sort std::pair instead of just ints - first int is original data, second int is original index. Then supply a comparator that only sorts on the first int. Example:
Your problem instance: v = [5 7 8]
New problem instance: v_prime = [<5,0>, <8,1>, <7,2>]
Sort the new problem instance using a comparator like:
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
The result of std::sort on v_prime, using that comparator, should be:
v_prime = [<5,0>, <7,2>, <8,1>]
You can peel out the indices by walking the vector, grabbing .second from each std::pair.
Suppose Given vector is
A=[2,4,3]
Create a new vector
V=[0,1,2] // indicating positions
Sort V and while sorting instead of comparing elements of V , compare corresponding elements of A
//Assume A is a given vector with N elements
vector<int> V(N);
std::iota(V.begin(),V.end(),0); //Initializing
sort( V.begin(),V.end(), [&](int i,int j){return A[i]<A[j];} );
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";
}
Now a contains both both our values and their respective indices in the sorted.
a[i].first = value at i'th.
a[i].second = idx in initial array.
I wrote generic version of index sort.
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 ; }) ;
}
Usage is the same as that of std::sort except for an index container to receive sorted indexes.
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)) ;
you should get 2 1 0 3.
for the compilers without c++0x support, replace the lamba expression as a class template:
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) ; }
} ;
and rewrite std::sort as
std::sort(pv.begin(), pv.end(), PairComp(comp)()) ;
I came across this question, and figured out sorting the iterators directly would be a way to sort the values and keep track of indices; There is no need to define an extra container of pairs of ( value, index ) which is helpful when the values are large objects; The iterators provides the access to both the value and the 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 ) );
}
as for the usage example:
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 > ( ) );
now, for example, the 5th smallest element in the sorted vector would have value **idx[ 5 ] and its index in the original vector would be distance( A.begin( ), *idx[ 5 ] ) or simply *idx[ 5 ] - A.begin( ).
Consider using std::multimap as suggested by #Ulrich Eckhardt. Just that the code could be made even simpler.
Given
std::vector<int> a = {5, 2, 1, 4, 3}; // a: 5 2 1 4 3
To sort in the mean time of insertion
std::multimap<int, std::size_t> mm;
for (std::size_t i = 0; i != a.size(); ++i)
mm.insert({a[i], i});
To retrieve values and original indices
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
}
The reason to prefer a std::multimap to a std::map is to allow equal values in original vectors. Also please note that, unlike for std::map, operator[] is not defined for std::multimap.
There is another way to solve this, using a map:
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();
This will eradicate non-unique elements though. If that's not acceptable, use a multimap:
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()));
In order to output the indices, iterate over the map or multimap:
for (auto it = m.begin(); it != m.end(); ++it)
cout << it->second << endl;
Beautiful solution by #Lukasz Wiklendt! Although in my case I needed something more generic so I modified it a bit:
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;
}
Example: Find indices sorting a vector of strings by length, except for the first element which is a dummy.
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;
prints:
abc
ab
a
Make a std::pair in function then sort pair :
generic version :
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;
}
ideone
Well, my solution uses residue technique. We can place the values under sorting in the upper 2 bytes and the indices of the elements - in the lower 2 bytes:
int myints[] = {32,71,12,45,26,80,53,33};
for (int i = 0; i < 8; i++)
myints[i] = myints[i]*(1 << 16) + i;
Then sort the array myints as usual:
std::vector<int> myvector(myints, myints+8);
sort(myvector.begin(), myvector.begin()+8, std::less<int>());
After that you can access the elements' indices via residuum. The following code prints the indices of the values sorted in the ascending order:
for (std::vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it)
std::cout << ' ' << (*it)%(1 << 16);
Of course, this technique works only for the relatively small values in the original array myints (i.e. those which can fit into upper 2 bytes of int). But it has additional benefit of distinguishing identical values of myints: their indices will be printed in the right order.
If it's possible, you can build the position array using find function, and then sort the array.
Or maybe you can use a map where the key would be the element, and the values a list of its position in the upcoming arrays (A, B and C)
It depends on later uses of those arrays.
I recently stepped upon the elegant projection feature of C++20 <ranges> and it allows to write shorter/clearer code:
std::vector<std::size_t> B(std::size(A));
std::iota(begin(B), end(B), 0);
std::ranges::sort(B, {}, [&](std::size_t i){ return A[i]; });
{} refers to the usual std::less<std::size_t>. So as you can see we define a function to call on each element before any comparaison. This projection feature is actually quite powerful since this function can be, as here, a lambda or it can even be a method, or a member value. For instance:
struct Item {
float price;
float weight;
float efficiency() const { return price / weight; }
};
int main() {
std::vector<Item> items{{7, 9}, {3, 4}, {5, 3}, {9, 7}};
std::ranges::sort(items, std::greater<>(), &Item::efficiency);
// now items are sorted by their efficiency in decreasing order:
// items = {{5, 3}, {9, 7}, {7, 9}, {3, 4}}
}
If we wanted to sort by increasing price:
std::ranges::sort(items, {}, &Item::price);
Don't define operator< or use lambdas, use a projection!
Are the items in the vector unique? If so, copy the vector, sort one of the copies with STL Sort then you can find which index each item had in the original vector.
If the vector is supposed to handle duplicate items, I think youre better of implementing your own sort routine.
For this type of question
Store the orignal array data into a new data and then binary search the first element of the sorted array into the duplicated array and that indice should be stored into a vector or array.
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]));`
Here binarysearch is a function which takes the array,size of array,searching item and would return the position of the searched item
One solution is to use a 2D vector.
#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;
}
Here is the output:
Index Value
3 1
4 3
1 4
0 5
2 7
I have a vector of tuples.
Pseudo:
vector<(x, x)>
Where the two x'es represent two different values. What I want to do is find the x with the biggest value in the whole vector.
Code:
#include <iostream>
#include <vector>
#include <tuple>
int main() {
//Making vector
std::vector<std::tuple<int, int>> v;
//Adding one random tuple to it
v.push_back(std::make_tuple(1,50));
//Getting biggest value in the vector
int bufferMax = 0;
for (auto i : v) {
for (int j = 0; j < 2; j++) {
int nrToTest = std::get<0>(i);
if (j == 1) std::get<1>(i);
if (nrToTest > bufferMax) bufferMax = nrToTest;
}
}
//Output biggest
std::cout << bufferMax << std::endl;
}
Output: 1
In the first loop I loop over all tuples in the vector, than I have a second loop oscillating between the values 0 and 1. If that value is 0 I compare the first element in tuple i with the buffer, otherwise the second element in tuple i.
However, this code doesn't work as you can see at the output.
Why doesn't this algorithm work?
I have found another algorithm which does work (I guess)
int bufferMax = 0;
for (auto i : v) {
//Biggest tuple value:
int nrToTest = std::get<0>(i);
if (std::get<0>(i) < std::get<1>(i)) {
nrToTest = std::get<1>(i);
}
if (nrToTest > bufferMax) bufferMax = nrToTest;
}
But I am still wondering why the first one doesn't work?
It depends on how you define the max value. In the first code snippet, you compare only the first value of each tuple:
int bufferMax = 0;
for (auto i : v) { // Iterate over all elements
for (int j = 0; j < 2; j++) {
int nrToTest = std::get<0>(i); // Get the first entry in the tuple
if (j == 1) std::get<1>(i); // This is useless: you get the second value
// in the tuple but you don't do anything with it.
if (nrToTest > bufferMax) // You update the max, but nrToTest
// is always the first value in the tuple
bufferMax = nrToTest;
}
}
The second snippet works because, at each iteration, you set the nrToTest to be the largest of the entries in the tuple, and then you compare it with the global maximum.
This code snippet ( sorting by first element) works:
std::vector<tuple<int, int>> vec;
The sorting using std::max_element
auto result = std::max_element(vec.begin(), vec.end(), [](const std::tuple<int, int>& lhs, const std::tuple<int, int>& rhs)
{
return std::get<0>(lhs) < std::get<0>(rhs);
});
Use std::tie to obtain the result:
int a, b;
std::tie(a,b) = *(result);
std::cout << a << " " << b << std::endl;
Using C++, and hopefully the standard library, I want to sort a sequence of samples in ascending order, but I also want to remember the original indexes of the new samples.
For example, I have a set, or vector, or matrix of samples A : [5, 2, 1, 4, 3]. I want to sort these to be B : [1,2,3,4,5], but I also want to remember the original indexes of the values, so I can get another set which would be:
C : [2, 1, 4, 3, 0 ] - which corresponds to the index of each element in 'B', in the original 'A'.
For example, in Matlab you can do:
[a,b]=sort([5, 8, 7])
a = 5 7 8
b = 1 3 2
Can anyone see a good way to do this?
Using C++ 11 lambdas:
#include <iostream>
#include <vector>
#include <numeric> // std::iota
#include <algorithm> // std::sort, std::stable_sort
using namespace std;
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
// using std::stable_sort instead of std::sort
// to avoid unnecessary index re-orderings
// when v contains elements of equal values
stable_sort(idx.begin(), idx.end(),
[&v](size_t i1, size_t i2) {return v[i1] < v[i2];});
return idx;
}
Now you can use the returned index vector in iterations such as
for (auto i: sort_indexes(v)) {
cout << v[i] << endl;
}
You can also choose to supply your original index vector, sort function, comparator, or automatically reorder v in the sort_indexes function using an extra vector.
You could sort std::pair instead of just ints - first int is original data, second int is original index. Then supply a comparator that only sorts on the first int. Example:
Your problem instance: v = [5 7 8]
New problem instance: v_prime = [<5,0>, <8,1>, <7,2>]
Sort the new problem instance using a comparator like:
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
The result of std::sort on v_prime, using that comparator, should be:
v_prime = [<5,0>, <7,2>, <8,1>]
You can peel out the indices by walking the vector, grabbing .second from each std::pair.
Suppose Given vector is
A=[2,4,3]
Create a new vector
V=[0,1,2] // indicating positions
Sort V and while sorting instead of comparing elements of V , compare corresponding elements of A
//Assume A is a given vector with N elements
vector<int> V(N);
std::iota(V.begin(),V.end(),0); //Initializing
sort( V.begin(),V.end(), [&](int i,int j){return A[i]<A[j];} );
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";
}
Now a contains both both our values and their respective indices in the sorted.
a[i].first = value at i'th.
a[i].second = idx in initial array.
I wrote generic version of index sort.
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 ; }) ;
}
Usage is the same as that of std::sort except for an index container to receive sorted indexes.
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)) ;
you should get 2 1 0 3.
for the compilers without c++0x support, replace the lamba expression as a class template:
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) ; }
} ;
and rewrite std::sort as
std::sort(pv.begin(), pv.end(), PairComp(comp)()) ;
I came across this question, and figured out sorting the iterators directly would be a way to sort the values and keep track of indices; There is no need to define an extra container of pairs of ( value, index ) which is helpful when the values are large objects; The iterators provides the access to both the value and the 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 ) );
}
as for the usage example:
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 > ( ) );
now, for example, the 5th smallest element in the sorted vector would have value **idx[ 5 ] and its index in the original vector would be distance( A.begin( ), *idx[ 5 ] ) or simply *idx[ 5 ] - A.begin( ).
Consider using std::multimap as suggested by #Ulrich Eckhardt. Just that the code could be made even simpler.
Given
std::vector<int> a = {5, 2, 1, 4, 3}; // a: 5 2 1 4 3
To sort in the mean time of insertion
std::multimap<int, std::size_t> mm;
for (std::size_t i = 0; i != a.size(); ++i)
mm.insert({a[i], i});
To retrieve values and original indices
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
}
The reason to prefer a std::multimap to a std::map is to allow equal values in original vectors. Also please note that, unlike for std::map, operator[] is not defined for std::multimap.
There is another way to solve this, using a map:
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();
This will eradicate non-unique elements though. If that's not acceptable, use a multimap:
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()));
In order to output the indices, iterate over the map or multimap:
for (auto it = m.begin(); it != m.end(); ++it)
cout << it->second << endl;
Beautiful solution by #Lukasz Wiklendt! Although in my case I needed something more generic so I modified it a bit:
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;
}
Example: Find indices sorting a vector of strings by length, except for the first element which is a dummy.
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;
prints:
abc
ab
a
Make a std::pair in function then sort pair :
generic version :
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;
}
ideone
Well, my solution uses residue technique. We can place the values under sorting in the upper 2 bytes and the indices of the elements - in the lower 2 bytes:
int myints[] = {32,71,12,45,26,80,53,33};
for (int i = 0; i < 8; i++)
myints[i] = myints[i]*(1 << 16) + i;
Then sort the array myints as usual:
std::vector<int> myvector(myints, myints+8);
sort(myvector.begin(), myvector.begin()+8, std::less<int>());
After that you can access the elements' indices via residuum. The following code prints the indices of the values sorted in the ascending order:
for (std::vector<int>::iterator it = myvector.begin(); it != myvector.end(); ++it)
std::cout << ' ' << (*it)%(1 << 16);
Of course, this technique works only for the relatively small values in the original array myints (i.e. those which can fit into upper 2 bytes of int). But it has additional benefit of distinguishing identical values of myints: their indices will be printed in the right order.
If it's possible, you can build the position array using find function, and then sort the array.
Or maybe you can use a map where the key would be the element, and the values a list of its position in the upcoming arrays (A, B and C)
It depends on later uses of those arrays.
I recently stepped upon the elegant projection feature of C++20 <ranges> and it allows to write shorter/clearer code:
std::vector<std::size_t> B(std::size(A));
std::iota(begin(B), end(B), 0);
std::ranges::sort(B, {}, [&](std::size_t i){ return A[i]; });
{} refers to the usual std::less<std::size_t>. So as you can see we define a function to call on each element before any comparaison. This projection feature is actually quite powerful since this function can be, as here, a lambda or it can even be a method, or a member value. For instance:
struct Item {
float price;
float weight;
float efficiency() const { return price / weight; }
};
int main() {
std::vector<Item> items{{7, 9}, {3, 4}, {5, 3}, {9, 7}};
std::ranges::sort(items, std::greater<>(), &Item::efficiency);
// now items are sorted by their efficiency in decreasing order:
// items = {{5, 3}, {9, 7}, {7, 9}, {3, 4}}
}
If we wanted to sort by increasing price:
std::ranges::sort(items, {}, &Item::price);
Don't define operator< or use lambdas, use a projection!
Are the items in the vector unique? If so, copy the vector, sort one of the copies with STL Sort then you can find which index each item had in the original vector.
If the vector is supposed to handle duplicate items, I think youre better of implementing your own sort routine.
For this type of question
Store the orignal array data into a new data and then binary search the first element of the sorted array into the duplicated array and that indice should be stored into a vector or array.
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]));`
Here binarysearch is a function which takes the array,size of array,searching item and would return the position of the searched item
One solution is to use a 2D vector.
#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;
}
Here is the output:
Index Value
3 1
4 3
1 4
0 5
2 7
I have two questions, Any help will be highly appriciated.
I have a matrix A ={ 0 1 0 0 1 1 0 0}. Now I found the locations of 0's indexes and saved in the vector B={0 2 3 6 7}.
How Can I extract the elemnents indexed by vector B in A to a new vector, without damaging original vector A? i.e. I want to get C= {0 0 0 0 0} which is the data from A, indexed by B.
How can I erase the elements in A indexed by B?
I tried something like this for question no. 2,but did not suceed.
///// erasing the elements in particular locations
sort (B.begin(), B.end());
for(int i=A.size() - 1; i >= 0; i--){
A.erase(A.begin() + B[i]);
}
1.How Can I extarct the elemnents indexed by vector B in A, without damaging original vector A? i.e. I want to get C= {0 0 0 0 0} which is the data from A, indexed by B.
std::vector<int> C;
for (size_t i = 0; i < B.size(); ++i )
C.push_back(A[B[i]]);
Of course, we're assuming that B does not have entries that will go out-of-bounds of the A vector.
Q1:
If you are happy making a new vector, PaulMcKenxie's answer is what you want:
std::vector<int> C;
for (size_t i = 0; i < B.size(); ++i )
C.push_back(A[B[i]]);
Q2:
Otherwise, you need to remove each instance not indexed by B.
This is relatively complex, as by removing an entry the way you did, you force realocation that can/(will?) invalidate your iterators / pointers to the data.
Probably the best solution (simple and efficient) to this is to create a temporary vector C above, and then swapping the reduced data in.
void delete_indexes(std::vector<int> &data, std::vector<int> &indexes)
{
std::vector<int> temp;
for (size_t i = 0; i < indexes.size(); ++i )
{
temp.push_back(data[indexes[i]]);
}
data.swap(temp); //does the work
}
int main()
{
//do stuff
delete_indexes(A,B);
}
The swap option is fast (just swaps instead of removing and writing) and the temp vector (with your original data) is disposed of when it goes out of scope.
Edit:
This answer could also be what you are looking for, assuming you have a function for generating each element of B that you can apply (even if it is A[i] == 1 (code edited to suit):
for(auto it = A.begin(); it != A.end();)
{
if (criteria_for_B(*it))
it = A.erase(it);
else
++it;
}
For me, I use the erase function but with a counter to décrement the iterator :
#include <iostream>
#include <vector>
using namespace std;
int main(){
vector<int> A;
A.push_back(0);
A.push_back(1);
A.push_back(0);
A.push_back(0);
A.push_back(1);
A.push_back(1);
A.push_back(0);
A.push_back(0);
vector<int> B;
B.push_back(0);
B.push_back(2);
B.push_back(3);
B.push_back(6);
B.push_back(7);
for(int i=0; i<A.size(); i++){
cout << A[i] << "-";
}
cout << endl;
vector<int> C = A;
int ii=0;
for(int i=0; i<B.size(); i++){
C.erase(C.begin() -ii + B[i] );
ii++;
}
for(int i=0; i<C.size(); i++){
cout << C[i] << "-";
}
}
You can use a third vector as me or just directly modify A.
I hope that will help you !
Here are some std-syle generic utilities (standard c++98).
1. Extract elements at indices
/// Extract elements from vector at indices specified by range
template <typename ForwardIt, typename IndicesForwardIt>
inline std::vector<typename std::iterator_traits<ForwardIt>::value_type>
extract_at(
ForwardIt first,
IndicesForwardIt indices_first,
IndicesForwardIt indices_last)
{
typedef std::vector<typename std::iterator_traits<ForwardIt>::value_type>
vector_type;
vector_type extracted;
extracted.reserve(static_cast<typename vector_type::size_type>(
std::distance(indices_first, indices_last)));
for(; indices_first != indices_last; ++indices_first)
extracted.push_back(*(first + *indices_first));
return extracted;
}
/// Extract elements from collection specified by collection of indices
template <typename TVector, typename TIndicesVector>
inline TVector extract_at(const TVector& data, const TIndicesVector& indices)
{
return extract_at(data.begin(), indices.begin(), indices.end());
}
2. Remove elements at indices
//! Remove one element with given index from the range [first; last)
template <typename ForwardIt>
inline ForwardIt remove_at(ForwardIt first, ForwardIt last, const size_t index)
{
std::advance(first, index);
for(ForwardIt it = first + 1; it != last; ++it, ++first)
*first = *it;
return first;
}
/*!
* Remove elements in the range [first; last) with indices from the sorted
* range [indices_first, indices_last)
*/
template <typename ForwardIt, typename SortedIndicesForwardIt>
inline ForwardIt remove_at(
ForwardIt first,
ForwardIt last,
SortedIndicesForwardIt indices_first,
SortedIndicesForwardIt indices_last)
{
typedef typename std::vector<bool> flags;
// flag elements to keep
flags is_keep(
static_cast<flags::size_type>(std::distance(first, last)), true);
for(; indices_first != indices_last; ++indices_first)
is_keep[static_cast<flags::size_type>(*indices_first)] = false;
// move kept elements to beginning
ForwardIt result = first;
for(flags::const_iterator it = is_keep.begin(); first != last; ++first, ++it)
if(*it) // keep element
*result++ = *first; //in c++11 and later use: *result++ = std::move(*first);
return result;
}
Usage (erase-remove idiom):
std::vector<int> vec{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
std::vector<int> ii{6, 3, 1};
std::sort(ii.begin(), ii.end());
vec.erase(remove_at(vec.begin(), vec.end(), ii.begin(), ii.end()), vec.end());
I've stumbled upon this problem: I can't seem to select the item at the index' position in a normal std::set. Is this a bug in STD?
Below a simple example:
#include <iostream>
#include <set>
int main()
{
std::set<int> my_set;
my_set.insert(0x4A);
my_set.insert(0x4F);
my_set.insert(0x4B);
my_set.insert(0x45);
for (std::set<int>::iterator it=my_set.begin(); it!=my_set.end(); ++it)
std::cout << ' ' << char(*it); // ups the ordering
//int x = my_set[0]; // this causes a crash!
}
Anything I can do to fix the issue?
It doesn't cause a crash, it just doesn't compile. set doesn't have access by index.
You can get the nth element like this:
std::set<int>::iterator it = my_set.begin();
std::advance(it, n);
int x = *it;
Assuming my_set.size() > n, of course. You should be aware that this operation takes time approximately proportional to n. In C++11 there's a nicer way of writing it:
int x = *std::next(my_set.begin(), n);
Again, you have to know that n is in bounds first.
Try this you will be able to use set in another way namely ordered_set
This is very much used in CP
Hope this is diff from all and will help you/someone!
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
#define ordered_set tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>
Now you can use
order_of_key (k) : Number of items strictly smaller than k .
find_by_order(k) : K-th element in a set (counting from zero). //This is what you need
[https://www.geeksforgeeks.org/ordered-set-gnu-c-pbds/][1]
A usual implementation of std::set is to use binary search trees, notably self-balancing binary search trees such as red-black trees
They don't give you constant time access to the n-th element. However, you seems to want the first. So try in C++11:
auto it = my_set.begin();
int first=0;
if (it != my_set.end()) first = *it;
There is no way you can access it in constant time.
But you can reach to any element in O(n) time.
E.g.
std::set<int>::iterator it;
it=my_set.begin();
advance(it,n);
cout<<*it;
I don't think std::set has any methods of doing this in better than O(n) time, but I recently made this data structure using a set and a Binary Index Tree that can do most things the std::set can do, but it can also get the index of an element in O(log n) time, as well as the element at a specific index in O((log n) * (log n)) time:
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <math.h>
#include <vector>
#include <queue>
#include <bitset>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
using namespace std;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;
typedef long long ll;
typedef pair<ll, ll> pll;
#define max(n, m) ((n>m)?n:m)
#define min(n, m) ((n<m)?n:m)
#define f first
#define s second
struct ss
{
// binary index tree (to mark elements)
int bit[1000010]; // set this number to the max you will use
// set (to store the numbers in order)
set<int> nums;
// the maximum element in the set (NOTE: this data structure works with marking in the BIT array, but you can make this better by using an unordered set to store all values that could appear inside of the set, but this will increase runtime by a high constant factor)
int mx;
// constructor
ss(int maxEl)
{
mx = maxEl + 5;
}
int sum(int arr[], int idx)
{
int ans = 0;
idx ++;
if(idx > mx + 5) return -1;
while(idx > 0)
{
ans += arr[idx];
idx -= idx & (-idx);
}
return ans;
}
void update(int arr[], int idx, int val, int size)
{
idx ++;
while(idx <= size)
{
arr[idx] += val;
idx += idx & (-idx);
}
}
int bs(int l, int r, int idx)
{
int mid = (l + r) / 2;
if(l == r) return mid + 1;
if(l == r - 1)
{
if(sum(bit, r) == idx) return r + 1;
return r;
}
if(sum(bit, mid) <= idx) return bs(mid, r, idx);
return bs(l, mid - 1, idx);
}
// regular set functions
set<int>::iterator find(int num) { return nums.find(num); }
set<int>::iterator lower_bound(int num) { return nums.lower_bound(num); }
set<int>::iterator upper_bound(int num) { return nums.upper_bound(num); }
int size() { return (int)nums.size(); }
set<int>::iterator begin() { return nums.begin(); }
set<int>::iterator end() { return nums.end(); }
bool empty() { return nums.empty(); }
// slightly modified insert and erase functions to also mark stuff in BIT (still O(log n) though)
void insert(int num)
{
if(nums.find(num) == nums.end())
update(bit, num, 1, mx); // marks the element in the BIT if it doesn't already exist
nums.insert(num);
}
void erase(int num)
{
if(nums.find(num) != nums.end())
update(bit, num, -1, mx); // unmarks the element in the BIT if it exists in the set
nums.erase(num);
}
// gets index (0-indexed) of a specific element in O(log n), returns -1 if element not in set
int idx(int num)
{
if(nums.find(num) == nums.end())
return -1;
return sum(bit, num - 1);
}
// gets the iterator of the element at a specific index (0-indexed) in O((log n) * (log n)), returns end of set if idx is invalid
set<int>::iterator at(int idx)
{
if(idx < 0 || idx >= nums.size())
return nums.end();
return nums.find(bs(0, mx, idx));
}
};
int main()
{
ss test = ss(1000);
test.insert(1);
test.insert(3);
test.insert(5);
test.insert(1);
test.insert(9);
test.insert(1000);
cout << *test.at(1) << "\n";
test.erase(3);
cout << *test.at(1) << "\n";
cout << test.idx(1) << "\n";
cout << *test.at(-1) << "\n";
}
This set does have some flaws since it marks elements in the Binary Indexed Tree, so the elements cannot be negative or really big without some extra modifications, but it can still be helpful in some cases. Also, using an std::map or some other type of map could make the set work with negative numbers, big numbers, as well as other data types, but this would increase the runtime by a factor of O(log n) and I think you would have to know all the elements that could appear in the set beforehand so that you can store them in the correct order inside of the map.
EDIT: I just realized there is already a policy-based data structure called ordered-set that has the same functions as a set but can do the two operations (get element at index and get index of element) in O(log n). Read more here: https://www.geeksforgeeks.org/ordered-set-gnu-c-pbds/. This might not work in all compilers though
This is not a bug in the STD. There is no random access in a std::set. If you need random access by index, you can use std::vector
Sometimes there's a good reason for needing a set you can index into. I had to implement this functionality recently to support a legacy API which has functions to return the number of items, and the item at an index, so that the caller can enumerate the items.
My way of solving the problem is to use std::vector, and use std::equal_range to find and insert or delete items in the set. For example, inserting a new item into the set looks like this:
std:vector<std::string> my_set;
...
std::string new_item("test");
auto range = std::equal_range(my_set.begin(),my_set.end(),new_item);
if (range.first == range.second)
my_set.insert(range.first,new_item);
Deleting is very similar: use equal_range to find the item, and if range.first is not equal to range.second, delete that range.
i believe the most optimal way, especially if this indexing happens in a loop, is to convert to a vector.
auto my_vect = std::vector(my_set.begin(), my_set.end()); // O[n]
int output = my_vect[n]; // O[1]
std::set<int> my_set;
my_set.insert(0x4A);
my_set.insert(0x4F);
my_set.insert(0x4B);
my_set.insert(0x45);
int arr[my_set.size()];
set<int>::iterator it = my_set.begin();
for (int i = 0; i < my_set.size(); i++) {
arr[i] = *it;
it++;
}
cout << arr[0];
Edit: Edited code. You can't access set using index but the above method would provide an "index" i if you want to copy the elements from set into an array, provided you have created an array of sufficient size before hand.