我想迭代向量的所有元素,并为每个元素检查向量的所有其他元素的条件。
逻辑:
Precondition: q is not in vector
for every x,y in vector
if d(x, y) <= d(x, q) && d(x, q) <= d(y, q) then
eliminate x, y
方法:
for(vector<Point_d>::iterator it = candidates.begin(); it != candidates.end(); ++it){
for (vector<Point_d>::iterator it2 = candidates.begin(); it2 != candidates.end(); ++it2) {
if(dist.transformed_distance(*it, *it2) <= dist.transformed_distance(*it, q)
&& dist.transformed_distance(*it, q) <= dist.transformed_distance(*it2, q)){
/* Remove x,y without invalidate the iterators */
}
}
}
我知道如果删除循环内的元素,迭代器将失效。有没有办法用Erase-Remove Idiom做到这一点,还是有其他方法可以做到这一点?我已经搜索了很多,我找到了各种可以组合起来使其工作的部分,即通过从for循环中删除迭代器并使用erase和remove_if,但我无法弄明白,也因为我是c ++和STL的新手我想听听更好的方法。
修改
如果可能,我也不想为相同的元素d(x, x) <= d(x, q)
做条件。
答案 0 :(得分:2)
您描述的概念算法可以用两种不同的方式解释:
识别集合中满足某些条件 pred (x,y)的所有元素对{x,y}。确定所有这些对后,删除所有参与其中的元素。
您提供的确切命令式伪代码版本。一旦发现元素被清除,元素就会被移除。
2.与1.的差异是,当您在元素 y 配对时检测到它满足您的谓词时,删除元素 x 之后,就会消除该元素与另一个元素 z 形成令人满意的配对的可能性,并且可能会发现没有其他元素会与 z 形成如此令人满意的配对。
第一种解释的解决方案如下:
#include <iostream>
#include <vector>
#include <algorithm>
// Finds in the container 'c' pairs of elements {x, y} such that
// pred(x, y) == true
// and removes any such elements.
// The remaining elements may be reordered.
// pred is assumed to be commutative, i.e.
// pred(x, y) == pred(y, x)
template<class C, class Pred>
void remove_element_pairs(C& c, Pred pred)
{
typedef typename C::iterator It;
typedef typename C::value_type X;
It e = c.end();
for ( It it = c.begin(); it != e; )
{
const X& a = *it;
const auto boundPred = [&pred, a](const X& x) -> bool { pred(a, x); };
if ( c.end() == find_if(std::next(it), c.end(), boundPred) )
++it;
else
std::swap(*it, *--e);
}
c.erase(e, c.end());
}
int main()
{
std::vector<int> v{1, 2, 7, 0, 0, 4, 4, 5};
remove_element_pairs(v, [](int a, int b) -> bool { return a + b == 8; });
for(int x : v)
std::cout << x << " ";
return 0;
}
答案 1 :(得分:2)
如果使用索引,您的工作可能会更容易,如下面的代码所示。我有一个稍微修改过的live demo,表明这有效。 (我将dist
函数替换为只返回整数abs(x-y)
的函数。
for(size_t i=0; i<candidates.size();){
bool deleted = false;
for (size_t j = 0; j < candidates.size();) {
if(i==j) {
++j;
continue;
}
if(dist.transformed_distance(candidates[i], candidates[j]) <= dist.transformed_distance(candidates[i], q)
&& dist.transformed_distance(candidates[i], q) <= dist.transformed_distance(candidates[j], q)){
bool dec_i = false;
if(i < j) --j;
else dec_i = true;
candidates.erase(std::next(candidates.begin(), i));
candidates.erase(std::next(candidates.begin(), j));
if(dec_i) --i;
deleted = true;
break;
}
else
++j;
}
if(!deleted)
++i;
}
请注意,您可以使用替代实现来标记要在第一次传递中删除的元素,然后删除它们。在这种情况下,行为是不同的:由于元素未被删除,它们仍被考虑用于以后的对匹配。因此,单个元素可以与多于一个的元素配对以进行删除,并且最终可能移除比上述元素更多的元素。这次的成本是O(n ^ 2)而不是O(n ^ 3)。现场演示here:
std::vector<int> deleteIndices;
deleteIndices.reserve(candidates.size());
for(size_t i=0; i<candidates.size(); ++i){
for (size_t j = 0; j < candidates.size(); ++j) {
if(i==j) {
continue;
}
if(dist.transformed_distance(candidates[i], candidates[j]) <= dist.transformed_distance(candidates[i], q)
&& dist.transformed_distance(candidates[i], q) <= dist.transformed_distance(candidates[j], q)){
deleteIndices.push_back(i);
deleteIndices.push_back(j);
}
}
}
std::sort(deleteIndices.begin(), deleteIndices.end());
auto unique_end = std::unique(deleteIndices.begin(), deleteIndices.end());
//I'm using this complicated thing as the template param just because I don't know what your element type is
std::vector<std::remove_reference_t<decltype(candidates[0])>> output;
output.reserve(candidates.size() - deleteIndices.size());
auto it = deleteIndices.begin();
auto i = 0;
std::copy_if(candidates.begin(), candidates.end(), std::back_inserter(output), [&it,&i,unique_end](int elem)
{
if(it==unique_end) {
++i;
return true;
}
if(i == *it) {
++i;
++it;
return false;
}
++i;
return true;
});
答案 2 :(得分:1)
我真的不认为你能比O更好(&lt; = N ^ 2)
#include <vector>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <iterator>
struct point_d {
double x, y;
};
std::ostream& operator<<(std::ostream& os, const point_d& p)
{
return os << "(" << p.x << ", " << p.y << ")";
}
double distance(const point_d& l, const point_d& r)
{
return std::sqrt(std::pow(r.x-l.x, 2) + std::pow(r.y-l.y,2));
}
using d_array = std::vector<point_d>;
//for every x,y in vector if d(x, y) <= d(x, q) && d(x, q) <= d(y, q) then eliminate x, y. q is not in vector.
d_array remove_q(const d_array& vec, point_d q)
{
d_array result;
std::vector<char> dropped(vec.size(), 0); // note! avoid vector<bool>
result.reserve(vec.size());
auto is_dropped = [&](auto& p)
{
return dropped[std::distance(vec.data(), std::addressof(p))];
};
auto drop = [&](auto& p)
{
dropped[std::distance(vec.data(), std::addressof(p))] = 1;
};
auto should_drop = [&](auto& x) {
for (auto& y : vec)
{
if (is_dropped(y))
return true;
if (std::addressof(x) != std::addressof(y))
{
if (distance(x, y) <= distance(x, q)
and distance(x, q) <= distance(y, q))
{
return true;
}
}
}
return false;
};
for (auto& x : vec) {
if (not should_drop(x))
result.push_back(x);
else
drop(x);
}
return result;
}
int main()
{
d_array v = {
point_d{ 0, 0},
point_d{ 1, 1},
point_d{ 0.5, 0.5 },
point_d{ 0.4, 0.4 },
point_d{ 0.25, 0.25 }
};
auto v2 = remove_q(v, {0.45, 0.45});
std::copy(v2.begin(), v2.end(), std::ostream_iterator<point_d>(std::cout, ", "));
std::cout << std::endl;
}