C++ Apply function to some elements in a container - c++

I would like to apply a function to some elements of an std::vector.I use std::includes to check if a "smaller" vector exists in a "bigger" one, and if exists I would like to apply a function to these elements of the "bigger" vector that are equal to the elements of the "smaller". Any suggestions?
Edit:
The following was incorrectly posted as an answer by the OP
There is a problem with std::search! It finds the first occurrence of a sequence contained in a vector while in my vector these elements are in several positions.Also i have a vector of objects!!!

Not sure what part you're having trouble with, but here's a simple example showing the range of elements contained in the larger vector that are identical to the contents of the smaller one being multiplied by 2. I used std::search instead of std::includes to determine whether the larger vector contains the range of elements in the smaller one because unlike includes, which returns a boolean result, search will return an iterator to the beginning of the contained range in the larger vector.
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
void times_two(int& t)
{
t *= 2;
}
int main()
{
std::vector<int> v1{1,2,3,4,5,6,7,8,9};
std::vector<int> v2{4,5,6};
// find if the larger vector contains the smaller one
auto first = std::search(v1.begin(), v1.end(), v2.begin(), v2.end());
if(first != v1.end()) {
// get the last element in the sub-range
auto last = std::next(first, v2.size());
// apply function to each sub-range element
std::for_each(first, last, times_two);
}
for(auto const& v : v1) {
std::cout << v << ' ';
}
std::cout << '\n';
}
Output:
1 2 3 8 10 12 7 8 9
Edit:
Here's an example that uses boost::find_nth to perform the search.

Related

How to sort an array(in ascending order) based on the ranks of each element which is in the another array?

Let two arrays
arr = [1,5,6,3,10]
rank = [100,0,1,100,2]
Based on the rank array (Which holds the ranks of the mirror elements in the arr) the result should be
[5,6,10,1,3]
The first element in the result is 5
(The index of 5 in arr is arr[1] and the index of 0 in rank is
rank[1]. This is how we have to take ranks. 0 is smallest of all ranks so 5 is printed first )
The second element in the result is 6
The index of 6 in arr array is at the index arr[2] and its rank is
1 because that is at the index rank[2]. 1 is the second smallest
of all ranks so 6 is printed next)
This is how we have to sort the array based on the rank array.
Here if two ranks are same then we have to compare the values in the array itself and print the smaller first
arr[0] = 1 and arr[3] = 3 Both having same ranks 100. So compare those elements and print the smallest value. That gives the
result
5,6,10,1,3
The short answer is: std::sort.
The long answer depends on how you store the values and the ranks, whether you can afford to copy them, and whether you also need the ranks sorted. For the following I assumed that you do not need the ranks sorted and the sorting is done on the original container of values. It is not the most efficient implementation, but it is simple enough to get you started:
#include <vector>
#include <utility>
#include <algorithm>
#include <iostream>
#include <iterator>
template <typename Iter,typename RankIter>
void rank_sort(Iter begin,Iter end,RankIter rankbegin){
std::vector<std::pair<typename std::iterator_traits<RankIter>::value_type,
typename std::iterator_traits<Iter>::value_type >> res;
for (auto beg = begin; beg != end; ++beg,++rankbegin) res.emplace_back(*rankbegin,*beg);
std::sort(res.begin(),res.end());
for (const auto& e : res){
*begin = e.second;
++begin;
}
}
int main() {
std::vector<int> arr{1,5,6,3,10};
std::vector<int> rank{100,0,1,100,2};
rank_sort(arr.begin(),arr.end(),rank.begin());
for (const auto& a : arr) { std::cout << a << " ";}
}
The basic idea is to create a std::vector<std::pair<int,int>> and then simply sort that via std::sort. The rest of the code is about copying the values and ranks into that vector and copying the values out of it after sorting.
If possible you should store the values and ranks in a std::vector<std::pair<int,int>> in the first place. Then sorting them is trivial. Alternatively, as mentioned in a comment, you can use a sorted container, like eg a std::map.

Using sort function to sort vector of tuples in a chained manner

