我正在尝试编写一种能够执行N维混合偏导数的算法。我知道我需要能够实现什么,但是我似乎无法想出实现N维案例所需的正确循环/递归。
以下是前4个维度的模式:
| 1D wzyx | 2D | 3D | 4D |
----------------------------------------------------------
| dx (0001) | dx (0001) | dx (0001) | dx (0001) |
| | dy (0010) | dy (0010) | dy (0010) |
| | dyx (0011) | dyx (0011) | dyx (0011) |
| | | dz (0100) | dz (0100) |
| | | dzx (0101) | dzx (0101) |
| | | dzy (0110) | dzy (0110) |
| | | dzyx (0111) | dzyx (0111) |
| | | | dw (1000) |
| | | | dwx (1001) |
| | | | dwy (1010) |
| | | | dwyx (1011) |
| | | | dwz (1100) |
| | | | dwzx (1101) |
| | | | dwzy (1110) |
| | | | dxyzw (1111) |
每个维度的导数(因为它遵循二元模式)是(2 ^ dim)-1;例如,2 ^ 3 = 8-1 = 7。
dyx的导数是y维中相邻点的dx值。这适用于所有混合部分。因此dzyx是z维度中相邻点的dyx。我不确定这个段落是否是该问题的相关信息,只是我认为这是为了完整性。
欢迎任何帮助指针建议。粗体部分是我需要实现的部分。
:: EDIT ::
我将通过提供一个我需要的例子来尝试更明确一些。这只是一个2D案例,但它可以说明我认为的整个过程。
我需要帮助提出将在列dx,dy,dyx等中生成值的算法。人
| X | Y | f(x, y) | dx | dy | dyx |
-------------------------------------------------------------------------
| 0 | 0 | 4 | (3-4)/2 = -0.5 | (3-4)/2 | (-0.5 - (-2.0))/2 |
| 1 | 0 | 3 | (0-4)/2 = -2.0 | (2-3)/2 | (-2.0 - (-2.0))/2 |
| 2 | 0 | 0 | (0-3)/2 = -1.5 | (-1-0)/2 | (-1.5 - (-1.5))/2 |
| 0 | 1 | 3 | (2-3)/2 = -0.5 | (0-4)/2 | (-0.5 - (-0.5))/2 |
| 1 | 1 | 2 | (-1-3)/2 = -2.0 | (-1-3)/2 | (-1.5 - (-2.0))/2 |
| 2 | 1 | -1 | (-1-2)/2 = -1.5 | (-4-0)/2 | (-1.5 - (-1.5))/2 |
| 0 | 2 | 0 | (-1-0)/2 = -0.5 | (0-3)/2 | (-0.5 - (-0.5))/2 |
| 1 | 2 | -1 | (-4-0)/2 = -2.0 | (-1-2)/2 | (-2.0 - (-2.0))/2 |
| 2 | 2 | -4 |(-4--1)/2 = -1.5 |(-4--1)/2 | (-1.5 - (-1.5))/2 |
f(x,y)未知,只知道其值;因此,分析区分是没有用的,它必须只是数字。
欢迎任何帮助指针建议。粗体部分是我需要实现的部分。
::编辑 - 再次::
答案 0 :(得分:4)
通过函数式编程可以彻底解决这个问题。实际上,\ partial_ {xy} f是沿着\ partial_y f的x的偏导数。
我想你有一个黑盒函数(或函数对象)f
,将其值作为指向内存缓冲区的指针。它的签名被认为是
double f(double* x);
现在,这是一个将(二阶有限差分)偏导数得到f:
的代码template <typename F>
struct partial_derivative
{
partial_derivative(F f, size_t i) : f(f), index(i) {}
double operator()(double* x)
{
// Please read a book on numerical analysis to tweak this one
static const double eps = 1e-4;
double old_xi = x[index];
x[index] = old_xi + eps;
double f_plus = f(x);
// circumvent the fact that a + b - b != a in floating point arithmetic
volatile actual_eps = x[index];
x[index] = old_xi - eps;
actual_2eps -= x[index]
double f_minus = f(x);
return (f_plus - f_minus) / actual_2eps;
}
private:
F f;
size_t index;
};
template <typename F>
partial_derivative<F> partial(F f, index i)
{
return partial_derivative<F>(f, i);
}
现在,要计算\ partial_ {123} f,您可以:
boost::function<double(double*)> f_123 = partial(partial(partial(f, 0), 1), 2);
如果您需要全部计算:
template <typename F>
boost::function<double(double*)> mixed_derivatives(F f, size_t* i, size_t n_indices)
{
if (n_indices == 0) return f;
else return partial(mixed_derivatives(f, i + 1, n_indices - 1), i[0]);
}
所以你可以这样做:
size_t indices[] = { 0, 1, 2 };
boost::function<double(double*)> df_123
= mixed_derivatives(f, indices, sizeof(indices) / sizeof(size_t));
答案 1 :(得分:2)
如果我理解正确,我认为以下内容可行:
function partial_dev(point, dimension):
neighbor_selector = top_bit(dimension)
value_selector = dimension XOR neighbor_selector
prev_point = point_before(point,neighbor_selector)
next_point = pointafter(point,neighbor_selector)
if value_selector == 0:
return (f[prev_point] - f[next_point])/2
else:
return ( partial_dev(prev_point, value_selector) -
partial_dev(next_point, value_selector) )/2
这个想法是:维度值的最高位是选择前后点的坐标。如果维值的其余部分为0,则使用f
值作为偏导数计算的点。如果不是,则得到其余位表示的偏导数来计算值。
如果需要计算所有维值的所有值,则根本不需要递归:只需使用维度选择器作为数组索引,其中每个数组元素都包含为该维度设置的完整值。数组初始化为vals[0][coords] = f(coords)
。然后计算vals[1]
,vals[2]
,并在计算vals[3]
时,使用vals[1]
作为值表而不是vals[0]
(因为3 = 0b11
其中,邻居选择器为0b10
,而value_selector为0b01
)。
答案 2 :(得分:1)
看起来您可能只是根据维度(二进制位数)进行循环,然后递归到下一个二进制数字。
粗糙(不是C ++)伪代码:
Function partialcalc(leadingdigit, dimension)
If dimension > 1 {
For i = 1 to dimension {
//do stuff with these two calls
partialcalc(0, i - 1)
partialcalc(1, i - 1)
}
}
Else {
//partialcalc = 1D case
}
return partialcalc
End Function
递归的工作方式是你有一个问题,它可以被分解成等同于较大问题的子问题,只是更小。因此,由于您将所有二进制数字用于维度位置,因此您只需通过基于维度数字中的0和1值递归到两个子问题来对顶部维度进行计算。递归的底部是维度= 1级别。既然你强调你只需要弄清楚如何构造循环递归,并且已经计算出数学,那么这个结构应该适合你。
答案 3 :(得分:0)
嗯,为了从答案开始,我的第一个想法是用Chebyshev多项式进行插值。然后可以容易地区分(或集成)近似。
Gnu Scientific Library有一个实现。
注意,我不是数值方法方面的专家,所以我无法解释这种方法的问题。但是,如果您想要局部近似,它应该可以正常工作。如果有人知道更好,请随意投票。