计算序列的余弦
我必须计算以下内容:
float2 y = CONSTANT;
for (int i = 0; i < totalN; i++)
h[i] = cos(y*i);
totalN 是一个很大的数字,所以我想以更有效的方式进行计算。有什么办法可以改善这一点吗?我怀疑是有的,因为毕竟我们知道 n=1..N 时 cos(n) 的结果是什么,所以也许有一些定理可以让我以更快的方式计算这个结果。我真的很感激任何提示。
预先感谢,
费德里科
I have to calculate the following:
float2 y = CONSTANT;
for (int i = 0; i < totalN; i++)
h[i] = cos(y*i);
totalN is a large number, so I would like to make this in a more efficient way. Is there any way to improve this? I suspect there is, because, after all, we know what's the result of cos(n), for n=1..N, so maybe there's some theorem that allows me to compute this in a faster way. I would really appreciate any hint.
Thanks in advance,
Federico
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
使用最美丽的数学公式之一,欧拉公式
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
幂
^n
是n
次重复乘法。因此,您可以通过重复复数乘以
exp(i*phi)
来计算cos(n*phi)
并同时计算sin(n*phi)
从(1+i*0)
开始。代码示例:
Python:
或C:
Using one of the most beautiful formulas of mathematics, Euler's formula
exp(i*x) = cos(x) + i*sin(x)
,substituting
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
Power
^n
isn
repeated multiplications.Therefore you can calculate
cos(n*phi)
and simultaneouslysin(n*phi)
by repeated complex multiplication byexp(i*phi)
starting with(1+i*0)
.Code examples:
Python:
or C:
我不确定您愿意在精度与性能之间做出什么样的妥协,但在这些链接中对各种正弦曲线近似技术进行了广泛的讨论:
正弦曲线的乐趣 - 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
I'm not sure what kind of accuracy vs. performance compromises you're willing to make, but there are extensive discussions of various sinusoid approximation techniques at these links:
Fun with Sinusoids - http://www.audiomulch.com/~rossb/code/sinusoids/
Fast and accurate sine/cosine - http://www.devmaster.net/forums/showthread.php?t=5784
Edit (I think this is the "Don Cross" link that's broken on the "Fun with Sinusoids" page):
Optimizing Trig Calculations - http://groovit.disjunkt.com/analog/time-domain/fasttrig.html
也许最简单的公式是
如果预先计算常数 2*cos(y),则可以通过一次乘法和一次减法根据前 2 个值计算每个值 cos(n+y)。
即,用伪代码
Maybe the simplest formula is
If you precompute the constant 2*cos(y) then each value cos(n+y) can be computed from the previous 2 values with one single multiplication and one subtraction.
I.e., in pseudocode
这是一个方法,但它使用了一点内存。它使用三角恒等式:
然后这是代码:
如果我没有犯任何错误,那么应该可以做到。当然,可能存在舍入问题,因此请注意这一点。我用Python实现了这个,而且非常准确。
Here's a method, but it uses a little bit of memory for the sin. It uses the trig identities:
Then here's the code:
If I didn't make any errors then that should do it. Of course there could be round-off problems so be aware of that. I implemented this in Python and it is quite accurate.
这里有一些很好的答案,但它们都是递归的。使用浮点运算时,递归计算不适用于余弦函数;您总是会遇到舍入误差,这些误差很快就会复合。
考虑计算 y = 45 度,totalN 10 000。最终结果不会是 1。
There are some good answers here but they are all recursive. Recursive calculation will not work for cosine function when using floating point arithmetic; you will invariably get rounding errors which quickly compound.
Consider calculation y = 45 degrees, totalN 10 000. You won't end up with 1 as the final result.
为了解决 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) = Re (k - 1) 通过线性关系。由于R是所谓的正交矩阵,因此R e(k - 1) 与e(k - 1) 具有相同的范数,并且误差增长非常缓慢。 (它增长的原因是由于舍入;R 的计算机表示一般来说几乎是正交的,但不完全正交,因此有必要根据精度不时地使用三角运算重新启动递归这仍然比使用三角运算来计算每个值要快得多。)
To address Kirk's concerns: all of the solutions based on the recurrence for cos and sin boil down to computing
x(k) = R x(k - 1),
where R is the matrix that rotates by y and x(0) is the unit vector (1, 0). If the true result for k - 1 is x'(k - 1) and the true result for k is x'(k), then the error goes from e(k - 1) = x(k - 1) - x'(k - 1) to e(k) = R x(k - 1) - R x'(k - 1) = R e(k - 1) by linearity. Since R is what's called an orthogonal matrix, R e(k - 1) has the same norm as e(k - 1), and the error grows very slowly. (The reason it grows at all is due to round-off; the computer representation of R is in general almost, but not quite orthogonal, so it will be necessary to restart the recurrence using the trig operations from time to time depending on the accuracy required. This is still much, much faster than using the trig ops to compute each value.)
您可以使用复数来做到这一点。
如果定义 x = sin(y) + i cos(y),cos(y*i) 将是 x^i 的实部。
您可以迭代计算所有 i 。复数乘法是 2 次乘法加两次加法。
You can do this using complex numbers.
if you define x = sin(y) + i cos(y), cos(y*i) will be the real part of x^i.
You can compute for all i iteratively. Complex multiply is 2 multiplies plus two adds.
知道 cos(n) 并没有帮助——你的数学库已经为你做了这些琐碎的事情。
知道 cos((i+1)y)=cos(iy+y)=cos(iy)cos(y)-sin(iy)<如果您预先计算 cos(y) 和 sin(y),并一路跟踪 cos(iy) 和 sin(i*y),em>sin(y) 会有所帮助。不过,这可能会导致一些精度损失 - 您必须进行检查。
Knowing cos(n) doesn't help -- your math library already does these kind of trivial things for you.
Knowing that cos((i+1)y)=cos(iy+y)=cos(iy)cos(y)-sin(iy)sin(y) can help, if you precompute cos(y) and sin(y), and keep track of both cos(iy) and sin(i*y) along the way. It may result in some loss of precision, though - you'll have to check.
您需要得到的 cos(x) 有多准确?如果您可以忍受一些,您可以创建一个查找表,以 2*PI/N 间隔对单位圆进行采样,然后在两个相邻点之间进行插值。将选择 N 以达到某种所需的精度水平。
我不知道插值是否实际上比计算余弦成本更低。由于它通常在现代 CPU 中以微代码完成,因此可能不是。
How accurate do you need the resulting cos(x) to be? If you can live with some, you could create a lookup table, sampling the unit circle at 2*PI/N intervals and then interpolate between two adjacent points. N would be chosen to achieve some desired level of accuracy.
What I don't know is whether an interpolation is actually less costly than computing a cosine. Since its usually done in microcode in modern CPUs, it may not be.