假设我有一个功能
double f(vector <double> &x) {
// do something with x
return answer;
}
在数学上,f
是x
的每个组成部分的连续函数。现在我想评估x
的数值梯度。有两种方法如下
方法1。
double DELTA = 1e-5;
double y1 = f(x);
vector <double> gradX(x.size());
for (int i = 0; i < x.size(); ++i) {
x[i] += DELTA;
double y2 = f(x);
gradX[i] = (y2 - y1) / DELTA;
x[i] -= DELTA;
}
方法2。
double DELTA = 1e-5;
vector <double> gradX(x.size());
for (int i = 0; i < x.size(); ++i) {
x[i] += DELTA;
double y2 = f(x);
x[i] -= 2.0 * DELTA;
double y1 = f(x);
gradX[i] = (y2 - y1) / (2.0 * DELTA);
x[i] += DELTA;
}
我观察到方法1 给出非常不合理的数字(6位数字),而方法2 给出更合理的数字。
方法2 是否更好?应该总是首选吗?
感谢。
修改:了解更多内容。这些实现在C中完成,并使用了一些CUDA内核。
答案 0 :(得分:1)
这是预期的结果,因为方法-1是一阶精确(前向差),而方法-2是二阶精确方法(中心差)。这可以使用Taylor系列轻松证明。有关详细信息,您可以阅读有关有限差分方法的任何书籍。
要获得与一阶方法类似的精度,与二阶方法相比,您必须使用较小的DELTA作为第一阶方法。
从您的实现中可以清楚地看出,方法-2的成本更高,(对每个x评估f1和f2),使用具有较小DELTA的方法-1将是有益的。但是,如果准确性更受关注,那么您可以使用方法-2,使用较小的DELTA。