So I tried sorting my list of tuples in a manner that next value's first element equals the second element of the present tuple.(first tuple being the one with smallest first element)
(x can be anything)
unsorted
3 5 x
4 6 x
1 3 x
2 4 x
5 2 x
sorted
1 3 x
3 5 x
5 2 x
2 4 x
4 6 x
I used the following function as my third argument in the custom sort function
bool myCompare(tuple<int,int,int>a,tuple<int,int,int>b){
if(get<1>(a) == get<2>(b)){
return true;
}
return false;
}
But my output was unchanged. Please help me fix the function or suggest me another way.
this can't be achieved by using std::sort with a custom comparison function. Your comparison function doesn't establish a strict weak order onto your elements.
The std::sort documentation states that the comparison function has to fulfill the Compare requirements. The Comparison requirements say the function has to introduce a strict weak ordering.
See https://en.wikipedia.org/wiki/Weak_ordering for the properties of a strict weak order
Compare requirements: https://en.cppreference.com/w/cpp/named_req/Compare
The comparison function has to return true if the first argument is before the second argument with respect to the strict weak order.
For example the tuple a=(4, 4, x) violates the irreflexivity property comp(a, a) == false
Or a=(4, 6, x) and b=(6, 4, y) violate the asymmetry property that if comp(a, b) == true it is not the case that comp(b, a) == true
I am not sure, where the real problem is coming from.
But the background is the Cyclic Permutation Problem.
In your special case you are looking for a k-cycle where k is equal to the count of tuples. I drafted a solution for you that will show all cycles (not only the desired k-cycle).
And I use the notation described int the provided link. The other values of the tuple are irrelevant for the problem.
But how to implement?
The secret is to select the correct container types. I use 2. For a cyle, I use a std::unordered_set. This can contain only unique elements. With that, an infinite cycle will be prevented. For example: 0,1,3,0,1,3,0,1,3 . . . is not possible, because each digit can only be once in the container. That will stop our way through the permutations. As soon as we see a number that is already in a cycle, we stop.
All found cycles will be stored in the second container type: A std::set. The std::set can also contain only unique values and, the values are ordered. Because we store complex data in the std::set, we create a custom comparator for it. We need to take care that the std::set will not contain 2 double entries. And double would be in our case also 0,1,3 and 1,3,0. In our custom comparator, we will first copy the 2 sets into a std::vector and sort the std::vectors. This will make 1,3,0 to 0,1,3. Then we can easily detect doubles.
Please note:
I do always only store a value from the first permutation in the cycle. The 2nd is used as helper, to find the index of the next value to evaluate.
Please see the below code. I will produces 4 non trivial cycles- And one has the number of elements as expected: 1,3,5,2,4.
Porgram output:
Found Cycles:
(1,3,5,2,4)(3,5,2,4)(2,4)(5,2,4)
Please digest.
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_set>
#include <iterator>
#include <set>
// Make reading easier and define some alies names
using MyType = int;
using Cycle = std::unordered_set<MyType>;
using Permutation = std::vector<MyType>;
using Permutations = std::vector<Permutation>;
// We do not want to have double results.
// A double cyle is also a Cycle with elements in different order
// So define custom comparator functor for our resulting set
struct Comparator {
bool operator () (const Cycle& lhs, const Cycle& rhs) const {
// Convert the unordered_sets to vectors
std::vector<MyType> v1(lhs.begin(), lhs.end());
std::vector<MyType> v2(rhs.begin(), rhs.end());
// Sort them
std::sort(v1.begin(), v1.end());
std::sort(v2.begin(), v2.end());
// Compare them
return v1 < v2;
}
};
// Resulting cycles
using Cycles = std::set<Cycle, Comparator>;
int main() {
// The source data
Permutations perms2 = {
{3,4,1,2,5},
{5,6,3,4,2} };
// Lamda to find the index of a given number in the first permutation
auto findPos = [&perms2](const MyType& m) {return std::distance(perms2[0].begin(), std::find(perms2[0].begin(), perms2[0].end(), m)); };
// Here we will store our resulting set of cycles
Cycles resultingCycles{};
// Go through all single elements of the first permutation
for (size_t currentColumn = 0U; currentColumn < perms2[0].size(); ++currentColumn) {
// This is a temporary for a cycle that we found in this loop
Cycle trialCycle{};
// First value to start with
size_t startColumn = currentColumn;
// Follow the complete path through the 2 permutations
for (bool insertResult{ true }; insertResult; ) {
// Insert found element from the first permutation in the current cycle
const auto& [newElement, insertOk] = trialCycle.insert(perms2[0][startColumn]);
// Find the index of the element under the first value (from the 2nd permutation)
startColumn = findPos(perms2[1][startColumn]);
// Check if we should continue (Could we inster a further element in our current cycle)
insertResult = insertOk && startColumn < perms2[0].size();
}
// We will only consider cycles with a length > 1
if (trialCycle.size() > 1) {
// Store the current temporary cycle as an additional result.
resultingCycles.insert(trialCycle);
}
}
// Simple output
std::cout << "\n\nFound Cycles:\n\n";
// Go through all found cycles
for (const Cycle& c : resultingCycles) {
// Print an opening brace
std::cout << "(";
// Handle the comma delimiter
std::string delimiter{};
// Print all integer values of the cycle
for (const MyType& m : c) {
std::cout << delimiter << m;
delimiter = ",";
}
std::cout << ")";
}
std::cout << "\n\n";
return 0;
}

