Prevent memory allocation in recursive combination generation - c++

(Sorry about the title, it's not the best descriptive)
I am playing with graph theory, and generating all possible combinations of a given set of input numbers. Given the input set {2,3,4}, my possible combinations (of which there are 3!), are:
The following recursive solution works, but I don't like the fact that I have to "copy" the input vector in order to "remove" the element that represents the node I am following in order to prevent including it for output again. Elements I am going to output are stored in vecValues whereas the elements I can currently choose from are stored in vecInput:
void OutputCombos(vector<int>& vecInput, vector<int>& vecValues)
{
// When hit 0 input size, output.
if (vecInput.size() == 0)
{
for (int i : vecValues) cout << i << " ";
cout << endl;
}
size_t nSize = vecInput.size();
for (vector<int>::iterator iter = begin(vecInput); iter != end(vecInput); ++iter)
{
auto vecCopy = vecInput;
vecCopy.erase(find(begin(vecCopy), end(vecCopy), *iter));
vecValues.push_back(*iter);
OutputCombos(vecCopy, vecValues);
vecValues.pop_back();
}
}
void OutputCombos(vector<int>& vecInput)
{
vector<int> vecValues;
OutputCombos(vecInput, vecValues);
}
int main()
{
vector<int> vecInput{ 2,3,4 };
OutputCombos(vecInput);
return 0;
}
As expected from my state space tree, the output is
2 3 4
2 4 3
3 2 4
3 4 2
4 2 3
4 3 2
How can I get around this without having to make a copy of the vector for each recursive call please?

You could always just use std::next_permutation from <algorithm>
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> input {2, 3, 4};
do {
for (auto i : input) std::cout << i << " ";
std::cout << std::endl;
} while(std::next_permutation(input.begin(), input.end()));
return 0;
}
This gives you the same output. You might want to check out a possible implementation of next_permutation, which involves swaps within the vector rather than copying the vector several times.

I think this might be closer to what you're looking for. A version without std::next_permutation that doesn't involve copying any vectors, and allows the input to remain const. However, it does this at the cost of checking the output in each iteration to make sure it doesn't add the same number twice.
#include<vector>
#include<iostream>
#include<algorithm>
template<typename T>
void OutputCombinations(
const std::vector<T>& input,
std::vector<typename std::vector<T>::const_iterator >& output)
{
for(auto it = input.begin(); it != input.end(); ++it)
{
if (std::find(output.begin(), output.end(), it) == output.end())
{
output.push_back(it);
if (output.size() == input.size())
{
for(auto node : output) std::cout << *node << " ";
std::cout << std::endl;
}
else OutputCombinations(input, output);
output.pop_back();
}
}
}
int main()
{
std::vector<int> nodes{ 2, 3, 4, 2 };
std::vector<std::vector<int>::const_iterator> result{};
OutputCombinations(nodes, result);
return 0;
}

After much studying I found inspiration in this article which gave me the ultimate solution. The idea is that we keep a vector of Boolean values which indicates whether or not a particular value has been used in the combination; that way we don't need to remove the element that we have already used hence there is no memory allocation overhead.
So, when building the branch {2,4,3}, if we get to {2,4}, vecTaken will be {true, false, true} and nNumBoolsSet will be 2. So when we loop, we will only "use" the element at index 1 of vecInput since that is the only element that has not been used as dictated by vecTaken.
void OutputCombos(vector<int>& vecInput, vector<int>& vecValues, vector<bool>& vecTaken, int& nNumBoolsSet)
{
size_t nSize = vecInput.size();
if (nNumBoolsSet == nSize)
{
for (int i : vecValues) cout << i << " ";
cout << endl;
return;
}
for (vector<int>::size_type i = 0; i < nSize; ++i)
{
if (vecTaken[i] == false)
{
vecValues.push_back(vecInput[i]);
vecTaken[i] = true;
++nNumBoolsSet;
OutputCombos(vecInput, vecValues, vecTaken, nNumBoolsSet);
vecTaken[i] = false;
vecValues.pop_back();
--nNumBoolsSet;
}
}
}
void OutputCombos(vector<int>& vecInput)
{
vector<int> vecValues;
vector<bool> vecTaken(vecInput.size(), false);
int nNumBoolsSet = 0;
OutputCombos(vecInput, vecValues, vecTaken, nNumBoolsSet);
}
int main()
{
vector<int> vecInput{ 2,3,4 };
OutputCombos(vecInput);
}

