Test for symmetry in relation using sets - c++

I am working with ordered pairs of type unsigned typedef pair<unsigned, unsigned> OP;, and sets of ordered pairs typedef set<OP> SOP;.
The ultimate goal of my program is to check if the set(relation) is an equivalence relation.
My problem: I have managed to check if a set is reflexive but currently I am trying to check if an ordered pair in the set (relation) is symmetric. I have currently constructed two for loops to compare the ordered pairs against each other but have struck a dead end in my comparison.
My code:
for (auto it3 = sop.begin(); it3 != sop.end(); it3++) { // loop through each pair in set
for (auto it4 = sop.begin(); it4 != sop.end(); it4++) { // compare with other pairs
// make sure first and second items in pair are different
while (it3->first != it3->second) {
//If the case is that there is not an instance of
//symmetric relation return false
if (!((it3->first == it4->second) && (it3->second == it4->first))) {
return false;
}
}
}
}

Your looping logic is completely flawed.
The inner while does not change neither it3 nor it4. So either it will return false or it will loop forever. In addition, the inner for loop doesn't take advantege of the fact that sets are ordered.
The test you are looking for is much simpler
It is sufficient to loop on sop, and check for every item if the symmetric is in the set as well. If one is not, it's not a symmetric relationship. If all succeed to find the reverse, it's fine:
bool is_symetric (SOP sop) {
for (auto it3 = sop.begin(); it3 != sop.end(); it3++) { // loop through each pair in set
if (it3->first != it3->second) {
if (sop.find({it3->second,it3->first })==sop.end()) {
return false;
}
}
}
return true;
}
Online demo
There's even a cooler solution if you're allowed to use the algorithm library:
bool is_symetric (SOP sop) {
return all_of(sop.cbegin(), sop.cend(),
[&sop](auto &x){ return x.first==x.second || sop.find({x.second,x.first })!=sop.end();}) ;
}
Online demo 2
And even cooler, is if you make it a template, able to work not only with unsigned, but with any other type:
template <class T>
bool is_symetric (set<pair<T,T>> sop) {
return all_of(sop.cbegin(), sop.cend(),
[&sop](auto &x){ return x.first==x.second || sop.find({x.second,x.first })!=sop.end();}) ;
}
Online demo 3 (with unsigned long long)

Related

unordered_map iterator points to end(), how can I retrieve a key from unordered_map?

I am currently trying to return the key value only from an unordered_map, groups, that has a string as a key and a vector of strings as my value. I am currently encountering the issue that my it iterator for the unordered_map, groups, is pointing at the end() and makes my statement false, never returning my groupKey. I can only use hasNext and getNextHome to iterate through my map. So for-loops cannot be used for this task.
I've initialized them as such in my header file:
Home::Home()
{
iter = 0;
it = groups.begin();
}
private:
int iter; // the iterator
unordered_map<string, vector<string>>::iterator it; // iterator for the map
void Home::resetHomeIterator()
{
iter = 0;
it = groups.begin();
}
bool Home::hasNext() {
if (iter == groups.size() && it == groups.end()) {
return false;
}
else if (iter < groups.size()) {
return true;
}
return false;
}
string Home::getNextHome()
{
if (hasNext() == false || it == groups.end()) {
resetHomeIterator();
}
it++;
string groupKey = it->first;
return groupKey;
}
Whenever I run this, the it->first gives me an error that I cannot "dereference end list iterator" and when I debug, I never get a groupKey back, it just goes to false so I never get a key returned.
I am trying to get a groupKey back and use a while-loop for my hasNext in my main.cpp file ( while (hasNext) ) and iterate the key values that way. I know my group map is not empty either, I saw as I was using the debugger that they were being placed accordingly. I've tried to work around it, but I'm not sure if I'm getting it. I might be missing something. How can I retrieve just the key since it's a string?
The main reason for this question is because iter is out of bounds. The element number of groups starts from 0. group.end() points to the position after the last valid element in the continuous space. So, iter should not become group.size().
if (iter == groups.size()-1 && it == groups.end()) {
return false;
}
In addition, it is not recommended to use iterators in Home. Because iterator and pointer are not the same. Iterators are for iterating and should be considered transient, and not stored for later use.

Loop over all (unordered) pairs of elements in a vector

