计算序列的余弦值

时间:2010-03-01 18:10:42

标签: algorithm math complexity-theory trigonometry

我必须计算以下内容:

float2 y = CONSTANT;
for (int i = 0; i < totalN; i++)
   h[i] = cos(y*i);

totalN是一个很大的数字,所以我想以更有效的方式做到这一点。有没有办法改善这个?我怀疑有,因为,毕竟,我们知道cos(n)的结果是什么,对于n = 1..N,所以也许有一些定理允许我以更快的方式计算它。我真的很感激任何提示。

提前致谢,

费德里科

9 个答案:

答案 0 :(得分:6)

使用最美丽的数学公式之一,Euler's formula
exp(i*x) = cos(x) + i*sin(x)

替换x := n * phi

cos(n*phi) = Re( exp(i*n*phi) )
sin(n*phi) = Im( exp(i*n*phi) )

exp(i*n*phi) = exp(i*phi) ^ n

权力^nn重复乘法。 因此,您可以通过从cos(n*phi)开始的sin(n*phi)重复复数乘法来计算exp(i*phi)(1+i*0)

代码示例:

的Python:

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)

或C:

#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);
  }
} 

答案 1 :(得分:6)

我不确定你愿意做出什么样的准确性与性能妥协,但是在这些链接上有各种正弦曲线近似技术的广泛讨论:

有趣的Sinusoids - http://www.audiomulch.com/~rossb/code/sinusoids/
快速准确的正弦/余弦 - http://www.devmaster.net/forums/showthread.php?t=5784

编辑(我认为这是在“与Sinusoids的乐趣”页面上打破的“Don Cross”链接):

优化Trig计算 - http://groovit.disjunkt.com/analog/time-domain/fasttrig.html

答案 2 :(得分:4)

也许最简单的公式是

  

cos(n + y)= 2cos(n)cos(y) - cos(n-y)。

如果预先计算常数2 * 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]

答案 3 :(得分:3)

这是一种方法,但它使用了一点点内存来处理罪恶。它使用trig标识:

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;

}

如果我没有犯任何错误那么应该这样做。当然可能存在四舍五入的问题,所以要注意这一点。我用Python实现了它,它非常准确。

答案 4 :(得分:1)

这里有一些很好的答案,但它们都是递归的。使用浮点运算时,递归计算对余弦函数不起作用;你总会得到四舍五入的错误,很快就会复杂化。

考虑计算y = 45度,总计N 10 000.最终结果不会以1结尾。

答案 5 :(得分:1)

解决Kirk关注的问题:所有基于cos和sin重现的解决方案归结为计算

x(k)= R x(k-1),

其中R是由y旋转的矩阵,x(0)是单位矢量(1,0)。如果k-1的真实结果是x'(k-1)并且k的真实结果是x'(k),则误差从e(k-1)= x(k-1)-x'变为(k-1)至e(k)= R x(k-1)-R x'(k-1)= R e(k-1)的线性。由于R是所谓的正交矩阵,因此R e(k-1)具有与e(k-1)相同的范数,并且误差增长得非常慢。 (它完全增长的原因是由于四舍五入; R的计算机表示通常差不多,但不完全正交,因此有必要根据精确度不时使用trig操作重新开始重复这仍然比使用trig运算来计算每个值要快得多。)

答案 6 :(得分:0)

您可以使用复数来执行此操作。

如果定义x = sin(y)+ i cos(y),则cos(y * i)将是x ^ i的实部。

你可以迭代地计算所有i。复数乘法是2乘以加2。

答案 7 :(得分:0)

知道cos(n)没有帮助 - 你的数学库已经为你做了这些琐碎的事情。

知道cos((i + 1)y)= cos(i y + y)= cos(i y) cos(y)-sin(i y) sin(y)可以帮助,如果你预先计算cos(y)和sin(y),并沿途跟踪cos(i y)和sin(i * y)。但是,它可能会导致一些精度损失 - 你必须检查。

答案 8 :(得分:0)

您需要得到的cos(x)有多准确?如果您可以使用某些,您可以创建一个查找表,以2 * PI / N间隔对单位圆进行采样,然后在两个相邻点之间进行插值。选择N将达到一定的准确度。

我不知道插值是否实际上比计算余弦更便宜。由于它通常在现代CPU中的微码中完成,因此可能不是。