I have an std::vector<unsigned int> v with some índices (for performance purposes I can't use std::set.
I want to create the negated version of v, i.e., the vector with all the remaining indices. If, in a range [0,10], v=[0,2,4,6,8], I want the negated version, nv=[1,3,5,7,9].
Note: v is sorted!
What is the most effective way of doing so?
It is linear time.
std::vector<unsigned int> getnv(unsigned rangebeg, unsigned rangeend, const std::vector<unsigned int>& v)
{
std::vector<unsigned int> nv;
nv.reserve(rangeend - rangebeg - v.size());
std::vector<unsigned int>::const_iterator next = v.begin();
// skip out-of-range values in v
while (next != v.end() && *next < rangebeg) {
++next;
}
for (unsigned i = rangebeg; i < rangeend; ++i)
{
if (next != v.end() && *next == i) {
++next;
continue;
}
nv.push_back(i);
}
return nv;
}
It seems the array v[a,b,c......] is already sorted.
Then, initialse vn=[] and traverse through the elements and insert nos from v[i-1] to v[i] into vn[].
Code:
void insert_nos(int A, int B)
{
for(int i=A+1;i<B;i++)
vn.push_back(i);
}
insert_nos(range_start-1, v[0]);
for(int i=0;i<v.size()-1;i++)
insert_nos(v[i],v[i+1]);
insert_nos(v[v.size()-1],range_end+1);
Make sure to handle the boundary cases.
Complexity: O(n)
Here's a version without raw loops:
std::array<int, 2> range{0,10};
std::vector<int> items{0, 1, 2, 4, 5, 9};
std::vector<int> v(range.back() - range.front() - items.size());
std::generate(v.begin(), v.end(),
[iter = items.cbegin(), endIter = items.cend(), i = range.front()] () mutable
{ while(iter < endIter && i == *iter) { ++iter; ++i; } return i++; });
Related
I have the following function to pick a random element from a std::set:
int pick_random(const std::set<int>& vertex_set) {
std::uniform_int_distribution<std::set<int>::size_type> dist(0, vertex_set.size() - 1);
const std::set<int>::size_type rand_idx = dist(mt);
std::set<int>::const_iterator it = vertex_set.begin();
for (std::set<int>::size_type i = 0; i < rand_idx; i++) {
it++;
}
return *it;
}
However, I wonder how to properly draw a sample of n elements from a set. With C++17 compiler I can use std::sample function, but this is not the case here because I have C++11 compiler.
If you don't mind the copy, an easy way is to create a std::vector from your std::set, shuffle it using std::shuffle and then takes the first n elements:
std::vector<int> pick_random_n(const std::set<int>& vertex_set, std::size_t n) {
std::vector<int> vec(std::begin(vertex_set), std::end(vertex_set));
std::shuffle(std::begin(vec), std::end(vec), mt);
vec.resize(std::min(n, vertex_set.size()));
return vec;
}
If you do not want the extra-copy, you could look at the implementation of std::sample, from, e.g., libc++ and implement your own for std::set:
std::vector<int> pick_random_n(const std::set<int>& vertex_set, std::size_t n) {
auto unsampled_sz = vertex_set.size();
auto first = std::begin(vertex_set);
std::vector<int> vec;
vec.reserve(std::min(n, unsampled_sz));
for (n = std::min(n, unsampled_sz); n != 0; ++first) {
auto r =
std::uniform_int_distribution<std::size_t>(0, --unsampled_sz)(mt);
if (r < n) {
vec.push_back(*first);
--n;
}
}
return vec;
}
Suppose I have a vector as follows
std::vector<int> v = {3, 9, 7, 7, 2};
I would like to sort this vector of elements so that the vector will be stored as 77932. So first, we store the common elements (7), then we sort the remaining elements from the highest to the lowest.
If I have a vector as follows
std::vector<int> v = {3, 7, 7, 7, 2};
Here, it would lead to 77732.
Same for
std::vector<int> v = {7, 9, 2, 7, 9};
it should lead to 99772, because the 9s are higher than 7s.
One last example
std::vector<int> v = {7, 9, 7, 7, 9};
it should lead to 77799, because there are more 7s than 9s.
What could be the fastest algorithm to implement this?
Use std::multiset to do counting for you. Then sort using a simple custom comparer with tie breaking logic implemented with std::tie:
std::vector<int> data = {7, 9, 2, 7, 9};
std::multiset<int> count(data.begin(), data.end());
std::sort(
data.begin()
, data.end()
, [&](int a, int b) {
int ca = count.count(a);
int cb = count.count(b);
return std::tie(ca, a) > std::tie(cb, b);
}
);
std::copy(data.begin(), data.end(), std::ostream_iterator<int>(std::cout, " "));
Demo 1
Edit: count(n) function of of std::multiset is linear in the number of duplicates, which may degrade the performance of your sorting algorithm. You can address this by using std::unordered_map in its place:
std::vector<int> data = {7, 9, 2, 7, 9};
std::unordered_map<int,int> count;
for (auto v : data)
count[v]++;
std::sort(
data.begin()
, data.end()
, [&](int a, int b) {
return std::tie(count[a], a) > std::tie(count[b], b);
}
);
std::copy(data.begin(), data.end(), std::ostream_iterator<int>(std::cout, " "));
Demo 2.
You will need an auxiliary frequency count structure, then you can just define a comparator lambda and use whatever sort you like, std::sort is a sensible default
std::unordered_map<int, size_t> frequency;
std::for_each(v.begin(), v.end()
, [&](int i) { ++frequency[i]; });
std::sort(v.begin(), v.end()
, [&](int lhs, int rhs)
{
return std::tie(frequency[lhs], lhs) < std::tie(frequency[rhs], rhs);
});
I wouldn't be satisfied if a candidate proposed an auxiliary map for this task - clearly a sort does most of the work, and the auxiliary structure should be a vector (or, after I've actually tried to implement it, 2 vectors):
void custom_sort(vector<int> &v)
{
if (v.size() < 2)
return;
sort(v.begin(), v.end(), std::greater<int>());
vector<int> dupl;
vector<int> singl;
int d;
bool dv = false;
for (int i = 1; i < v.size(); ++i)
{
if (!dv)
{
if (v[i - 1] == v[i])
{
d = v[i];
dv = true;
dupl.push_back(d);
}
else
{
singl.push_back(v[i - 1]);
}
}
else
{
dupl.push_back(d);
if (v[i] != d)
dv = false;
}
}
if (!dv)
singl.push_back(v.back());
else
dupl.push_back(d);
auto mid = copy(dupl.begin(), dupl.end(), v.begin());
copy(singl.begin(), singl.end(), mid);
}
But yes, the branching is tricky - if you want to use it for more than an inverview, please test it... :-)
EDIT this answers an early version of the question.
If the elements are small integers, i.e. have limited range, we can extend the counting sort algorithm (since the keys here are the elements, we don't need to establish the starting position separately).
void custom_sort(std::vector<int>&v, const int N)
// assume that all elements are in [0,N[ and N elements fit into cash
{
vector<int> count(N);
for(auto x:v)
count.at(x) ++; // replace by count[x]++ if you're sure that 0 <= x < N
int i=0;
// first pass: insert multiple elements
for(auto n=N-1; n>=0; --n)
if(count[n] > 1)
for(auto k=0; k!=count[n]; ++k)
v[i++] = n;
// second pass: insert single elements
for(auto n=N-1; n>=0; --n)
if(count[n] == 1)
v[i++] = n;
}
There is O(N Log(N)) algorithm with extra O(N) memory.
#include <cstdio>
#include <vector>
#include <algorithm>
#include <utility>
int main(){
typedef std::pair<int, int> pii;
typedef std::vector< int > vi ;
typedef std::vector< pii > vii;
vi v = {7, 9, 7, 7, 9};
//O( N log(N) )
std::sort(v.begin(), v.end());
vii vc;
vc.reserve(v.size());
// O (N) make (cnt, value) pair of vector
for(size_t i = 0; i != v.size(); ++i)
{
if (vc.empty() || v[i] != vc.back().second ){
vc.push_back( pii(0, v[i]) ) ;
}
vc.back().first ++ ;
}
// O (N Log(N) ) sort by (cnt, value)
std::sort( vc.begin(), vc.end() ) ;
// O(N) restore they, reverse order.
v.clear();
for(int i = 0; i < (int)vc.size(); ++i){
int rev_i = vc.size() - i - 1;
int cnt = vc[rev_i].first;
for(int k = 0; k < cnt; ++k)
v.push_back( vc[rev_i].second ) ;
}
/////////////////////////
for(size_t i = 0; i != v.size(); ++i){
printf("%4d, ", v[i]);
}
printf("\n");
}
How to compare two vectors? Both cointains integer values:
void interaction(vehicles::position &pos, int number, enviroment object)
{
for (auto i = object.x.begin(); i<object.x.end(); i++)
for (auto j = object.y.begin(); j<object.y.end(); j++)
if (pos.x[number] == object.x[i] && pos.y[number] == object.y[j])
cout << "\nInteraction\n";
}
First vector (declared in class):
int remaining_move;
struct position{
vector<int> x;
vector<int> y;
}pos;
Second:
struct enviroment
{
vector<int> x;
vector<int> y;
string graphic;
};
Errors:
object.x[i]
This is wrong. It would work if i were a numeric index, but it's not: it's an iterator.
Instead:
*i
I therefore further suggest calling it something other than i; how about it?
Also, your inner loop condition is wrong. You wrote i, rather than j. Twice. [edit: and now you've fixed that in the question, for some reason]
I'd recommend sorting them first, so this is how I would do it:
std::sort(v1.begin(), v1.end());
std::sort(v2.begin(), v2.end());
std::vector<int> v3;
std::set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(v3));
If you're just looking for pos.x[number] and pos.y[number] in object.x and object.y, respectively, why not just:
auto itX = std::find(object.x.begin(), object.x.end(), pos.x[number]);
auto itY = std::find(object.y.begin(), object.y.end(), pos.y[number]);
if (itX != object.x.end() && itY != object.y.end()) {
std::cout << "\nInteraction\n";
}
To compare one vector element, you compare the value in the slots:
std::vector<int> a;
std::vector<int> b;
//...
if (a[0] == b[0])
{
// elements are equal
}
More elements can be compared by using a loop:
for (unsigned int i = 0; i < a.size(); ++i)
{
if (a[i] != b[i])
{
break;
}
}
One of the thorns is when the vectors are of different sizes. You'll have to decide on what your actions are on that topic.
There are probably some algorithms in <algorithm> that you can use on a vector.
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 have a vector of strings:
std::vector<std::string> fName
which holds a list of file names <a,b,c,d,a,e,e,d,b>.
I want to get rid of all the files that have duplicates and want to retain only the files that do not have duplicates in the vector.
for(size_t l = 0; l < fName.size(); l++)
{
strFile = fName.at(l);
for(size_t k = 1; k < fName.size(); k++)
{
strFile2 = fName.at(k);
if(strFile.compare(strFile2) == 0)
{
fName.erase(fName.begin() + l);
fName.erase(fName.begin() + k);
}
}
}
This is removing a few of the duplicate but still has a few duplicates left, need help in debugging.
Also my input looks like <a,b,c,d,e,e,d,c,a> and my expected output is <b> as all other files b,c,d,e have duplicates they are removed.
#include <algorithm>
template <typename T>
void remove_duplicates(std::vector<T>& vec)
{
std::sort(vec.begin(), vec.end());
vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
}
Note: this require that T has operator< and operator== defined.
Why it work?
std::sort sort the elements using their less-than comparison operator
std::unique removes the duplicate consecutive elements, comparing them using their equal comparison operator
What if i want only the unique elements?
Then you better use std::map
#include <algorithm>
#include <map>
template <typename T>
void unique_elements(std::vector<T>& vec)
{
std::map<T, int> m;
for(auto p : vec) ++m[p];
vec.erase(transform_if(m.begin(), m.end(), vec.begin(),
[](std::pair<T,int> const& p) {return p.first;},
[](std::pair<T,int> const& p) {return p.second==1;}),
vec.end());
}
See: here.
If I understand your requirements correctly, and I'm not entirely sure that I do. You want to only keep the elements in your vector of which do not repeat, correct?
Make a map of strings to ints, used for counting occurrences of each string. Clear the vector, then copy back only the strings that only occurred once.
map<string,int> m;
for (auto & i : v)
m[i]++;
v.clear();
for (auto & i : m)
if(i.second == 1)
v.push_back(i.first);
Or, for the compiler-feature challenged:
map<string,int> m;
for (vector<string>::iterator i=v.begin(); i!=v.end(); ++i)
m[*i]++;
v.clear();
for (map<string,int>::iterator i=m.begin(); i!=m.end(); ++i)
if (i->second == 1)
v.push_back(i->first);
#include <algorithms>
template <typename T>
remove_duplicates(std::vector<T>& vec)
{
std::vector<T> tvec;
uint32_t size = vec.size();
for (uint32_t i; i < size; i++) {
if (std::find(vec.begin() + i + 1, vec.end(), vec[i]) == vector.end()) {
tvec.push_back(t);
} else {
vec.push_back(t);
}
vec = tvec; // : )
}
}
You can eliminate duplicates in O(log n) runtime and O(n) space:
std::set<std::string> const uniques(vec.begin(), vec.end());
vec.assign(uniques.begin(), uniques.end());
But the O(log n) runtime is a bit misleading, because the O(n) space actually does O(n) dynamic allocations, which are expensive in terms of speed. The elements must also be comparable (here with operator<(), which std::string supports as a lexicographical compare).
If you want to store only unique elements:
template<typename In>
In find_unique(In first, In last)
{
if( first == last ) return last;
In tail(first++);
int dupes = 0;
while( first != last ) {
if( *tail++ == *first++ ) ++dupes;
else if( dupes != 0 ) dupes = 0;
else return --tail;
}
return dupes == 0 ? tail : last;
}
The algorithm above takes a sorted range and returns the first unique element, in linear time and constant space. To get all uniques in a container you might use it like so:
auto pivot = vec.begin();
for( auto i(find_unique(vec.begin(), vec.end()));
i != vec.end();
i = find_unique(++i, vec.end())) {
std::iter_swap(pivot++, i);
}
vec.erase(pivot, vec.end());