是否有 STL 算法可以根据某些容差来查找容器中是否存在元素?
Posted
技术标签:
【中文标题】是否有 STL 算法可以根据某些容差来查找容器中是否存在元素?【英文标题】:Is there an STL algorithm to find if an element is present in a container based on some tolerance? 【发布时间】:2016-07-25 18:19:23 【问题描述】:我要解决的问题如下:我有一个浮点容器(双向量向量):
std::vector<std::vector<double>> dv 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0 ;
然后,假设我有一个新点(双向量):
std::vector<double> v1 0.0001, 1.0;
我想根据一些容差检查 dv 容器中是否存在点 v1。两个向量之间的距离被计算为欧几里得距离。
我已经查看了相关的问题和答案:
How to find if an item is present in a std::vector? check if a std::vector contains a certain object?并且还尝试使用std::find_if()
,但没有成功,因为它只接受一元谓词。
目前我想出了一个临时解决方案。首先,我创建了一个通用函数来查找两个向量之间的欧几里得距离:
template <typename InputIt1, typename InputIt2>
double EuclideanDistance(InputIt1 beg1, InputIt1 end1, InputIt2 beg2)
double val = 0.0;
while (beg1 != end1)
double dist = (*beg1++) - (*beg2++);
val += dist*dist;
return val > 0.0? sqrt(val) : 0.0;
其次,我创建了check_if
函数来根据容差(Epsilon)检查容器中是否存在元素:
template <typename Container, typename Element>
bool check_if(const Container& c, const Element& el,
const double Epsilon = 0.001)
auto pos = c.begin();
for (; pos != c.end(); ++pos)
if (EuclideanDistance(pos->begin(), pos->end(), el.begin()) < Epsilon)
return true;
return false;
然后我可以在这样的上下文中使用我的代码:
// Check if container contains v1 using check_if()
if (check_if(dv, v1))
std::cout << "Using check_if() - Container contains v1\n";
所以我的问题如下:
-
是否有内部的 STL 算法来实现相同的目标?如果没有,我该如何改进我的代码?例如,我不确定如何在
check_in()
中使用任何距离函数而不是EuclideanDistance()
?
我想知道 AshleysBrain 建议 (https://***.com/a/3451045/3737891) 使用 std::set
而不是 std::vector
是否会对浮点容器产生影响?
【问题讨论】:
我会将点存储在std::pair<double>
或 std::array<double,2>
- std::vector
对于固定大小的数据来说太过分了,而且您需要验证它是否始终有 2 个元素
使用捕获Epsilon
的lambda并将该lambda传递给find_if
用std::find_if
再试一次,因为我看不出有任何原因它不起作用。
使用 lambda,类似于:std::find_if(dv.begin(), dv.end(), [&](const auto&inner) return EuclideanDistance(inner.begin(), inner.end(), v1.begin()) < Epsilon; )
@Jarod42 它不能是 v1.begin()
- OP 在该向量中存储点 xy 坐标
【参考方案1】:
并且还尝试使用 std::find_if() 没有任何成功,因为它只接受一元谓词。
这就是你想要的。单个参数将是您正在查看的向量的元素。
std::vector<std::vector<double>> dv
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0;
std::vector<double> v1 0.0001, 1.0;
auto find_result =
std::find_if(begin(dv), end(dv), [&v1](std::vector<double> const &dve)
assert(dve.size() == v1.size());
return EuclideanDistance(begin(dve), end(dve), begin(v1)) < 0.001;
);
另一方面,如果您的点都是固定维度,那么使用vector<double>
可能不是正确的表示。 tuple<double,double>
、array<double 2>
或具有重载运算符的自定义类可能都是合适的替代品。
【讨论】:
在我的例子中,所有点都是 n 维向量。但是,只有在执行期间,我才会知道这些点的确切尺寸 (n),即,对于不同的运行,这些点的尺寸可能不同。然而,在同一次运行中,所有点都具有相同的维度。因此,我的目标是选择正确的容器来存储这些点,然后使用一种有效的算法来检查新点是否已经在这个容器中。【参考方案2】:是否有内部的 STL 算法来实现相同的目标?如果不, 我怎样才能改进我的代码?例如,我不确定如何使用 check_in() 中的任何距离函数而不是 EuclideanDistance()?
不完全是 STL(如果我没记错的话),但是...
正如其他人所建议的那样,对于点坐标,您应该考虑 std::tuple<double, double, ...>
,而不是 std::vector<double>
,或者,如果它们是 2D 点(你的情况,如果我没记错的话),std::pair<double, double>
。
但我建议您使用另一个 tamplate 类(但仅适用于 2D 点,不适用于 3D 点):std::complex<T>
您的dv
可能是
std::vector<std::complex<double>> dv 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0 ;
新点
std::complex<double> v1 0.0001, 1.0;
另一个建议:避免平方根(计算成本很高);检查距离的平方与Epsilon
的平方;使用std::norm
的几个复数之差,您可以准确地得到(如果我没记错的话)距离的平方
使用std::complex<double>
,bames53的例子就变得简单了
#include <vector>
#include <complex>
#include <iostream>
#include <algorithm>
int main()
std::vector<std::complex<double>> dv
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0 ;
std::complex<double> v1 0.0001, 1.0;
auto find_result =
std::find_if(begin(dv), end(dv), [&v1](std::complex<double> const &dve)
return std::norm(v1-dve) < 0.000001; );
std::cout << *find_result << std::endl; // print (0,1)
return 0;
--- 编辑---
我想知道 AshleysBrain 的建议是否 (https://***.com/a/3451045/3737891) 改为使用 std::set std::vector 的容器可能会产生任何影响 浮点数?
我认为“浮点”组件对于在std::vector
和std::set
之间进行选择并不重要。
例如,如果(对于您的应用程序)保持点的插入顺序很重要,您应该使用std::vector
(或std::queue
);如果您必须进行大量搜索,我认为std::set
更好(或者std::multiset
如果您使用带有molteplicity 的点)。
如果你决定使用std::complex<double>
,我给你另一个建议:看看std::valarray
。
我不知道你到底想用你的积分做什么,但是……我想这很有用。
p.s.:对不起我的英语不好
【讨论】:
【参考方案3】:根据您的评论(“在我的情况下,点可以是任何维数向量......”)我知道将std::complex<double>
用于点坐标(或std::pair<double, double>
,或std::tuple<double, double, ...>
)的建议不适用。
所以我建议使用std::vector<double>
,而不是std::array<double, N>
,其中N
是点的维度。
我坚持我的建议以避免平方根并检查距离平方与Epsilon
的平方。
我的第三个建议是按以下方式使用std::inner_product()
(以N == 3
为例)
#include <vector>
#include <array>
#include <numeric>
#include <iostream>
#include <algorithm>
int main()
std::vector<std::array<double, 3>> dv
0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0, 0.0, 1.0,
1.0, 1.0, 0.0, 1.0, 0.0, 1.0,
0.0, 1.0, 1.0, 1.0, 1.0, 1.0 ;
std::array<double, 3> v1 0.0001, 1.0, -0.0001 ;
auto result = std::find_if(dv.cbegin(), dv.cend(),
[&v1](std::array<double, 3> const & dve)
return std::inner_product(v1.cbegin(), v1.cend(),
dve.cbegin(), 0.0, std::plus<double>(),
[](double d1, double d2) auto d = d1-d2; return d*d; )
< 0.00001; );
std::cout << "result: ";
for ( const auto & d : *result )
std::cout << '(' << d << ')';
std::cout << std::endl;
return 0;
输出是
result: (0)(1)(0)
再次对不起我的英语。
【讨论】:
以上是关于是否有 STL 算法可以根据某些容差来查找容器中是否存在元素?的主要内容,如果未能解决你的问题,请参考以下文章