lower_bound() in C++

From reading from the Internet, I understand that The lower_bound() method in C++ is used to return an iterator pointing to the first element in the range [first, last) which has a value not less than value. This means that the function returns the index of the next smallest number just greater than that number.
So, for the given code below I understood that the output is 3. But, as there is repetition of 6. How can I get the index of last 6 using lower_bound(). I can implement my own binary_search() for that, but I want to know how to do it by lower_bound().
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main ()
{
int array[] = {5,6,7,7,6,5,5,6};
vector<int> v(array,array+8); // 5 6 7 7 6 5 5 6
sort (v.begin(), v.end()); // 5 5 5 6 6 6 7 7
vector<int>::iterator lower,upper;
lower = lower_bound (v.begin(), v.end(), 6);
upper = upper_bound (v.begin(), v.end(), 6);
cout << "lower_bound for 6 at position " << (lower- v.begin()) << '\n';
return 0;
}
Use pair of lower_bound and upper_bound. Or one equal_range -- that would be more optimal.
Both upper_bound and high part of equal_range would be past the last "6". The same as end is not last, it is past the last.
You can use reverse iterators into the vector, but then to fulfill the ordering requirement for std::lower_bound you need to inverse the comparison, so you need to use std::greater instead of the default std::less. This however also means that now you are not really looking for a lower bound, but for an upper bound with respect to that comparison function, so:
auto upper = std::upper_bound(v.rbegin(), v.rend(), 6, std::greater{});
If the array is sorted, iterating between lower_bound and upper_bound you get all elements which equal your pivot point:
lower = lower_bound(v.begin(), v.end(), 6);
upper = upper_bound(v.begin(), v.end(), 6);
for (auto it = lower; it != upper; it++) {
assert(6 == *it);
}
The question you are asking, i.e. what is the index of the last 6, doesn't have a corresponding function in the standard library because is ill-defined in the case when the range doesn't contain any 6. In all other cases since you have a random access container you can get an iterator to the last 6 by removing one from upper_bound (upper - 1 in your code), in the same way you get the last index of an array by removing 1 from length.
However I suggest you avoid relying on the position of the last element equal when you design your algorithm. Also note that if you need both lower and upper bound you can get both at the same time with equal_range, which may even perform better because it may be optimised to only traverse the data structure once:
std::tie(lower,upper) = equal_range(v.begin(), v.end(), 6);
for (auto it = lower; it != upper; it++) {
assert(6 == *it);
}
You can use lower_bound again, updating the begin and the value:
auto lower = std::lower_bound (v.cbegin(), v.cend(), 6);
auto upper = std::lower_bound (lower, v.cend(), 6 + 1);
std::cout << "Number of values found: " << std::distance(lower, upper) << '\n';

Set_Intersection with repeated values

I think the set_intersection STL function described here: http://www.cplusplus.com/reference/algorithm/set_intersection/
is not really a set intersection in the mathematical sense. Suppose that the examples given I change the lines:
int first[] = {5,10,15,20,20,25};
int second[] = {50,40,30,20,10,20};
I would like to get 10 20 20 as a result. But I only get unique answers.
Is there a true set intersection in STL?
I know it's possible with a combination of merges and set_differences, btw. Just checking if I'm missing something obvious.
I would like to get 10 20 20 as a result. But I only get unique answers. Is there a true set intersection in STL?
std::set_intersection works how you want.
You probably get the wrong answer because you didn't update the code properly. If you change the sets to have 6 elements you need to update the lines that sort them:
std::sort (first,first+5); // should be first+6
std::sort (second,second+5); // should be second+6
And also change the call to set_intersection to use first+6 and second+6. Otherwise you only sort the first 5 elements of each set, and only get the intersection of the first 5 elements.
Obviously if you don't include the repeated value in the input, it won't be in the output. If you change the code correctly to include all the input values it will work as you want (live example).
cplusplus.com is not a good reference, if you look at http://en.cppreference.com/w/cpp/algorithm/set_intersection you will see it clearly states the behaviour for repeated elements:
If some element is found m times in [first1, last1) and n times in [first2, last2), the first std::min(m, n) elements will be copied from the first range to the destination range.
Even the example at cplusplus.com is bad, it would be simpler, and harder to introduce your bug, if it was written in idiomatic modern C++:
#include <iostream> // std::cout
#include <algorithm> // std::set_intersection, std::sort
#include <vector> // std::vector
int main () {
int first[] = {5,10,15,20,20,25};
int second[] = {50,40,30,20,10,20};
std::sort(std::begin(first), std::end(first));
std::sort(std::begin(second), std::end(second));
std::vector<int> v;
std::set_intersection(std::begin(first), std::end(first),
std::begin(second), std::end(second),
std::back_inserter(v));
std::cout << "The intersection has " << v.size() << " elements:\n";
for (auto i : v)
std::cout << ' ' << i;
std::cout << '\n';
}
This automatically handles the right number of elements, without ever having to explicitly say 5 or 6 or any other magic number, and without having to create initial elements in the output vector and then resize it to remove them again.
set_intersection requires both ranges to be sorted. In the data you've given, second is not sorted.
If you sort it first, you should get your expected answer.