Related

Range-based for loop with special case for the first item

I find myself often with code that looks like this:
bool isFirst = true;
for(const auto &item: items)
{
if(!isFirst)
{
// Do something
}
// Normal processing
isFirst = false;
}
It seems that there ought to be a better way to express this, since it's a common pattern in functions that act like a "join".
Maybe a for_first_then_each is what you're looking for? It takes your range in terms of iterators and applies the first function to the first element and the second function to the rest.
#include <iostream>
#include <vector>
template<typename BeginIt, typename EndIt, typename FirstFun, typename OthersFun>
void for_first_then_each(BeginIt begin, EndIt end, FirstFun firstFun, OthersFun othersFun) {
if(begin == end) return;
firstFun(*begin);
for(auto it = std::next(begin); it != end; ++it) {
othersFun(*it);
};
}
int main() {
std::vector<int> v = {0, 1, 2, 3};
for_first_then_each(v.begin(), v.end(),
[](auto first) { std::cout << first + 42 << '\n'; },
[](auto other) { std::cout << other - 42 << '\n'; }
);
// Outputs 42, -41, -40, -39
return 0;
}
You can't know which element you are visiting in a range based for loop unless you are looping over a container like an array or vector where you can take the address of the object and compare it to the address of the first item to figure out where in the container you are. You can also do this if the container provides lookup by value, you can see if the iterator returned from the find operation is the same as the begin iterator.
If you need special handling for the first element then you can fall back to a traditional for loop like
for (auto it = std::begin(items), first = it, end = std::end(items); it != end; ++it)
{
if (it == first)
{
// do something
}
// Normal processing
}
If what you need to do can be factored out of the loop then you could use a range based for loop and just put the processing before the loop like
// do something
for(const auto &item: items)
{
// Normal processing
}
With Ranges coming in C++20, you can split this in two loops:
for (auto const& item : items | view::take(1)) {
// first element only (or never executed if items is empty)
}
for (auto const& item : items | view::drop(1)) {
// all after the first (or never executed if items has 1 item or fewer)
}
If you don't want to wait for C++20, check out range-v3 which supports both of these operations.
This won't work like this with an Input range (like if items is really a range that reads from cin) but will work just fine with any range that is Forward or better (I'm guessing items is a container here, so that should be fine).
A more straightforward version is actually to use enumerate (which only exists in range-v3, not in C++20):
for (auto const& [idx, item] : view::enumerate(items)) {
if (idx == 0) {
// first element only
}
// all elements
}
A fun alternative solution, that I would not use in production without great care, would be to use custom iterator.
int main() {
std::vector<int> v{1,2,3,4};
for (const auto & [is_first,b] : wrap(v)) {
if (is_first) {
std::cout << "First: ";
}
std::cout << b << std::endl;
}
}
A toy implementation could look like this:
template<typename T>
struct collection_wrap {
collection_wrap(T &c): c_(c) {}
struct magic_iterator {
bool is_first = false;
typename T::iterator itr;
auto operator*() {
return std::make_tuple(is_first, *itr);
}
magic_iterator operator++() {
magic_iterator self = *this;
itr++;
//only works for forward
is_first = false;
return self;
}
bool operator!=(const magic_iterator &o) {
return itr != o.itr;
}
};
magic_iterator begin() {
magic_iterator itr;
itr.is_first = true;
itr.itr = c_.begin();
return itr;
}
magic_iterator end() {
magic_iterator itr;
itr.is_first = false;
itr.itr = c_.end();
return itr;
}
T &c_;
};
template<typename Collection>
collection_wrap<Collection>
wrap(Collection &vec) {
return collection_wrap(vec);
}
Check the object address to see if it's the first item:
for(const auto &item: items)
{
if (&item != &(*items.begin())
{
// do something for all but the first
}
// Normal processing
}
An approach still valid in C++ is to use a macro:
#include <iostream>
#include <vector>
#define FOR(index, element, collection, body) { \
auto &&col = collection; \
typeof(col.size()) index = 0; \
for(auto it=col.begin(); it!=col.end(); index++, it++) { \
const auto &element = *it; \
body; \
} \
}
using namespace std;
int main() {
vector<int> a{0, 1, 2, 3};
FOR(i, e, a, {
if(i) cout << ", ";
cout << e;
})
cout << endl;
FOR(i, e, vector<int>({0, 1, 2, 3}), {
if(i) cout << ", ";
cout << e;
})
cout << endl;
return 0;
}
Prints:
0, 1, 2, 3
0, 1, 2, 3
This solution is succinct compared to alternative options. On the downside, index is being tested and incremented on each iteration of the loop - this can be avoided by increasing the complexity of the macro and by using bool first instead of index, but using index in the macro covers more use cases than bool first.
Since C++20, you can slightly improve your range-based for loop by using an init-statement. The init-statement allows you to move your isFirst flag into the scope of the loop so that this flag is no longer visible outside the loop:
std::vector<int> items { 1, 2, 3 };
for(bool isFirst(true); const auto &item: items) {
if(!isFirst) {
std::cout << "Do something with: " << item << std::endl;
}
std::cout << "Normal processing: " << item << std::endl;
isFirst = false;
}
Output:
Normal processing: 1
Do something with: 2
Normal processing: 2
Do something with: 3
Normal processing: 3
Code on Wandbox
I assume you want to know how to retrieve the first element, you could do this with an array and a vector.
I'm going to show the array here.
First include this in your code:
#include <array>
Then convert your array accordingly:
std::array<std::string, 4> items={"test1", "test2", "test3", "test4"};
for(const auto &item: items)
{
if(item == items.front()){
// do something
printf("\nFirst: %s\n", item.c_str()); //or simply printf("\nFirst:"); since you gonna output a double element
}
// Normal processing
printf("Item: %s\n", item.c_str());
}
return 0;
}