I have a std::vector of some data (Points in my case) and I want to loop over all distinct pairs of elements. The order of the pair is not important (as I am only interested in the distance of the points). With a classic for loop what I would want to do would be something like:
std::vector<double> vec{-1., 3., 5., -8., 123., ...};
for (std::vector<double>::size_type first = 0; first < vec.size(); ++first) {
for (std::vector<double>::size_type second = first+1; second < vec.size();
++second) {
// Compute something using std::fabs(vec.at(first)-vec.at(second))
}
}
My question is now if one can achieve this more elegantly using range based loops.
I wouldn't attempt to coerce it into a range based loop (since contriving the start of the inner loop will be tricky), but I would work directly with the iterators to clarify the loop body and to make the code less dependent on the specific container you're using:
for (auto first = vec.begin(); first != vec.end(); ++first){
for (auto second = first + 1; second != vec.end(); ++second){
// Your vec.at(first) is now simply *first.
}
}
Note that first + 1 is always valid since first is never vec.end() when first + 1 is evaluated.
std::vector::at is also required by the C++ standard to check that the supplied index is in the bounds of the vector (and throw a std::out_of_range exception if it isn't within the bounds), which is an unnecessary overhead in your case.
I provide this answer only because OP want a way of doing that with
range based for loops. It isn't more elegant than ordinary loops.
If your vector doesn't have duplicate numbers you can use reverse iteration instead of beginning from a specific point in the second loop, so that you can use range based for in your iterations.
for reverse iteration by range based for loops you want an adapter class.
template <typename It>
class reverse_adapter
{
public:
reverse_adapter(It rbegin, It rend)
: _rbegin(rbegin), _rend(rend)
{}
It begin() const { return _rbegin; }
It end() const { return _rend; }
private:
It _rbegin;
It _rend;
};
template<typename Container>
reverse_adapter<typename Container::reverse_iterator> make_reverse(Container& container)
{
reverse_adapter<typename Container::reverse_iterator> adapter(std::rbegin(container), std::rend(container));
return adapter;
}
And use this adapter for reverse iteration in second loop.
for(auto val : vec)
{
for (auto second_val : make_reverse(vec)) // Start from last to current item in first loop
{
if (val == second_val) break; // Instead of first + 1 in second loop
auto dif = val - second_val;
}
}

Removing an object from vector while iterating through it

I'm new to C++ so I'm having trouble figuring out how to best remove an object from a vector while still iterating through it.
Basically, I need to iterate through two vectors. For every item, if the ID's match, I can remove them.
//For every person, check to see if the available bags match:
for(std::vector<Person>::iterator pit = waitingPeopleVector.begin(); pit != waitingPeopleVector.end(); ++pit) {
for(std::vector<Bag>::iterator bit = waitingBagsVector.begin(); bit != waitingBagsVector.end(); ++bit) {
int pId = pit->getId();
int bId = bit->getId();
if(pId == bId){
//a match occurs, remove the bag and person
}
}
}
Working with iterators is a bit confusing, I know I can use the .erase() function on my vectors, but I can't really pass pit or bit. Any help appreciated. Thanks
From the standard:
The iterator returned from a.erase(q) points to the element
immediately following q prior to the element being erased. If no such
element exists, a.end() is returned.
I would use it in something like using the erase method:
std::vector<Person>::iterator pit = waitingPeopleVector.begin();
std::vector<Bag>::iterator bit = waitingBagsVector.begin();
while (pit != waitingPeopleVector.end())
{
bool didit;
while (bit != waitingBagsVector.end())
{
didit = false;
if (pit->getId() == bit->getId() && !didit)
{
bit = waitingBagsVector.erase(bit);
pit = waitingPeopleVector.erase(pit);
didit = true;
}
else
{
++bit;
}
}
if (didit)
continue;
else
++pit;
}
Using the erase-remove idiom will achieve this objective, the below offers an (untested) way using lambdas and <algorithm> functions to remove elements from wPL which have the same ID as wBL. It shouldn't be too much effort to extend this to both lists. Note, we have used std::list instead of std::vector for faster removal.
std::list<Person> wPL;
std::list<Bag> wBL;
//...
wPL.erase(std::remove_if(wPL.begin(), wPL.end(),
[&wBL](auto x) { return std::find_if(wBL.begin(), wBL.end(), [](auto y)
{ return x.getId() == y.getId();); }), wPL.end() };

How to use correct pointer for find and find_if when dealing with vector

I have a structure like this:
struct client
{
string name;
double money;
};
I also have 2 predicates:
bool less_10(const client& a)
{
return a.money < 10;
}
bool not_a(const client& a)
{
return a.name.at(0) != 'A';
}
In my main function I use this to filter out the result stored in vector client_list (everyone with money < 10 (choice 1) or everyone with name not start with A (else))
if (choice_filter == 1)
{
vector<client>::iterator it3;
it3 = find_if(client_list.begin(), client_list.end(), less_10);
while (it3 != client_list.end())
{
**client_list.erase(it3);
it3 = find_if(it3 + 1, client_list.end(), less_10);
}
client_list.erase(it3);**
}
else
{
vector<client>::iterator it4;
it4 = find_if(client_list.begin(), client_list.end(), not_a);
while (it4 != client_list.end())
{
**client_list.erase(it4);
it4 = find_if(it4 + 1, client_list.end(), not_a);
}
client_list.erase(it4);**
}
I notice that if I erase first, then find_if, i'll lost the last client. So i added 1 more line to erase, but the program crashes as iterator is now at the end, cant erase.
Is there any way to get around this? I want to keep using find_if with predicates as well as while loop like above as they are required.
As others have said, std::remove_if is the best solution. If
you're doing this for pedagogical reasons (which I suspect is
the case, given these particular predicates): you're on the
right track. The only issue is that client_list.erase
invalidates the iterator. But since it returns an iterator to
the element immediately after the element it erased, you can use
something like:
std::vector<Client>::iterator it
= std::find_if( client_list.begin(), client_list.end(), predicate );
while ( it != client_list.end() ) {
it = client_list.erase( it );
it = std::find_if( it, client_list.end(), predicate );
}
And you don't want to call erase after the loop. The iterator
designates the end, where there is no element to be erased.
The typical way to go is to use a temporary vector:
vector<client> tmp;
for (...)
{
if(predicate(it))
tmp.push_back(*it);
}
client_list.swap(tmp);
This is similar to what Chris suggested in a comment, although that solution would first move elements to the end of the vector and then truncate them from there. I'm not sure if that doesn't change the order on the way, just check the documentation. Depending on what you want, either could do the work though.
If you used a different container like list<> that did not invalidate all iterators in erase(), you could do this:
it = c.begin();
end = c.end();
while(it != end)
{
if(predicate(*it))
{
c.erase(it++);
}
else
{
++it;
}
}
Note that if you call erase(), you invalidate that iterator still, hence the iterator is first incremented and erase() is called with the former value using the postfix increment.
I also agree with chris, to using std::remove_if:
{
remove_if(client_list.begin(), client_list.end(), less_10);
}
But if you want to reinvent the wheel:
{
vector<client>::iterator it3 = client_list.begin();
while (true)
{
it3 = find_if(it3, client_list.end(), less_10);
if (it3 == client_list.end()) {
break;
}
it3 = client_list.erase(it3);
}
}

Half edge twins

I have implemented a Half-edge data structure for loading 3d objects. I find that the part of assigning twin/pair edges takes the longest computation time (especially for objects which have hundreds of thousands half edges). The reason is that I use nested loops to accomplish this. Is there a simpler and efficient way of doing this?
Below is the code which I've written. HE is the half-edge data structure. hearr is a vector containing all the half edges. vert is the starting vertex and end is the ending vertex. Thanks!!
HE *e1,*e2;
for(size_t i=0;i<hearr.size();i++){
e1=hearr[i];
for(size_t j=1;j<hearr.size();j++){
e2=hearr[j];
if((e1->vert==e2->end)&&(e2->vert==e1->end)){
e1->twin=e2;
e2->twin=e1;
}
}
}
I used some simple keywords like break and continue, and also set the value of j in the inner loop as j=i. This improved the speed significantly. Earlier it took my 403 seconds for a set of data. Now its 11 seconds. These are the changes. Any comments are welcome. Thanks!
for(size_t i=0;i<hearr.size();i++){
e1=hearr[i];
if(e1->twin!=0)
continue;
for(size_t j=i;j<hearr.size();j++){
e2=hearr[j];
if(e2->twin!=0)
continue;
if((e1->vert==e2->end)&&(e2->vert==e1->end)){
e1->twin=e2;
e2->twin=e1;
break;
}
}
}
Here is a solution. I haven't compiled it.
The basic idea is to sort the range by (vert then end) and by (end then vert). Each of these takes nlgn time.
We then walk both lists in parallel looking for ranges where the vert-major sorted list's end equals the end-major sorted list's end.
One we have these ranges, we call DoTwins. This walks the ranges in question, looking for where the vert-major list's end matches the end-major list's vert. I then check if there are multiple edges that are exactly equivalent (if there are, things go poorly, so I assert), then hook up the twins.
Each iteration of each loop (inner or outer) advances where we are analyzing in a list by 1, and each outer loop never looks back. So this is O(n).
Note that the DoTwins loop and the loop that calls DoTwins follow basically the same logic with slightly different tests. Refactoring that logic might improve the code.
Disclaimer: Code has not been compiled (or run, or debugged), just written from scratch, so expect there to be typos and errors. But the basic idea should be sound.
// A procedure to solve a subproblem -- the actual assignment of the
// twin variables. The left range's "vert" field should equal the
// right range's "end" field before you call this function. It proceeds
// to find the subsets where the left "end" equals the right "vert",
// and sets their twin field to point to each other. Note that things
// go squirrly if there are multiple identical edges.
template< typename HEPtrRange >
void DoTwins( HEPtrRange EqualVertRange, HEPtrRange EqualEndRange )
{
auto it1 = EqualVertRange.first;
auto it2 = EqualEndRange.first;
while( it1 != EqualVertRange.second && it2 != EqualEndRange.second )
{
Assert((*it1)->vert == (*it2)->end);
if ((*it1)->end > (*it2)->vert)
{
++(*it2);
continue;
}
if ((*it1)->end < (*it2)->vert)
{
++(*it1);
continue;
}
Assert((*it1)->end == (*it2)->vert);
// sanity check for multiple identical edges!
auto it3 = it1;
while (it3 != EqualVertRange.second && (*it3)->end == (*it1)->end)
++it3;
auto it4 = it2;
while (it4 != EqualVertRange.second && (*it4)->end == (*it2)->end)
++it4;
// the range [it1, it3) should have its twin set to the elements
// in the range [it2, it4). This is impossible unless they
// are both of size one:
Assert( it3 - it1 == 1 );
Assert( it4 - it2 == 1 );
for (auto it = it1; it != it3; ++it)
(*it)->twin = it2;
for (auto it = it2; it != it4; ++it)
(*it)->twin = it1;
it1 = it3;
it2 = it4;
}
}
Elsewhere:
// A vector of the edges sorted first by vert, then by end:
std::vector<HE*> vertSorted(&hearr[0], (&hearr[0]).size());
std::sort(vertSorted.begin(), vertSorted.end(),
[](HE* e1, HE* e2)
{
if (e1->vert != e2->vert)
return e1->vert < e2->vert;
return e1->end < e2->end;
}
);
// A vector of the edges sorted first by end, then by vert:
std::vector<HE*> endSorted = vertSorted;
std::sort(endSorted.begin(), endSorted.end(),
[](HE* e1, HE* e2)
{
if (e1->end != e2->end)
return e1->end < e2->end;
return e1->vert < e2->vert;
}
);
// iterate over both at the same time:
auto it1 = vertSorted.begin();
auto it2 = endSorted.begin();
while(it1 != vertSorted.end() && it2 != endSorted.end())
{
// we are looking for cases where left->vert == right->end.
// advance the one that is "lagging behind":
if ((*it1)->vert > (*it2)->end)
{
++it2;
continue;
}
if ((*it1)->vert < (*it2)->end)
{
++it1;
continue;
}
Assert( (*it1)->vert == (*it2)->end );
// Find the end of the range where left->vert == right->end
auto it3 = it1;
while (it3 != vertSorted.end() && (*it3)->vert == (*it1)->vert)
{
++it3;
}
auto it4 = it2;
while (it4 != endSorted.end() && (*it4)->vert == (*it2)->vert)
{
++it4;
}
auto EqualVertRange = std::make_pair(it1, it3);
auto EqualEndRange = std::make_pair(it2, it4);
// Delegate reverse lookups and assignment of twin variable to a subprocedure:
DoTwins( EqualVertRange, EqualEndRange );
it1 = it3;
it2 = it4;
}
A better solution would be to sort the array, then perform a binary search providing your own comparison. Or consider hashing each node, then performing a lookup while providing a custom comparison