我有两个数组,包括x,y值,y = f(x)。我想提供一个函数来查找x的值,该值对应于y的最小或最大采样值。
在循环遍历数组中的值之前,选择适当的比较运算符的有效方法是什么?
例如,我想做类似以下的事情:
double FindExtremum(const double* x, const double* y,
const unsigned int n, const bool isMin) {
static std::less<double> lt;
static std::greater<double> gt;
std::binary_function<double,double,bool>& IsBeyond = isMin ? lt : gt;
double xm(*x), ym(*y);
for (unsigned int i=0; i<n; ++i, ++x, ++y) {
if (IsBeyond()(*y,ym)) {
ym = *y;
xm = *x;
}
}
}
不幸的是,基类std::binary_function
没有定义虚拟运算符()。
像g ++ 4.8这样的编译器是否能够优化最直接的实现?
double FindExtremum(const double* x, const double* y,
const unsigned int n, const bool isMin) {
double xm(*x), ym(*y);
for (unsigned int i=0; i<n; ++i, ++x, ++y) {
if ( ( isMin && (*y<ym)) ||
(!isMin && (*y>ym)) ) {
ym = *y;
xm = *x;
}
}
}
还有另一种方法可以安排事情让编译器更容易优化吗?有没有一个众所周知的算法呢?
如果可能,我宁愿避免使用模板化函数。
答案 0 :(得分:6)
您需要将比较仿函数作为模板化函数参数传递,例如
template <typename Compare>
double FindExtremum(const double* x, const double* y,
const unsigned int n, Compare compare) {
double xm(*x), ym(*y);
for (unsigned int i=0; i<n; ++i, ++x, ++y) {
if (compare(*y,ym)) {
ym = *y;
xm = *x;
}
}
}
然后,如果您需要运行时选择,请编写如下内容:
if (isMin) {
FindExtremum(x, y, n, std::less<double>());
} else {
FindExtremum(x, y, n, std::greater<double>());
}
在这种情况下,实际上不可能避免模板化功能。性能最佳的代码将是直接在循环中嵌入比较操作的代码,避免函数调用 - 您可以编写模板或写入此函数的两个副本。模板化函数显然是更好的解决方案。
答案 1 :(得分:1)
为了获得最高效率,请将比较运算符或比较运算符选项作为模板参数,并且不要忘记度量。
在努力实现最高效率时,进行虚拟呼叫并不是朝向目标的方向。
也就是说,这很可能是过早优化的情况,Donald Knuth如此描述:
“过早优化是所有邪恶的根源”
(我省略了他的保留,这听起来更有力。:-))
我没有参与微观优化狂热,而是浪费你的时间,而是浪费你的时间,我建议更高效地尝试使代码尽可能清晰且可证明正确。例如,使用std::vector
而不是原始数组和单独传递的大小。并且,例如,不要像另一个答案中所建议的那样调用布尔比较运算符compare
,因为这是三值比较的常规名称(例如在std::string::compare
中)。
答案 2 :(得分:1)
这里出现了一些问题。首先,我认为你的情况过于复杂。例如,有两个函数会更容易,一个计算最小值,另一个计算最大值,然后根据isMin
的值调用其中任何一个。
更多信息,请注意您在每次迭代中 如何进行测试以查看isMin
是否为真,(至少在您最后显示的“优化”代码中)而这种比较本来可以一次。
现在,如果在编译时可以以任何方式推导isMin
,则可以使用模板类来选择针对案例优化的正确实现,并且没有任何运行时开销(未经过测试,从中编写)存储器):
template<bool isMin>
class ExtremeFinder
{
static float FindExtreme(const double* x, const double* y,
const unsigned int n)
{
// Version that calculates when isMin is false
}
};
template<>
class ExtremeFinder<true>
static float FindExtreme(const double* x, const double* y,
const unsigned int n)
{
// Version that calculates when isMin is true
}
};
并将其称为ExtremeFinder<test_to_know_isMin>::FindExtreme(...);
,或者,如果您无法在编译时决定它,则可以随时执行:
if (isMin_should_be_true)
ExtremeFinder<true>::FindExtreme(...);
else
ExtremeFinder<false>::FindExtreme(...);
答案 3 :(得分:1)
如果您有2个分离标准,例如<
和>=
,您可以使用bool less
函数参数并在循环中使用XOR:
if (less ^ (a>=b))
不了解性能,但很容易写。
或不覆盖所有可能性 - 分离<
和>
:
if ( (a!=b) && (less ^ (a>b) )