How to get indices of a Mat with specific value?

I want to find indices of an array that equal with specific value. so i've Written this code:
vector<int> _classes = { 2,2,1,1,3,3,3,3,5,5,4,4,5,6,6 };
vector<int> labelVec = {1,2,3,4,5,6};
vector<int> index;
for (int i = 0; i < labelVec.size(); i++)
{
compare(_classes, labelVec[i], index, CMP_EQ);
std::vector<int>::iterator nn = find(index.begin(), index.end(), 255);
}
but i have this error : Unhandled exception at 0x760B5608 in compareFuncTest.exe: Microsoft C++ exception: cv::Exception at memory location 0x004DDC44. if i define index as Mat, this problem will be resolved. but if i define index as Mat, i can't use from find(). also in this documentation states: output array (in my code as index) that has the same size and type as the input arrays. PLZ help me to fix this code.
I still do not get what is the point of this test, I guess this will be in some other algorithm... So, I give you two possible solutions.
1) Without OpenCV
First, you must know that
std::vector<int>::iterator nn = find(index.begin(), index.end(), 255);
Will only give you the first occurrance. Knowing this, here is a way you could check if the label is inside the _classes vector.
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> _classes = { 2,2,1,1,3,3,3,3,5,5,4,4,5,6,6 };
std::vector<int> labelVec = {1,2,3,4,5,6,7};
for (const auto& label: labelVec)
{
std::vector<int>::iterator nn = find(_classes.begin(), _classes.end(), label);
if (nn != _classes.end())
{
std::cout << "I got the value from _classes: " << *nn << std::endl;
} else
{
std::cout << "I couldn't find the value with label:" << label << std::endl;
}
}
}
Here I iterate over all the labels (as you did) and then use the find directly in the classes, but with the label variable. Then I check if I found the label or not, if not, it will give you a value equal to _classes.end() which will give error if you try to use it (look at the extra label 7 which is not found).
This example can be tested here online.
2) With OpenCV
no oline test here. But this one is also easy to do. If you have a Mat in index you will only need to change the iterators to be templated. Like this:
auto nn = find(index.begin<int>(), index.end<int>(), 255);
If you a cv::Mat of classes you can also do it as in the method before and skip the comparison part (this would be faster)
Update
Since you want is the indices and all of them, then you have to iterate over it :/ if you wanted the values you could have used copy_if. You can create a lambda function to easily do the job.
like this:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
auto getIndices = [](const std::vector<int>& vec, const int value){
std::vector<int> result;
for (size_t t = 0; t < vec.size(); t++)
{
if (vec[t] == value)
{
result.push_back(static_cast<int>(t));
}
}
return result;
};
std::vector<int> _classes = { 2,2,1,1,3,3,3,3,5,5,4,4,5,6,6 };
std::vector<int> labelVec = {1,2,3,4,5,6,7};
for (const auto& label: labelVec)
{
std::vector<int> nn = getIndices(_classes, label);
std::cout << "I got the following indices for value"<< label<< ": [ ";
for (const auto& n : nn)
{
std::cout << n << ",";
}
std::cout << " ]" << std::endl;
}
}

