到目前为止,所有答案每次都重新计算阶乘。我肯定不会那样做的。相反,你可以写:
float cos(int n, float x)
{
if (n > MAX)
return 1;
return 1 - x*x / ((2 * n - 1) * (2 * n)) * cos(n + 1, x);
}
考虑 cos 返回以下内容(对于点的位置感到抱歉):
您可以看到,对于 n>MAX、n=MAX 等,这是正确的。 x 的符号交替和幂很容易看到。
最后,在 n=1 时,你得到 0! = 1,因此调用 cos(1, x) 可以获得 cos 的泰勒展开式的第一个 MAX 项。
通过开发(当它有几个术语时更容易看到),您可以看到第一个公式等效于以下内容:
对于 n > 0,您在 cos(n-1, x) 中将前一个结果除以 (2n-3)(2n-2),然后乘以 x²。可以看到当n=MAX+1这个公式为1,n=MAX则为1-x²/((2MAX-1)2MAX),以此类推。
如果您允许自己使用辅助函数,那么您应该将上面的签名更改为float cos_helper(int n, float x, int MAX) 并像这样调用它:
float cos(int n, float x) { return cos_helper(1, x, n); }
编辑:将n 的含义从评估项的程度(如目前为止的答案中)反转为项数(如问题中及以下),但仍不会每次都重新计算总阶乘,我建议使用二项关系。
让我们简单地定义cos(0,x) = 0和cos(1,x) = 1,并尝试一般地实现泰勒级数前n项之和的cos(n,x)。
那么对于每个 n > 0,我们可以写出 cos(n,x) from cos(n-1,x) :
cos(n,x) = cos(n-1,x) + x2n / (2n)!
现在对于 n > 1,我们尝试让 cos(n-1,x) 的最后一项出现(因为它是最接近我们要添加的一项):
cos(n,x) = cos(n-1,x) + x² / ((2n-1)2n) * ( x2n-2 / (2n-2)!)
通过将此公式与前一个公式相结合(将其调整为 n-1 而不是 n):
cos(n,x) = cos(n-1,x) + x² / ((2n-1)2n) * ( cos(n-1,x) - cos(n-2,x) )
我们现在有了 cos(n,x) 的纯递归定义,没有辅助函数,没有重新计算阶乘,并且 n 是泰勒分解之和中的项数。
但是,我必须强调以下代码的性能非常糟糕:
- 性能方面,除非某些优化允许不重新评估在上一步中评估为
cos( (n-1) - 1, x) 的 cos(n-1,x)
- 精度方面,因为抵消效应:我们得到 x2n-2 / (2n-2) 的精度!很糟糕
现在这个免责声明已经到位,代码来了:
float cos(int n, float x)
{
if (n < 2)
return n;
float c = x * x / (2 * (n - 1) * 2 * n);
return (1-c) * cos(n-1, x) + c * cos(n-2, x);
}