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
Related
Probably very simple but can't get my head around it atm
I have this
// standard std::map and std::map::iterator
auto pos = map.find(val);
for(auto it = map.begin; it != pos; ++it)
I want to search for an element and then process all elements before (container is ordered, so just in iteration order) and including the find location, however this doesn't appear to examine the final element at position 'pos'. How can I achieve this?
Just move things around.
auto pos = map.find(val);
for(auto it = map.begin(); it != map.end(); ++it)
{
// Do something
if (it == pos)
break;
}
If it's possible that the value does not exist in the map, this will simply end up iterating over the entire map and that's why it != map.end(); is explicitly needed, to catch this particular runaway train...
Caution: if you have explicit continues inside the for loop, some additional TLC will be needed.
You need to differentiate between the cases where val is found in map and otherwise:
void f(/*map::[const_]iterator*/ it);
auto pos = map.find(val);
for (auto it = map.begin(); it != pos; ++it)
f(it);
if (pos != map.end())
f(pos);
You could use a do...while() loop but you would have to check for pos!=end() each time.
I have following problem definition and searching on an efficient way (a dirty way already found):
I have a set of correspondences whith integer IDs, e.g.:
(0,9)
(1,5)
(9,2)
(2,3)
what i want is a set of arrays which all have connected correspondecnes included, in my example that would be
(0,9,2,3)
(1,5)
My dataset is really big so i need it very efficient, best in C++ and tbb.
What i currently did and what works (but is in fact slow and single threadded):
struct point
{
std::set<size_t> others;
};
std::map<size_t, point> globalList;
//globalList is filled with input data set, for my example:
globalList[0].others.insert(0);
globalList[0].others.insert(9);
globalList[1].others.insert(1);
globalList[1].others.insert(5);
globalList[9].others.insert(9);
globalList[9].others.insert(2);
globalList[2].others.insert(2);
globalList[2].others.insert(3);
bool changed;
do
{
changed = false;
for (auto it1 = globalList.begin(); it1 != globalList.end(); ++it1 )
{
for (auto it2 = it1 ; it2 != globalList.end(); ++it2 )
{
if (it2 == it1 )
continue;
auto findIt = it2->second.others.find(it1->first);
bool merge = false;
if( findIt != it2->second.others.end())
{
merge = true;
}
else
{
for( auto otherIt = it1->second.others.begin(); otherIt != it1->second.others.end(); ++otherIt )
{
findIt = it2->second.others.find(*otherIt );
if (findIt != it2->second.others.end())
{
merge = true;
break;
}
}
}
if(merge )
{
it1->second.others.insert(it2->second.others.begin(), it2->second.others.end());
auto it2remove = it2;
--it2;
globalList.erase(it2remove );
changed= true;
}
}
}
} while (changed);
}`
any suggestions, tips (links to algorithms, e.g. in boost) or implementations would be great....
What you want to do is basically find connected components in a graph. In your case you are starting with a set of edges (each pair is an edge).
There is for example the boost graph library, which has an implementation.
It looks like finding the longest path in trees. What do you do with loops ? I would try with a tree or graph storage of your items.
You are looking for Union Find or Disjoint Set Data Structure
An efficient implementation along with a great tutorial can be found here.
My problem is as follows: I use an iterator, and I want to compare each element to the next element. Prototype looks like below, how can I increase the iterator to be able to compare?
Also, how can I set a proper condition for this to happen? I mean how to point on the last element, not on the next after the last like with end() function:
std::vector<T>::const_iterator it;
std::vector<T>::const_iterator it2;
for (it = set.begin(), it != set.end(); it++)
{
// some things happen
if ( final == it )
{
if ( it != set.end()-1 ) // how to write properly condition?
{
it2 = it + 1; //how to assign the next here?
if (...)//some condition
{
if ( it->func1() - it2->func1()) < 20 ) //actual comparison of two consecutive element values
// do something
}
}
}
}
In C++11 use the functions std::next() and std::prev().
Your code could become:
// before
it != std::set.end()-1
// after
it != std::prev(set.end())
and
// before
it2 = it + 1;
// after
it2 = std::next(it);
That is true also for non-vector containers, such as map,set or others.
NOTE: after std::next(it), "it" iterator remains unmodified!
NOTE 2: Use it2 = std::next(it,n); to increment as much as you need.
You can use adjacent_find to solve that. You should use the second form of that function (with predicate) and pass to the predicate your some things happen and some condition in c-tor
auto found = std::adjacent_find( set.begin(), set.end(),
[some_comdition]( const T & left, const T & right ) {
if ( some_comdition ) {
if ( left.func1() - right.func1() < 20 ) {
do_smth();
// return true; if there's no need to continue
}
}
return false;
}
);
Based on the fact that it++ is acceptable, we should define a new iterator called itplusone, which is initialized as itplusone = ++it. In this way, you can safely use the meaning of an iterator pointing to the next item of it. Also clearly, the range of iterator of itplusone bounded by terms itplusone != set.end(). I use this method to compute the total weight of a path, which is defined as a list object.
In the for loop, you use it++ which means it = it + 1, which is perfectly ok. So this one will be fine also it2 = it + 1. it2 will be pointing to the next value.
In the for loop again, you use it != set.end(), which is again perfectly ok. So you can also it + 1 < set.end(), just like you did in your code.
I don't see anything wrong in your code, just wanted to explain.
somewhat late, just discovered it, but like mentioned above, ++ iterator works fine.
vector<string> P
auto itA = begin(P);
while(itA != end(P))
{
if(itA != end(P))
{
++itA; //
}
}
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Erasing from a std::vector while doing a for each?
I'm trying to implement vertice coloring according to this algorithm;
/*
Given G=(V,E):
Compute Degree(v) for all v in V.
Set uncolored = V sorted in decreasing order of Degree(v).
set currentColor = 0.
while there are uncolored nodes:
set A=first element of uncolored
remove A from uncolored
set Color(A) = currentColor
set coloredWithCurrent = {A}
for each v in uncolored:
if v is not adjacent to anything in coloredWithCurrent:
set Color(v)=currentColor.
add v to currentColor.
remove v from uncolored.
end if
end for
currentColor = currentColor + 1.
end while
*/
I don't understand "add v to currentColor." line but I supposed, it means assing currentColor to v. Therefore what is the "set"? Anyway the problem is erasing element in vector while iterating it. This is the code.
vector<struct Uncolored> uc;
vector<struct Colored> c;
int currentColor = 0;
struct Colored A;
struct Colored B;
vector<struct Uncolored>::iterator it;
vector<struct Uncolored>::iterator it2;
vector<struct Colored>::iterator it3;
for(it=uc.begin();it<uc.end();it++){
A.id = (*it).id;
uc.erase(uc.begin());
A.color = currentColor;
c.push_back(A);
for(it2=uc.begin();it2<uc.end();it2++) {
it3=c.begin();
while(it3 != c.end()) {
if( adjacencyMatris[(*it2).id][(*it3).id] == 0 ) {
B.id = (*it2).id;
it2 = uc.erase(it2);
B.color = currentColor;
c.push_back(B);
}
it3++;
}
}
currentColor = currentColor + 1;
}
I think it2 = uc.erase(it2); line is already general use but It gives run time error.
In the line:
it2 = uc.erase(it2);
an element pointed by iterator it2 is removed from the vector, elements are shifted in memory in order to fill that gap which invalidates it2. it2 gets a new value and now points to the first element after the the removed one or the end of the vector (if removed element was the last one). This means that after erasing an element you should not advance it2. An alternative to proposed remove-erase idiom is a simple trick:
for(it2 = uc.begin(); it2 != uc.end();)
{
...
if(...)
{
it2 = uc.erase(it2);
}
else
{
++it2;
}
...
}
You can read more about this here.
Edit:
Regarding your comment, you can use a flag to pass the information whether an element has been erased or not, and you can check it when you get out from the inner loop:
for(it2=uc.begin(); it2 != uc.end();)
{
bool bErased = false;
for(it3 = c.begin(); it3 != c.end(); ++it3)
{
if(adjacencyMatris[(*it2).id][(*it3).id] == 0 )
{
B.id = (*it2).id;
it2 = uc.erase(it2);
bErased = true;
B.color = currentColor;
c.push_back(B);
break;
}
}
if(!bErased)
++it2;
}
After you've erased an element from uc you need to break from the inner loop. In the next iteration of the outer loop you'll be able to access the next element in the uc through a valid iterator.
Instead of working with iterator types, store an index into the vector. When you need an iterator--perhaps for passing into erase--you can say begin() + myIndex to generate an iterator.
This also makes the loop look more familiar, e.g.
for(ind=0; ind < uc.size(); ind++) {
vector::erase() can invalidate iterators pointing to the vector.
This invalidates all iterator and references to position (or first) and its subsequent elements.
You need to add the result of erase to the iterator (it will point to the element just after the one erased) and use that consequently. Note that in
for(it=uc.begin();it<uc.end();++it){
A.id = (*it).id;
uc.erase(uc.begin());
...
}
The iterator it is not valid after uc.erase, so subsequent ++ and use might result in runtime error.
Similarly, even though you assign the result of erase to it2, the call can invalidate it, which is not changed.
Your best bet is either to re-start your algorithm from the beginning after each erase(), or if you can alter it so that it can continue from the iterator returned by erase, do that to gain some efficiency.
You've got the runtime error because it2 = uc.erase(it2); returns the iterator following the last removed element, so the it2++ in for(it2=uc.begin();it2<uc.end();it2++) goes beyond the last element.
Try changing your if in:
if( adjacencyMatris[(*it2).id][(*it3).id] == 0 ) {
B.id = (*it2).id;
uc.erase(it2);
B.color = currentColor;
c.push_back(B);
break;
}
Let's say I have a sequential container, and a range (pair of iterators) within that container of elements that are currently 'active'. At some point, I calculate a new range of elements that should be active, which may overlap the previous range. I want to then iterate over the elements that were in the old active range but that are not in the new active range to 'deactivate' them (and similarly iterate over the elements that are in the new range but not the old range to 'activate' them).
Is this possible?
Does it become easier if I know that the start of the new active range will always be later in the container than the start of the old active range?
For the purposes of the question, assume the container is a vector.
You can use two sets for the last active range and another for the current active range. Use the set_difference algorithm to get the objects to be activated/deactivated.
What you need is a range_difference function. I though Boost.Range would provide something like this, but I didn't find anything in their doc (well, I didn't search very thoroughly...), so I rolled up my own.
The following function returns a pair of range, containing the result of the difference between the range denoted by (first1,last1) and the one denoted by (first2,last2). A pre-condition is that first1 must be positioned before or at the same position as first2.
template <typename InputIterator>
std::pair<
std::pair<InputIterator, InputIterator>,
std::pair<InputIterator, InputIterator> >
range_difference(InputIterator first1, InputIterator last1,
InputIterator first2, InputIterator last2)
{
typedef std::pair<InputIterator, InputIterator> Range;
InputIterator it;
// first1 must be <= first2
for (it = first1 ; it != last1 && it != first2 ; ++it);
Range left_range = std::make_pair(first1, it); // Left range
if (it == last1)
return std::make_pair(left_range, std::make_pair(first2, first2));
// it == first2
while (it != last1 && it != last2) ++it;
return std::make_pair(left_range, std::make_pair(it, last1)); // Right range
}
The result of the difference can be composed of two parts, if range2 is completely included into range1. You end up with a left range and a right range:
|_____________________|__________________|________________________|
first1 first2 last2 last1
In this case, the function returns (first1, first2),(last2, last1).
In this other configuration,
|_____________________| |________________________|
first1 last1 first2 last2
the function returns (first1, last1),(first2, first2). There are many other possible configurations. However, one important thing to know is that in the case where the right range is empty, it will be positioned at max(first2, last1). You'll see how this is necessary in the example.
Finally, if first1 and first2 are at the same position, the returned left range will be empty, i.d. (first1,first1).
Now, how can we use this function to solve your problem? Well that's rather easy for the "deactivate" range, but a little trickier for the "activate" one:
typedef std::vector<Activable>::iterator Iterator;
Iterator old_beg, old_end, new_beg, new_end; // Old and new ranges
typedef std::pair<Iterator, Iterator> Range;
typedef std::pair<Range, Range> SplitRange;
SplitRange deactivate = range_difference(old_beg, old_end, new_beg, new_end);
// Left range
for (Iterator it = deactivate.first.first;
it != deactivate.first.second;
++it)
it->deactivate();
// Right range
for (Iterator it = deactivate.second.first;
it != deactivate.second.second;
++it)
it->deactivate();
SplitRange activate =
range_difference(new_beg, new_end, new_beg, deactivate.second.first);
// Note the use of the previously returned right range -------^
for (Iterator it = activate.second.first;
it != activate.second.second;
++it)
it->activate();
And there you go. Maybe this solution is a little overkill to your problem, but I think the range_difference function could be useful in many place.
Here's a simple solution:
typedef std::pair<std::vector<T>::iterator, std::vector<T>::iterator> Range;
void Update(std::vector<T>& v, Range oldActive, Range newActive)
{
int op = 0;
for (std::vector<T>::iterator i = v.begin(), end = v.end(); i != end; ++i)
{
if (i == oldActive.first) op += 1;
if (i == oldActive.second) op -= 1;
if (i == newActive.first) op += 2;
if (i == newActive.second) op -= 2;
if (op == 1) i->Deactivate();
if (op == 2) i->Activate();
}
}
This deliberately puts simplicity before efficiency as a starting point, since it scans the entire vector; on the other hand it's single pass and does no copying.
I think I'll keep it simple:
// Iterators denoting the old and new ranges (might be cleaner to use some kind
// of typedef like James Hopkin's did, but that's not the most important)
std::vector<Activable>::iterator old_beg,
old_end,
new_beg,
new_end;
std::vector<Activable>::iterator it;
// Deactivate
for (it = old_beg ; // go from the beginning of the old range
it != old_end && it != new_beg ; // to either the end of the old one or the
++it) // beginning of the new one
it->deactivate();
// "Jump" to the correct position
if (it == old_end) it = new_beg; // no overlap
else it = old_end; // overlap
// Activate
for (; it != new_end ; ++it)
it->activate();
You'll note that I assumed that the new range couldn't be totally contained into the old one (e.g. you can't have an old range going from index 4 to 10, and a new one going from 5 to 7). If this is a case, you'll need to change a little the algorithm.