Merging two lists efficiently with limited bound

I am trying to merge two arrays/lists where each element of the array has to be compared. If there is an identical element in both of them I increase their total occurrence by one. The arrays are both 2D, where each element has a counter for its occurrence. I know both of these arrays can be compared with a double for loop in O(n^2), however I am limited by a bound of O(nlogn). The final array will have all of the elements from both lists with their increased counters if there are more than one occurrence
Array A[][] = [[8,1],[5,1]]
Array B[][] = [[2,1],[8,1]]
After the merge is complete I should get an array like so
Array C[][] = [[2,1],[8,2],[8,2],[5,1]]
The arrangement of the elements does not have to be necessary.
From readings, Mergesort takes O(nlogn) to merge two lists however I am currently at a roadblock with my bound problem. Any pseudo code visual would be appreciated.
I quite like Stepanov's Efficient Programming although they are rather slow. In sessions 6 and 7 (if I recall correctly) he discusses the algorithms add_to_counter() and reduce_counter(). Both algorithms are entirely trivial, of course, but can be used to implement a non-recursive merge-sort without too much effort. The only possibly non-obvious insight is that the combining operation can reduce the two elements into a sequence rather than just one element. To do the operations in-place you'd actually store iterators (i.e., pointers in case of arrays) using a suitable class to represent a partial view of an array.
I haven't watched the sessions beyond session 7 (and actually not even the complete session 7, yet) but I would fully expect that he actually presents how to use the counter produced in session 7 to implement, e.g., merge-sort. Of course, the run-time complexity of merge-sort is O(n ln n) and, when using the counter approach it will use O(ln n) auxiliary space.
A simple algorithm that requires twice as much memory would be to order both inputs (O(n log n)) and then sequentially pick the elements from the head of both lists and do the merge (O(n)). The overall cost would be O(n log n) with O(n) extra memory (additional size of the smallest of both inputs)
Here's my algorithm based on bucket counting
time complexity: O(n)
memory complexity: O(max), where max is the maximum element in the arrays
Output:
[8,2][5,1][2,1][8,2]
Code:
#include <iostream>
#include <vector>
#include <iterator>
int &refreshCount(std::vector<int> &counters, int in) {
if((counters.size() - 1) < in) {
counters.resize(in + 1);
}
return ++counters[in];
}
void copyWithCounts(std::vector<std::pair<int, int> >::iterator it,
std::vector<std::pair<int, int> >::iterator end,
std::vector<int> &counters,
std::vector<std::pair<int, int&> > &result
) {
while(it != end) {
int &count = refreshCount(counters, (*it).first);
std::pair<int, int&> element((*it).first, count);
result.push_back(element);
++it;
}
}
void countingMerge(std::vector<std::pair<int, int> > &array1,
std::vector<std::pair<int, int> > &array2,
std::vector<std::pair<int, int&> > &result) {
auto array1It = array1.begin();
auto array1End = array1.end();
auto array2It = array2.begin();
auto array2End = array2.end();
std::vector<int> counters = {0};
copyWithCounts(array1It, array1End, counters, result);
copyWithCounts(array2It, array2End, counters, result);
}
int main()
{
std::vector<std::pair<int, int> > array1 = {{8, 1}, {5, 1}};
std::vector<std::pair<int, int> > array2 = {{2, 1}, {8, 1}};
std::vector<std::pair<int, int&> > result;
countingMerge(array1, array2, result);
for(auto it = result.begin(); it != result.end(); ++it) {
std::cout << "[" << (*it).first << "," << (*it).second << "] ";
}
return 0;
}
Short explanation:
because you mentioned, that final arrangement is not necessary, I did simple merge (without sort, who asked sort?) with counting, where result contains reference to counters, so no need to walk through the array to update the counters.
You could write an algorithm to merge them by walking both sequences sequentially in order, inserting where appropriate.
I've chosen a (seemingly more apt) datastructure here: std::map<Value, Occurence>:
#include <map>
using namespace std;
using Value = int;
using Occurence = unsigned;
using Histo = map<Value, Occurence>;
If you insist on contiguous storage, boost::flat_map<> should be your friend here (and a drop-in replacement).
The algorithm (tested with your inputs, read comments for explanation):
void MergeInto(Histo& target, Histo const& other)
{
auto left_it = begin(target), left_end = end(target);
auto right_it = begin(other), right_end = end(other);
auto const& cmp = target.value_comp();
while (right_it != right_end)
{
if ((left_it == left_end) || cmp(*right_it, *left_it))
{
// insert at left_it
target.insert(left_it, *right_it);
++right_it; // and carry on
} else if (cmp(*left_it, *right_it))
{
++left_it; // keep left_it first, so increment it
} else
{
// keys match!
left_it->second += right_it->second;
++left_it;
++right_it;
}
}
}
It's really quite straight-forward!
A test program: See it Live On Coliru
#include <iostream>
// for debug output
static inline std::ostream& operator<<(std::ostream& os, Histo::value_type const& v) { return os << "{" << v.first << "," << v.second << "}"; }
static inline std::ostream& operator<<(std::ostream& os, Histo const& v) { for (auto& el : v) os << el << " "; return os; }
//
int main(int argc, char *argv[])
{
Histo A { { 8, 1 }, { 5, 1 } };
Histo B { { 2, 1 }, { 8, 1 } };
std::cout << "A: " << A << "\n";
std::cout << "B: " << B << "\n";
MergeInto(A, B);
std::cout << "merged: " << A << "\n";
}
Printing:
A: {5,1} {8,1}
B: {2,1} {8,1}
merged: {2,1} {5,1} {8,2}
You could shuffle the interface a tiny bit in case you really wanted to merge into a new object (C):
// convenience
Histo Merge(Histo const& left, Histo const& right)
{
auto copy(left);
MergeInto(copy, right);
return copy;
}
Now you can just write
Histo A { { 8, 1 }, { 5, 1 } };
Histo B { { 2, 1 }, { 8, 1 } };
auto C = Merge(A, B);
See that Live on Coliru, too