Find elements in a vector which lie within specified ranges

I have a vector of integer elements in sorted. An example is given below:
vector<int> A ={3,4,5,9,20,71,89,92,100,103,109,110,121,172,189,194,198};
Now given the following "start" and "end" ranges I want to find out which elements of vector A fall into the start and end ranges.
int startA=4; int endA=8;
int startB=20; int endB=99;
int startA=120; int endC=195;
For example,
elements lying in range startA and startB are: {4,5}
elements lying in range startA and startB are: {20,71,89,92}
elements lying in range startC and startC are: {121,172,189,194}
One way to do this is to iterate over all elements of "A" and check whether they lie between the specified ranges. Is there some other more efficient way to find out the elements in the vector satisfying a given range
One way to do this is to iterate over all elements of "A" and check whether they lie between the specified ranges. Is there some other more efficient way to find out the elements in the vector satisfying a given range
If the vector is sorted, as you have shown it to be, you can use binary search to locate the index of the element that is higher than the lower value of the range and index of element that is lower than the higher value of the range.
That will make your search O(log(N)).
You can use std::lower_bound and std::upper_bound, which requires the container to be partially ordered, which is true in your case.
If the vector is not sorted, linear iteration is the best you can do.
If the vector is sorted all you need to do is to use dedicated functions to find your start range iterator and end range iterator - std::lower_bound and std::upper_bound. Eg.:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> A ={3,4,5,9,20,71,89,92,100,103,109,110,121,172,189,194,198};
auto start = std::lower_bound(A.begin(), A.end(), 4);
auto end = std::upper_bound(A.begin(), A.end(), 8);
for (auto it = start; it != end; it++) {
std::cout << *it << " ";
}
std::cout << std::endl;
}
//or the C++1z version (works in VS2015u3)
int main() {
std::vector<int> A ={3,4,5,9,20,71,89,92,100,103,109,110,121,172,189,194,198};
std::copy(std::lower_bound(A.begin(), A.end(), 4),
std::upper_bound(A.begin(), A.end(), 8),
std::ostream_iterator<int>(cout, " "));
std::cout << std::endl;
}
This however will work only if startX <= endX so you may want to test the appropriate condition before running it with arbitrary numbers...
Searching bound iterators using std::lower_bound and std::upper_bound will cost O(log(N)) however it has to be stated that iterating through the range of elements in average case is O(N) and the range may contain all the elements in your vector...
The best way I can think is to apply modified binary search twice and find two indices in the vector arr and then print all items in between this range . Time complexity will be O(log n).
A modified form of binary search looks like:(PS its for arrays, also applicable for vector):
int binary_search(int *arr,int start,int end,int key)
{
if(start==end)
{
if(arr[start]==key){return start+1;}
else if(arr[start]>key&&arr[start-1]<=key){return start;}
else return 0;
}
int mid=(start+end)/2;
if(arr[mid]>key && arr[mid-1]<=key)return mid;
else if(arr[mid]>key)return binary_search(arr,start,mid-1,key);
else return binary_search(arr,mid+1,end,key);
}
If range of integers of vector A is not wide, bitmap is worth the consideration.
Let's assume all integers of A are positive and are in between 0 ... 1024, the bitmap can be built with:
#include <bitset>
// ...
// If fixed size is not an option
// consider vector<bool> or boost::dynamic_bitset
std::bitset<1024> bitmap;
for(auto i : A)
bitmap.set(i);
That takes N iterations to set bits, and N/8 for storing bits. With the bitmap, one can match elements as follows:
std::vector<int> result;
for(auto i = startA; i < endA; ++i) {
if (bitmap[i]) result.emplace_back(i);
}
Hence speed of the matching depends on size of range rather than N. This solution should be attractive when you have many limited ranges to match.