float2 y = CONSTANT;
for (int i = 0; i < totalN; i++)
h[i] = cos(y*i);
totalN是一个很大的数,所以我希望能够更高效地进行计算。有没有什么方法可以提高效率?我怀疑肯定有这种方法,毕竟我们知道当n=1..N时cos(n)的结果,所以也许有某个定理可以让我更快地计算出结果。如果有任何提示,我将不胜感激。
谢谢您提前的帮助!
Federico
float2 y = CONSTANT;
for (int i = 0; i < totalN; i++)
h[i] = cos(y*i);
totalN是一个很大的数,所以我希望能够更高效地进行计算。有没有什么方法可以提高效率?我怀疑肯定有这种方法,毕竟我们知道当n=1..N时cos(n)的结果,所以也许有某个定理可以让我更快地计算出结果。如果有任何提示,我将不胜感激。
谢谢您提前的帮助!
Federico
x := n * phi
:
exp(i*x) = cos(x) + i*sin(x)
,cos(n*phi) = Re( exp(i*n*phi) )
sin(n*phi) = Im( exp(i*n*phi) )
又因为exp(i*n*phi) = exp(i*phi) ^ n
,所以可以通过重复复杂乘法的方式,使用exp(i*phi)
来计算cos(n*phi)
和同时sin(n*phi)
,从(1+i*0)
开始。from math import *
DEG2RAD = pi/180.0 # conversion factor degrees --> radians
phi = 10*DEG2RAD # constant e.g. 10 degrees
c = cos(phi)+1j*sin(phi) # = exp(1j*phi)
h=1+0j
for i in range(1,10):
h = h*c
print "%d %8.3f"%(i,h.real)
#include <stdio.h>
#include <math.h>
// numer of values to calculate:
#define N 10
// conversion factor degrees --> radians:
#define DEG2RAD (3.14159265/180.0)
// e.g. constant is 10 degrees:
#define PHI (10*DEG2RAD)
typedef struct
{
double re,im;
} complex_t;
int main(int argc, char **argv)
{
complex_t c;
complex_t h[N];
int index;
c.re=cos(PHI);
c.im=sin(PHI);
h[0].re=1.0;
h[0].im=0.0;
for(index=1; index<N; index++)
{
// complex multiplication h[index] = h[index-1] * c;
h[index].re=h[index-1].re*c.re - h[index-1].im*c.im;
h[index].im=h[index-1].re*c.im + h[index-1].im*c.re;
printf("%d: %8.3f\n",index,h[index].re);
}
}
我不确定您愿意做出什么样的准确性与性能妥协,但是这些链接中有关于各种正弦近似技术的广泛讨论:
玩转正弦曲线 - http://www.audiomulch.com/~rossb/code/sinusoids/
快速而准确的正弦/余弦 - http://www.devmaster.net/forums/showthread.php?t=5784
编辑(我认为这是“Fun with Sinusoids”页面上损坏的“Don Cross”链接):
优化三角函数计算 - http://groovit.disjunkt.com/analog/time-domain/fasttrig.html
如果您预先计算常量2*cos(y),那么每个值cos(n+y)都可以通过前两个值进行一次乘法和一次减法计算得出。 即,伪代码如下:cos(n+y) = 2cos(n)cos(y) - cos(n-y).
h[0] = 1.0
h[1] = cos(y)
m = 2*h[1]
for (int i = 2; i < totalN; ++i)
h[i] = m*h[i-1] - h[i-2]
这里有一种方法,但是它需要一点内存来存储sin值。它使用三角恒等式:
cos(a + b) = cos(a)cos(b)-sin(a)sin(b)
sin(a + b) = sin(a)cos(b)+cos(a)sin(b)
h[0] = 1.0;
double g1 = sin(y);
double glast = g1;
h[1] = cos(y);
for (int i = 2; i < totalN; i++){
h[i] = h[i-1]*h[1]-glast*g1;
glast = glast*h[1]+h[i-1]*g1;
}
这里有一些不错的答案,但它们都是递归的。当使用浮点算术计算余弦函数时,递归计算将无法工作;您将不可避免地得到舍入误差,这些误差会很快累积。
考虑计算 y = 45 度,totalN 为 10,000。您最终不会得到 1 作为结果。
你可以使用复数来实现这个功能。
如果你定义 x = sin(y) + i cos(y),cos(y*i) 将是 x^i 的实部。
你可以通过迭代计算所有的 i。复数乘法是 2 次乘法加上两次加法。
知道cos(n)并没有帮助——你的数学库已经为你处理了这些微不足道的事情。
但是,如果你预先计算cos(y)和sin(y),并在途中跟踪cos(i*y)和sin(i*y),那么知道cos((i+1)y)=cos(iy+y)=cos(iy)cos(y)-sin(iy)sin(y)可能会有所帮助。不过这可能会导致一些精度损失,你需要进行检查。
h[i]
,或许为了以后使用。所以真正的问题很可能是插值和计算之间的选择,就像Larry提到的那样,如果可以避免插值(通过选择足够大的N),这可能正是OP正在寻找的。这个解决方案还有一个优点,就是不会出现浮点累积误差。 - Aryabhatta