Display Vector elements in a reverse order

I have two questions but they are interlinked.:
part:a->
I have been trying to display the elements of vector in reverse order. But nothing is working. I have used iterotar like;
for (it=vec.end(); it!=vec.begin(); --it){
// it is iterator not reverse_iterator.
// do work
}
P.S I am not much familiar with iterators. I have used them for the first time today to
display elem in reverse order.
also tried;
for (int i=vec.size(); i!=0; i--){
//display
}
No matter what I do it always display the elem in same order as they are present i.e not in the reverse order.
part_b->
Is there any way that I can store the output of a recursive function direct into a vector. like the code is:
I know this does not work. I have tried but just giving you an idea
what I am upto.
#include <iostream>
using namespace std;
#include "vector"
int func(int num);
vector <int> vec;
int main() {
int num=34;
// I know this would not work. But is there any possibilitiy that
// I can store the output in a vector.
vec = binary(num);
// trying to display the vector.
for (int i=vec.size();i!=0;i--) {
cout<<vec[i]<<" ";
} // not working for reverse display.
} //main.
int func(int num) {
if (num==1) {
//vec.push_back(1);
return 1;
}
else if(num==0) {
//vec.push_back(0);
return 0;
}
else {
//vec.push_back(input%2);
return binary(input/2);
}
} //func.
I hope you do unnderstand the question. if I am able to do the part b the there is no need to reverse the elem of the vector.
The standard solution uses reverse iterators:
for (auto it = v.rbegin(); it != v.rend(); ++it)
{
if (it != v.rbegin()) { std::cout << ' '; }
std::cout << *it;
}
Alternatively, you can use indices, but keep the "reversal" idiom and increment the index:
for (std::size_t i = 0; i != v.size(); ++i)
{
if (i != 0) { std::cout << ' '; }
std::cout << v[v.size() - i - 1];
}
Note that reverse iterators are literally just doing something very similar to the explicit loop under the hood. In particular, the base() member function of a reverse iterator gives you the corresponding ordinary iterator offset by one, just as we have a - 1 in the index computation. (E.g. rbegin() is just end() under the hood, but decrements by one upon dereferencing.)
Use reverse iterators:
for (auto it = vec.crend(); it != vec.crbegin(); ++it) {
std::cout << *it << ' ';
}
std::cout << '\n';
Part A
Assuming you haven't got access to C++11:
vector<int>::const_reverse_iterator it;
for (it=vec.rbegin(); it!=vec.rend(); ++it)
{
// do work
}
Part B
It looks very much like you're trying to display a number in binary. Unfortunately the standard flags on ostream only allow hex, decimal or octal as far as I'm aware, but can I suggest a simpler way of doing this?
#include <bitset>
bitset< sizeof(int) << 3 > b(34);
cout << b << endl;
Which gives:
00000000000000000000000000100010
The rather ugly looking sizeof(int) << 3 is just a way of getting the size of an int in bits to avoid truncation.
I wrote little program which may help. Maybe your vector is empty?
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char *argv[])
{
vector<int> vec;
vec.insert(vec.begin(), 1, 1);
vec.insert(vec.begin(), 1, 2);
vec.insert(vec.begin(), 1, 3);
vector<int>::iterator i;
for (i = vec.end(); i != vec.begin(); --i)
{
cout << *i;
}
cout << *i;
return 0;
}

