Note: While writing this question, I think I already found the answer. Feel free to ammend or append it with a better version. I thought it might be nice to document my problem. edit I was wrong, my aswer was not correct.
Considering a list of integer pairs: I'd like to topologically sort them based on a partial ordering. This is similar to Is partial-order, in contrast to total-order, enough to build a heap? , but I'd like to use std::sort instead of std::priority_queue.
To do so I wrote this piece of code:
#include <iostream>
#include <vector>
#include <algorithm>
struct pair {
int a, b;
pair(int a, int b) : a(a), b(b) {}
std::ostream &print(std::ostream &out) const {
return (out << "(" << a << ", " << b << ")");
}
};
std::ostream &operator<<(std::ostream &out, const pair &p) { return p.print(out); }
struct topological_pair_comparator {
bool operator()(const pair &p, const pair &q) const { return p.a<q.a && p.b<q.b; }
} tpc;
std::vector<pair> pairs = {
pair(1,1),
pair(1,2),
pair(2,1),
pair(3,1),
pair(1,3),
pair(5,5),
pair(2,2),
pair(4,0)
};
int main() {
std::sort(pairs.begin(), pairs.end(), tpc);
for(const pair &p : pairs) std::cout << p << " ";
std::cout << std::endl;
return 0;
}
Source: http://ideone.com/CxOVO0
Resulting in:
(1, 1) (1, 2) (2, 1) (3, 1) (1, 3) (2, 2) (4, 0) (5, 5)
Which is pretty much topologially sorted (proof by example ;).
However, the partial ordering creates that !((1,2) < (2,1)) and !((1,2) > (2,1)) according to the tpc, and hence one may conclude (1,2) == (2,1). However, paragraph 25.4.3 of the c++ standard (January 2012 working draft) states:
For all algorithms that take Compare, there is a version that uses operator< instead. That is, comp(*i,
*j) != false defaults to *i < *j != false. For algorithms other than those described in 25.4.3 to work
correctly, comp has to induce a strict weak ordering on the values.
Edited: According to ecatmur 's valid answer:
A partial ordering is not necessarily a strict weak ordering; it breaks the transitivity of incomparibility. So I'd like to drop my reasoning that a partial ordering is always a strict weak ordering and the associated questions, and add the question: am I doomed to write my own topological sorting algorithm or use the boost implementation which requires me to build the graph?
Solution: A smart suggestion of ecatmur:
struct topological_pair_comparator {
bool operator()(const pair &p, const pair &q) const { return (p.a + p.b) < (q.a + q.b); }
} tpc;
Source: http://ideone.com/uoOXNC
Please note, the SO about heaps does not explicitely mention that std::sort sorts topologically; except for one comment, which is not backed up by argumentation.
Consider the values
pair
x{0, 1},
y{2, 0},
z{1, 2};
Here,
!tpc(x, y) && !tpc(y, x);
!tpc(y, z) && !tpc(z, y);
However,
tpc(x, z);
Thus your comparator does not impose a strict weak ordering, and behavior is undefined if you use it with std::sort or in any other role where a strict weak ordering is required.
A comparator that is strict weak and is a refinement of your comparator would be:
struct refined_comparator {
bool operator()(const pair &p, const pair &q) const { return p.a + p.b < q.a + q.b; }
} rc;
Related
I want to find the minimum element of a filtered list. In Python, I would write:
it = (x for x in [1, 8, 4, 3] if x % 2 == 0)
min(it, default=None)
I hoped that the c++ equivalent would read something like:
const std::vector<int> array {1, 8, 4, 3};
const auto arr_end = std::end(array);
auto it = std::find_if(std::begin(array), arr_end, [](int value) { return value % 2 == 0; });
auto jt = std::min_element(it, arr_end);
if (jt != arr_end) {
std::cout << "Min even element is: " << *jt << std::endl;
} else {
std::cout << "No even element exists!" << std::endl;
}
The expected result is 4, but of course the actual result is 3. The reason: find_if skips to 8. Then from 8 to end the min element is chosen, which is 3.
My question: Is there a way to create an iterator over all even values that can be used to find the minimum element? I am not allowed to use boost, create a copy or to write to array. We are using c++17.
There isn't an answer in std as of C++17. In C++20 you can use std::ranges::filter_view, outside of std you can use ranges::filter_view from the range-v3 library, which was the demonstration implementation for the C++20 ranges proposal.
auto filtered = ranges::filter_view(array, [](int value) { return value % 2 == 0; });
auto it = std::min_element(filtered.begin(), filtered.end());
if (it != filtered.end()) {
std::cout << "Min even element is: " << *jt << std::endl;
} else {
std::cout << "No even element exists!" << std::endl;
}
My question: Is there a way to create an iterator over all even values that can be used to find the minimum element?
Yes!
It's slightly unfortunate that you're limited to C++17 with no Boost, because you ideally want ranges - specifically ranges::filter_view etc. which was added in C++20, and preceded by the Boost.Range library.
You may possibly be able to use the intermediate experimental range extension.
If none of those are viable, you can of course write your own filtered_iterator to use with std::min_element.
It's not much fun: although it's probably more reusable (and easier to test) than encoding all the logic into a single lambda, it's a lot of work if you're not planning to reuse it. Also, C++ iterators aren't ideally suited to emulating a Python-style generator, as demonstrated by the redundant end iterator e_ and the copy-assignment operator. You can't elide the end & predicate members of the filtered end iterator either, because both iterators usually need to be the same type.
template <typename BaseIterator, typename UnaryPredicate>
class filter_iterator
{
BaseIterator i_;
BaseIterator e_;
UnaryPredicate pred_;
public:
using reference = typename std::iterator_traits<BaseIterator>::reference;
using value_type = typename std::iterator_traits<BaseIterator>::value_type;
filter_iterator(filter_iterator &&) = default;
filter_iterator(filter_iterator const&) = default;
filter_iterator(BaseIterator i, BaseIterator e, UnaryPredicate p)
: i_(i), e_(e), pred_(p)
{}
filter_iterator& operator=(filter_iterator &&) = default;
filter_iterator& operator=(filter_iterator const& other) {
i_ = other.i_;
e_ = other.e_;
// This is questionable, because we can't copy the predicate without adding
// a level of indirection (ie, always wrapping it in std::function).
// For now, just assume it is stateless for convenience.
return *this;
}
bool operator==(filter_iterator const& other) const
{
return i_ == other.i_;
}
filter_iterator& operator++() {
// We could check i_ is not already e_ here,
// but the caller is required to check this outside anyway
i_ = find_if(next(i_), e_, pred_);
return *this;
}
filter_iterator operator++(int) const {
filter_iterator i(*this);
++i;
return i;
}
reference operator*() { return *i_; }
std::add_const_t<reference> operator*() const { return *i_; }
};
template <typename BaseIterator, typename UnaryPredicate>
bool operator!=(filter_iterator<BaseIterator, UnaryPredicate> const& a,
filter_iterator<BaseIterator, UnaryPredicate> const& b)
{
return !(a == b);
}
Then the wrapper function hides most of this ugliness for us:
template <typename BaseIterator, typename UnaryPredicate>
std::pair<filter_iterator<BaseIterator, UnaryPredicate>,
filter_iterator<BaseIterator, UnaryPredicate>>
filter(BaseIterator b, BaseIterator e, UnaryPredicate p)
{
using f = filter_iterator<BaseIterator, UnaryPredicate>;
auto fbegin = find_if(b, e, p);
return {f{fbegin, e, p}, {e, e, p}};
}
and we can use it like:
int main() {
std::vector<int> a {7, 1, 8, 4, 3, 2};
auto be = filter(a.begin(), a.end(),
[](int i){ return (i%2) == 0;});
auto min = std::min_element(be.first, be.second);
return *min;
}
If you are limited at c++17 there is no solution without making a copy.
If you can transition to C++ 20 the solution is pretty easy. C++ 20 introduced the std::views concept and added the <ranges> library. The concept of std::view is to not create a copy of the underlying container, and it does not modifies the actual values of the container. Behind the scenes the views are actually iterators(actually it is a bit more but lets stay at the basics)
So in your case you could something like this
const std::vector<int> array {1, 8, 4, 3};
auto isEven = [](auto i) { return i % 2 == 0; };
//This is actually an iterator pair(begin, end)
//No copies of the container ever made, the container does not change
auto filtered = array | std::views::filter(isEven);
auto min = std::ranges::min_element(filtered );
if (min != filtered .end())
std::cout << "Min " << *min << std::endl;
else
std::cout << "No min\n";
//You can try to print the vector, it will be unchanged!!!
std::find_if does not filter the vector. It only returns the first element for which the predicate is true. I suppose there is an elegant solution using ranges. The rather inelegant way is to use a custom comparator with min_element:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
const std::vector<int> array {1, 8, 4, 3};
std::vector<float> x;
if (array.size()) {
auto it = std::min_element(begin(array),end(array),
[](auto a, auto b){
if ((a % 2) && (b % 2)) return a < b;
if (a % 2) return false;
if (b % 2) return true;
return a < b;
});
if (*it % 2 == 0) std::cout << *it;
}
}
Odd elements are considered to be not < than other elements. When both are odd or both are even the "normal" < is used. Output is:
4
Note that I have to check if (*it % 2 == 0) because when there is no even element then the call to min_element will return an iterator to the smallest odd element.
PS: The tricky part of custom comparators is to get strict weak ordering correct. The above comparator can be written in a more concise way (thanks to Jarod42) like this:
return std::tuple{ bool{a%2} , a} < std::tuple{ bool{b%2} , b};
Tuples have a operator< that implements a strict weak ordering (given that the elements type provide one), hence writing it this way it is much easier to convice yourself that the comparator really is a strict weak ordering.
#include <unordered_set>
#include <iostream>
class edge{
public:
float a1;
float a2;
};
struct comp{
bool operator()(const edge& e1, const edge& e2) const {
return true;
return (
(e1.a1==e2.a1 && e1.a2==e2.a2) ||
(e1.a1==e2.a2 && e1.a2==e2.a1)
);
};
};
struct hash{
size_t operator()(const edge& e1) const {
// return std::hash<float>()(e1.a1+e1.a2);
return std::hash<float>()(e1.a1+e1.a2*2);
};
};
int main() {
std::unordered_set<edge,hash,comp> s1;
s1.insert(edge{1.1,2.2});
s1.insert(edge{2.2,1.1});
for( auto& it : s1 ) {
std::cout << it.a1 << " " << it.a2 << "\n";
}
std::cout << "s1.size " << s1.size() << "\n";
}
I realize that if different element has same hash value, then they are considered equal, but I just want this unordered_set use comparator that I define, just ignore hash?
How to achieve that?
I know i can use set, but using set need to consider order, if a < b is true and b < a is also true, then this element will not be inserted successfully, Sometimes, It is hard to provide order.
If anyone can help, much appreciated
edited:
My intention is to let two edges called e1,e2, they are same if (e1.a1==e2.a1&&e1.a2==e2.a2)or(e1.a1==e2.a2 && e1.a2==e2.a1) as I provided
in struct comp.
but when i test. it seems hash function can change the comparison too. Someone says the way I define hash and comparator result in undefined behaviour.
Is that true? why?
if true, how to solve this? I just want comparator decide which one is satisfied to be inserted in unordered_set without duplicate. And really do not care about hash.
BTW, thanks for some many people replying
If you want to handle edge.a1 and edge.a2 interchangeably, you have to implement a hashing function that returns the same value even when they are swapped. I advise against using addition, because addition may not be commutative for floats, but you could sort them by size and combine the hashes afterwards:
struct hash {
size_t operator()(const edge& e1) const {
auto h1 = std::hash<float>{}(std::min(e1.a1, e1.a2));
auto h2 = std::hash<float>{}(std::max(e1.a1, e1.a2));
return h1 ^ (h2 << 1)
};
};
This only makes sense for pretty large sets of floats, because otherwise the hashing overhead probably exceeds the benefit of using a hashed data structure in the first place.
Old answer for reference:
Objects with the same hash are not considered equal in
unordered_set. They are just stored in the same bucket. There is a
KeyEqual template parameter for the comparison, which by
default uses the operator== of your Key. So your main problem is,
that comp should implement e1 == e2 and not e1 < e2 (and should
probably be called equal).
The hash is just used to speed up the search, insertion, and removal
of elements.
On another note, you may want to use the hashes of the member
variables instead of the values themselves to compute the hash of
edge:
struct hash {
size_t operator()(const edge& e1) const {
auto h1 = std::hash<float>{}(e1.a1);
auto h2 = std::hash<float>{}(e1.a2);
return h1 ^ (h2 << 1)
};
};
This way, you won't get the same hash for two edges with swapped
coordinates. This way of combining hashes is suggested here (but
is not a good way to combine more than two).
You don't have to use the members of edge to provide the hash. It is only required that equal values have equal hashes. A "always valid" hash is
struct hash{
size_t operator()(const edge& e1) const {
return 0;
};
};
But it seems your original attempt is better
struct hash{
size_t operator()(const edge& e1) const {
return std::hash<float>{}(e1.a1 + e1.a2); // + is commutative
};
};
I need a structure in which I can insert elements, have no duplicate, use a custom comparer and have the smallest element first. I tried using std::priority_queue, but the problem is that I get a lot of duplicates and I run out of space. So I thought about using std::set : std::set< std::pair<Coordinates, int>, Compare> positions; where
Coordinates
{
public:
Coordinates(int x = 0, int y = 0, char tool = 'T') : x(x), y(y), tool(tool) {}
public:
int x, y;
char tool;
};
class Compare
{
public:
bool operator() (const std::pair<Coordinates, int>& c1, const std::pair<Coordinates, int>& c2) const
{
return c1.second < c2.second;
}
};
I want the elements to be sorted based on the second element of pair, which this implementation is doing, but the problem is that it is using the same comparer when inserting new pairs and I get duplicates. My question is: Is it possible to make the std::set to not allow duplicates also to order the elements based on the second element of pair?
Edit: Eliminated some code that was not necessary and changed in Compare > with <
Using your Comparer the set will contain only unique values of the int, since Coordinates isn't participating in the comparison at all.
std::set uses operator < for sorting as well as equality; equality is determined as !(a<b || b<a). Therefore operator < should take into account every attribute which makes the element unique.
You can specialize std::less for your type like this:
namespace std {
template<>
struct less<pair<Coordinates, int>> {
bool operator()(const pair<Coordinates, int>& a, const pair<Coordinates, int>& b) const {
return tie(a.second, a.first.x, a.first.y) < tie(b.second, b.first.x, b.first.y);
}
};
}
Then std::set< std::pair<Coordinates, int>> positions; should work.
The issue here is that since you only look at second in you comparator, you can only store pairs that have unique values for second. This is because the set only uses the comparator to compare the elements. It doesn't use your operator == to check for equality but instead does cmp(a, b) == cmp(b, a)1 to test if the values are equal.
If you wan to sort by second, but allow other points with the same second but different other values then you need to add those values into you comparator. The easiest way to do that is to use std::tie to build a couple of tuples and use the tuples operator < which "does the right thing". That would look like
class Compare
{
public:
bool operator() (const std::pair<Coordinates, int>& c1, const std::pair<Coordinates, int>& c2) const
{
return std::tie(c1.second, c1.first.x, c1.first.y) < std::tie(c2.second, c2.first.x, c2.first.y);
}
};
1: If a is not less then b, and b is not less than a then a and b must be equal
As stated, the issue was that you only looked at the second member of the pair, so the set didn't care if the Coordinates were different. You simply needed to include the Coordinates in your comparison.
Unlike the other answers, this one utilizes a lambda for the comparison. I prefer it over std::tie and mucking around with std overrides. It also saves you the trouble of writing up a functor yourself like you did with your Compare class.
#include <iostream>
#include <set>
class Coordinates {
public:
Coordinates(int x = 0, int y = 0, char tool = 'T') : x(x), y(y), tool(tool) {}
int x, y;
char tool;
};
int main() {
using CoordPair = std::pair<Coordinates, int>;
auto compare = [](const CoordPair& a, const CoordPair& b) {
if (a.second != b.second)
return a.second < b.second;
// Replace this with some method of comparing Coordinates
return a.first.x != b.first.x || a.first.y != b.first.y;
};
std::set<std::pair<Coordinates, int>, decltype(compare)> list(compare);
list.emplace(Coordinates(1, 1), 2);
list.emplace(Coordinates(2, 0), 2);
list.emplace(Coordinates(1, 1), 3);
list.emplace(Coordinates(1, 1), 2); // Shouldn't show up
for (auto i : list)
std::cout << '(' << i.first.x << ", " << i.first.y << ", " << i.first.tool
<< ')' << ", " << i.second << '\n';
}
Your C++ version wasn't specified, this needs at least C++11.
I have just started learning C++ programming a month or so ago. I am having a great difficulty in ranking and printing out the output based on the ranking. I followed some of the ideas posted in the forum and my code is below. I have no idea of what I have missed and how the code works. What I am trying to do is to sort out the player_data[5] in ascending order based on the attempt field and then sort out again the player_data[5] with time elapsed where the order of the array is based on the attempt and then time elapsed if the attempt is the same. After I sort out the structure of array, I want to cout based on the ranking. Would someone tell me what I am missing and give a brief explanation on the code itself. TIA
#include <algorithm>
using namespace std;
bool player_sorter(player_score const& lhs,player_score const& rhs);
struct player_score
{
char name[31];
int num_attempt;
time_t time_elapsed;
} player_data[5];
bool player_sorter(player_score const& lhs, player_score const& rhs)
{
if (lhs.num_attempt != rhs.num_attempt)
return lhs.num_attempt < rhs.num_attempt;
if (lhs.time_elapsed != rhs.time_elapsed)
return lhs.time_elapsed < rhs.time_elapsed;
}
The std::sort functions works with standard containers, as far as I know it doesn't with C-style array. You should define your type:
typedef struct player_score
{
char name[31];
int num_attempt;
time_t time_elapsed;
} player_score;
And then declare the actual container of your data:
std::vector<player_score> player_data;
Once your filled your container you can sort it with
std::sort(player_data.begin(), player_data.end(), player_sorter);
The function sort is an implementation of a sorting algorithm, maybe a Quicksort. If you call it like this you are telling the function that you want to sort from the beginning to the end, the whole container. The third argument is the function that performs the < comparison and it is fundamental to determine if an element goes before or after the other.
Also, player_sorter must return a value even if the two player_score are equal, you should add a return false to the end of the function, because in that case the first operand is not strictly less than the second but it is equal. In the case the operator were <= you would return true.
std::sort provides "good enough" sorting speed and complexity for most cases. Here is an example using a different struct and std::sort and several ways to sort.
struct foo {
int a;
int *b = nullptr;
bool operator<(const foo & other) const {
return (a < other.a);
}
}
void printvec(const std::vector<foo> & vec) {
for ( foo & f : vec ) {
std::cout << f.a << "\t" << (void*)f.b;
if ( f.b ) {
std::cout << "\t" << *f.b;
}
std::cout << "\n";
}
}
bool sort_foos_on_b(const foo & a, const foo & b) {
// if both are nullptr
if ( (nullptr == a.b) && (nullptr == b.b) ) {
return false;
}
// if one is nullptr
if ( (nullptr == a.b) != (nullptr == b.b) ) {
return (nullptr != a.b);
}
return (*a.b) < (*b.b);
}
void bar() {
const constexpr size_t MAX_FOO = 20;
std::default_random_engine generator;
std::uniform_int_distribution<int> rng(0,MAX_FOO-1);
std::vector<foo> vec(MAX_FOO);
// initialize
for ( foo & f : vec ) {
f.a = rng(generator);
if ( f.a & 1 ) {
f.b = &f.a;
}
}
// uses foo::operator<()
std::sort(vec.begin(), vec.end());
printvec(vec);
// uses lambda
std::sort(vec.begin(), vec.end(), [](const foo & a, const foo & b) -> bool {
// sorting on pointer because why not
return (f.a < f.b);
});
printvec(vec);
// uses explicit sort function
std::sort(vec.begin(), vec.end(), sort_foos_on_b);
printvec(vec);
}
bool foo::operator<() const is used to sort on foo::a.
The lambda is used to sort based on the pointer value of foo::b.
The explicit sort function is used to sort based on the nullness of foo::b and, if both are non-null, then the value pointed to by foo::b.
You can see tradeoffs for each. Using operator< will provide a default method of sorting (not needing to specify a sorting function to std::sort). The lambda allows you to customize your sorting right where you're using it. The explicit sorting function would be used if you need to sort the same way from different locations (instead of repeating the same lambda everywhere).
disclaimer: I wrote above code snippet from memory and don't have access to a compiler at the very moment so it might not compile "as-is"
I want to reverse the contents of a std::set(Not just iterating theough it in reverse, but reversing the contents iteslf). I found that std::set takes the compare as a function object for its constructor. Hence I came up with the following code to do the same:
#include <set>
using namespace std;
struct Comparator
{
Comparator(bool g) : m_g(g){}
bool operator()(int i1, int i2) const
{
if(m_g)
{
return i1>i2;
}
return i1<i2;
}
bool m_g;
};
int main(int argc, char** argv)
{
Comparator l(false);
set<int,Comparator> a(l);
a.insert(1);
a.insert(2);
a.insert(3);
Comparator g(true);
set<int,Comparator> b(g);
copy(a.begin(), a.end(), inserter(b, b.begin()));
a = b;
return 0;
}
This seems to work in VC9. But is this code correct? My doubt arises due to the fact my Comparator has state associated with it. Are comparators are allowed to have states?
Yes, that's legal. Consider that if the comparator was not allowed to have state, there would be no point in allowing you to pass a comparator as a constructor parameter. :)
As long as the comparator provides a strict weak ordering, it's fine (which, among other things, means that it has to be consistent. You can't change the state of it halfway through, so that it orders elements differently)
It's fine, but it's needlessly complex.
You can just use std::less (the default value for that template parameter!) or std::greater from the standard library. They are provided by <functional>.
A more generic solution. boost::assign and c++11 just for convenience (and the funny auto reverse)
# include <iostream>
# include <set>
# include <boost/assign.hpp>
using namespace boost::assign;
template <typename CL , typename Pred>
struct revPred {
revPred (Pred pred) : pred_(pred) {}
bool operator()(const CL & a, const CL& b)
{
return pred_(b,a);
}
Pred pred_;
};
template <typename CL , typename Pred, typename alloc>
inline
std::set<CL,revPred<CL,Pred>,alloc> reverseSet(const std::set<CL,Pred,alloc> & set) {
std::set<CL,revPred<CL,Pred>,alloc> res(revPred<CL,Pred>(set.key_comp()));
std::copy(set.begin(), set.end(), std::inserter(res, res.begin()));
return res;
}
int main()
{
std::set<int> s; s += 0 , 1 , 2 , 3;
std::for_each(s.begin(), s.end(), [](int x) { std::cout << x << " "; });
std::cout << std::endl;
auto reverse = reverseSet(s);
std::for_each(reverse.begin(), reverse.end(), [](int x) { std::cout << x << " "; });
std::cout << std::endl;
return 0;
}
There is nothing wrong with your code.
And there is nothing wrong with comparators having state.
This is ok since your comparison doesn't change dynamically and it does provide strict weak ordering.
However, if you're doing this so that the type of the set is the same even when the order changes, I might suggest an alternate idea. Instead of this comparison, you use two different set types with std::less and std::greater and use an iterator interface like the standard library does, rather than a container interface that depends on all the template parameters.
And finally as noted in the answer from #parapura rajkumar you should use the iterator pair constructor rather than std::copy:
// Assuming my other comments don't apply, modify as needed if they do:
Comparator g(true);
set<int, Comparator> b(a.rbegin(), a.rend(), g);
You don't need to provide a function object which maintains state. Just use a normal function and it should do the job. Pls. don't mind for the use of lambda. Used as a short cut to do the printing.
typedef bool (*Cmp)(int x, int y);
bool Compare(int x, int y)
{
return x < y;
}
bool CompareR(int x , int y)
{
return !Compare(x, y);
}
void SetReverse()
{
std::set<int, Cmp> s1(Compare);
s1.insert(1);
s1.insert(3);
s1.insert(2);
s1.insert(4);
std::for_each(s1.begin(), s1.end(), [](int x) { std::cout << x << "\n"; });
std::set<int, Cmp> s2(CompareR);
s2.insert(s1.begin(), s1.end());
std::for_each(s2.begin(), s2.end(), [](int x) { std::cout << x << "\n"; });
s1 = s2;
}
If you have a large enough set you have already paid the O(NlogN) penalty to make a balanced binary tree. A blind insert into the destination set, you will have to pay the penalty again.
Consider using one of these insert overload.
void insert ( InputIterator first, InputIterator last );
iterator insert ( iterator position, const value_type& x );
The range insert has linear complexity if [ first , last ) are sorted already.