Algorithm to merge (fuse two items together, replace them with the fusion) items in std::list (i.e. destructive clustering)

Apologies if this is obvious, I'm new to C++. There seem to be related answers on stackoverflow, just not those I understand enough to apply in my case.
I have a list of class instances that represent visual patches.
When the distance between features is below a threshold I would like to merge those items, replacing parents with the merged output.
Something like this:
Loop through all items using a nested for loop (to compare each item to every other item)
When a match is found (that is not the same instance):
Construct a new (child) instance from the matching pair, append to new list.
erase both (parent) items from the list
Continue iterating through the list finding other matches
Append the new list to the original list.
I know how to erase items from the list in a single for loop using iterators, but its unclear to me how it would work in a nested loop due to erase() incrementing to the next item.
I may also need to make this function recursive as eventually the merging should reduce the list to a set of representative instances by merging merges.
Suggestions would be appreciated.
Following is my attempt, which does not work (the nested loops interfere with one and other). What is the proper way to do this kind of pairwise comparison of elements in a list?
#include <iostream>
#include <list>
using namespace std;
int main() {
list<int> mylist;
list<int>::iterator mylistiterOutter;
list<int>::iterator mylistiterInner;
for(int i=0; i<10; i++) {
mylist.push_back(i);
cout << i << endl;
}
for(int i=0; i<10; i++) {
mylist.push_back(i);
cout << i << endl;
}
int counter =0;
for(mylistiterOutter = mylist.begin(); mylistiterOutter != mylist.end();) {
cout << "Size of mylist: " << mylist.size() << endl;
for(mylistiterInner = mylist.begin(); mylistiterInner != mylist.end();) {
cout << "mylistiterInner: " << *mylistiterInner << endl;
cout << "mylistiterOutter: " << *mylistiterOutter << endl;
//if (mylistiterOutter == mylistiterInner) {// match!
if (false) {
//mylistiterOutter = mylist.erase(mylistiterOutter);
//mylistiterInner = mylist.erase(mylistiterInner);
} else {
mylistiterOutter++;
mylistiterInner++;
}
counter++;
}
}
cout << endl << "Size of mylist: " << mylist.size() << endl << "NumIterations: " << counter << endl;
return(0);
}
Thanks #lalitm. I tried your approach first because it is closer to what I had originally envisioned, but J.N.'s proposal is more elegant so I'll try that also. Unfortunately I was unable to make #lalitm's approach work. (leads to segmentation fault). Following is slightly more complex code that includes sample class, and merging code, using #lalitm's approach:
#include <iostream>
#include <list>
#include <cmath>
using namespace std;
class percepUnit {
public:
int cx, cy; // location of percept in frame
bool remove; // used to delete percepts
// constructor method
percepUnit(int ix, int iy) {
cx = ix;
cy = iy;
remove = false;
}
};
bool canMerge(percepUnit unitA, percepUnit unitB) {
double dist = sqrt(pow(abs(unitA.cx-unitB.cx),2)+ pow(abs(unitA.cy-unitB.cy),2));
return (dist < 3);
}
percepUnit merge(percepUnit unitA, percepUnit unitB) {
int x,y;
x = unitA.cx+unitB.cx/2;
y = unitA.cy+unitB.cy/2;
return (percepUnit(x,y));
}
int main() {
list<percepUnit> mylist;
list<percepUnit> mergedlist;
list<percepUnit>::iterator mylistiterOutter;
list<percepUnit>::iterator mylistiterInner;
bool mylistiterOutterChanged;
mylist.push_back(percepUnit(0,0));
mylist.push_back(percepUnit(2,2));
mylist.push_back(percepUnit(5,5));
mylist.push_back(percepUnit(7,7));
//cout << "merge front/back? " << canMerge(mylist.front(),mylist.back()) << endl;
//percepUnit test = merge(mylist.front(),mylist.back());
//cout << "merged front/back (x,y): " << test.cx << "," << test.cy << endl;
for(mylistiterOutter = mylist.begin(); mylistiterOutter != mylist.end();) {
cout << "Size of mylist: " << mylist.size() << endl;
mylistiterInner = mylistiterOutter;
mylistiterOutterChanged = false;
for (++mylistiterInner; mylistiterInner != mylist.end();) {
if (canMerge(*mylistiterOutter, *mylistiterInner )) {
mergedlist.push_back(merge(*mylistiterOutter, *mylistiterInner));
mylistiterOutter = mylist.erase(mylistiterOutter);
mylistiterInner = mylist.erase(mylistiterInner);
mylistiterOutterChanged = true;
} else {
++mylistiterInner;
}
}
if (!mylistiterOutterChanged) {
++mylistiterOutter;
}
}
mylist.splice(mylist.end(), mergedlist);
return(0);
}
Here is my gdb info:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7b31d97 in std::_List_node_base::unhook() ()
from /usr/lib/libstdc++.so.6
(gdb) bt
#0 0x00007ffff7b31d97 in std::_List_node_base::unhook() ()
from /usr/lib/libstdc++.so.6
#1 0x0000000000401786 in std::list<percepUnit, std::allocator<percepUnit> >::_M_erase (this=0x7fffffffe4d0, __position=...)
at /usr/include/c++/4.4/bits/stl_list.h:1424
#2 0x000000000040153d in std::list<percepUnit, std::allocator<percepUnit> >::erase (this=0x7fffffffe4d0, __position=...)
at /usr/include/c++/4.4/bits/list.tcc:111
#3 0x0000000000401130 in main () at debug.cpp:61
Still no luck. I think the problem could be that the code above does not test if the two iterators are pointing at the same item in the list, and therefore that messes up the iterators (incrementing or not when they should not be).
How can I test if both iterators point at the same item? (without the brute force of comparing all the class members?, but then two copies of the same instance are not the same instance.)
void mergeObjects(std::list<T>& list)
{
std::list<T> new_list;
typedef std::list<T>::iterator Itr;
for (Itr i=list.begin(); i != list.end();)
{
Itr j=i;
bool is_merged = false;
for (++j; j != list.end();)
{
if (isMergeable(*i, *j))
{
T merged = mergeObj(*i, *j);
new_list.push_back(merged);
list.erase(j);
is_merged = true;
break;
}
else
{
++j;
}
}
if (is_merged)
{
i = list.erase(i);
}
else
{
++i;
}
}
list.splice(list.end(), new_list);
}
This should work since inserting and deleting elements does not invalidate any pointers, references and iterator to any other elements. [Ref: The C++ STL Tutorial and Reference, Nikolai Josuttis]
You didn't provide much insight yet, but here is a nice way of doing it:
#include <algorithm>
#include <set>
std::list<int> myList = {5,6,7,7,8,5};
std::set<int> uniques; // a set can only contain unique elements
// copying the list in the set will overwrite the same elements when duplicates are found
std::copy(myList.begin(), myList.end(), std::inserter(uniques, uniques.begin()));
// clear the existing list
myList.clear();
// copy back the unique elements.
std::copy(uniques.begin(), uniques.end(), std::back_inserter(myList));
for (int i: myList)
cout << i << endl;
EDIT : performance wise it should be comparable to your solution or faster because I the searching in the set is done using a 0(log(N)) algorithm, while you have two loops.
EDIT2 : you need something that's a bit more complex than what I thought. One starting piece of advice though: you can't loop with iterators and modify the container you are iterating on at the same time. You'll need to use integer indexes.
EDIT3 : The solution below provides a way using unordered_set. You need to be able to discrimate the objects belonging to the same "groups".
#include <unordered_set>
// That's the objects we are going to "fuse"
// In this example I suppose that all Points that have the same X must be fused
struct Point
{
double x;
double y;
// We need a way to say that which points are equals
bool operator==(const Point& other) const
{
return other.x == x;
}
};
// Then we need a unique identifier to place the points in a set
namespace std {
template <> struct hash<Point> {
double operator()(const Point& point) const {
return point.x;
}
};
}
// We will use an unordered_set to put our elements in
unordered_set<Point> uniques;
// Then we can proceed as before
std::copy(myList.begin(), myList.end(), std::inserter(uniques, uniques.begin()));
myList.clear();
std::copy(uniques.begin(), uniques.end(), std::back_inserter(myList));
I think that modifying (erasing) the list inside nested loop is a bad idea. The outer loop iterator (mylistiterOutter from your example code) will not work properly.
You should make two separate loops. The first one should search for the items to be merged and somehow remember them (without erasing). The second loop would then erase the remembered items and create the new ones.
Following is what I ended up with.
It's ragged: pairs are not compared twice (0,1 and 1,0)
instances are not compared to themselves (0,0)
#include <iostream>
#include <list>
#include <cmath>
#include <algorithm>
using namespace std;
class percepUnit {
public:
int cx, cy; // location of percept in frame
bool remove; // used to delete percepts
// constructor method
percepUnit(int ix, int iy) {
cx = ix;
cy = iy;
remove = false;
}
};
bool canMerge(percepUnit unitA, percepUnit unitB) {
double dist = sqrt(pow(abs(unitA.cx-unitB.cx),2)+ pow(abs(unitA.cy-unitB.cy),2));
return (dist < 3);
}
percepUnit merge(percepUnit unitA, percepUnit unitB) {
int x,y;
x = unitA.cx+unitB.cx/2;
y = unitA.cy+unitB.cy/2;
return (percepUnit(x,y));
}
// Predicate to use remove_if to delete merge inputs.
bool removePercepsMarkedForRemoval(const percepUnit &unit) {
return unit.remove;
}
int main() {
list<percepUnit> mylist;
list<percepUnit> mergedlist;
list<percepUnit>::iterator mylistiterOutter;
list<percepUnit>::iterator mylistiterInner;
mylist.push_back(percepUnit(0,0));
mylist.push_back(percepUnit(2,2));
mylist.push_back(percepUnit(5,5));
mylist.push_back(percepUnit(7,7));
mylist.push_back(percepUnit(15,15));
for(mylistiterOutter = mylist.begin(); mylistiterOutter != mylist.end(); mylistiterOutter++) {
mylistiterInner = mylistiterOutter; // bypass the same pair twice
while (mylistiterInner != mylist.end()) {
if (canMerge(*mylistiterOutter, *mylistiterInner) and mylistiterOutter != mylistiterInner) { // bypass the same instance
mergedlist.push_back(merge(*mylistiterOutter, *mylistiterInner));
mylistiterOutter->remove = true;
mylistiterInner->remove = true;
}
mylistiterInner++;
}
}
mylist.erase(remove_if(mylist.begin(), mylist.end(), removePercepsMarkedForRemoval), mylist.end());
mylist.splice(mylist.end(), mergedlist);
return(0);
}