如何高效地計算sin(x)與cos(x)的值?
sin(x)=x-x^3/3!+x^5/5!-。。。。 我覺得這個收斂比較慢, 有沒有收斂比較快的級數呢?, 就好比pi有收斂比較快的級數, 也有收斂比較慢的級數, 同樣cos(x)呢? 我又查到一個連分數計算方法, 請問有沒有比較好的精度高的辦法?
。。。。。。。。。。。。2015.06.19更新。萬能公式。sin(x)=2t/(1+t^2)t=tan(x/2)
然後對t泰勒展開,是否更快?
如果固定精度,可以用Cordic之類的查表法解決。
如果需要任意高精度,演算法就不那麼直接了,我貼一個apfloat庫用的演算法吧,假定輸入是實數。
分三步
第一步利用關係式
所以且這樣,我們只要求得就萬事大吉了第二步假定我們有了的演算法,那麼計算就是解方程,可以用牛頓迭代法解決,迭代公式為。
第三步,我們需要的任意精度演算法,基本原理是用Arithmetic–Geometric Mean迭代。
先解釋一下AGM的原理,選定後,進行迭代,那麼序列有公共極限,記作。
現在有定理,對且有
且此式子對於為複數也成立(當然)。所以,我們可以對逼近到任意精度,當然必須把的縮放下,使得,否則AGM不收斂。這樣的演算法收斂速度指數級的。Sin是周期函數,可以選擇0-45度的部分計算( @王希 已經說了,周期性和誘導函數)。
對於硬體來說:
0. 利用周期性縮小定義域。
1. 有限精度下的Sin和Cos都使用查表法(CORDIC)。2. 擴展精度的部分用泰勒展開。
對於軟體來說:
1. 有限精度可以選擇多項式逼近;
2. 擴展精度用泰勒展開。-------------------------------------------------
StackOverflow:c++ - How does C compute sin() and other math functions?有比泰勒級數更好的做法,簡單來講,sin(x)是函數空間中的一個矢量,然後5次多項式構成的空間是函數空間的一個子空間,sin(x)在五次多項式空間上的正交投影就是對sin(x)的最佳五次多項式逼近,這個近似在整個周期內的精度都非常高。這個五次多項式的表達式是
y = 0.987862x - 0.155271x^3 + 0.00564312x^5具體內容見《線性代數應該怎麼學》第六章。
--------------------
為了方便大家,具體步驟列在下面求sin(x)的逼近等價於求使如下積分最小的函數f(x)為了找到這樣的f(x)可定義如下內積令,為次數不超過五的實係數多項式組成的子空間,那麼問題重述為找到使得最小,由正交投影的性質可知,當時該值最小。為了求得正交投影,首先需要知道的一組規範正交基,對的基進行格拉姆-施密特正交化過程可得規範正交基為最後,可求得前面的都說的很具體,幾乎所有的解析函數的數值計算都是通過Pade 近似來獲得的,可以搜一下pade近似,
謝邀。我還沒有無聊到假裝被邀請。
因為它們都是奇函數或者偶函數,偶次冪/奇次冪均為0,因此級數展開其實是完全沒問題的,重要的是展開之前的處理:- 利用周期性把變成中的數,
- 再利用奇偶性把變成中的數,
- 再用誘導公式把變成中的數,
- 用誘導公式把變成中的數
- 最後級數展開。
例如要計算,就有
(這裡解釋一下,泰勒級數無論展開多少項,只要這個項數設定好就是的演算法,所以能做的就是優化常數。而計算機存儲實數的精度是有限的,一個操作如果即有利於速度(少展開)又有利於減小精度損失(比如上述處理),我就認為它是有意義的。)
由於二者都是交錯級數,故誤差不會超過最後一項的絕對值。上述公式可以將的絕對值減小到不超過,即精度可以近似為.我們看看其收斂情況:
注意到正弦函數和餘弦函數都是隔項的,因此實際需要的項數是的一半。也就是說達到9位精度只需要5項即可。這已經是非常不錯的了。而且這裡列舉的是最大可能的誤差,實際的誤差比這個誤差要小一些。個人認為無論如何,這個周期性都是必須利用的,再精確的多項式直接把十萬帶進去肯定抓瞎。不過如評論區知友所說,後邊幾步可能意義不大。而答主才疏學淺,沒聽過simd和cordic。
當然了,如果確定區間為,我們就可以先把最佳近似多項式設出來,比如.然後設目標函數
取目標函數最小值,求出。結果會和泰勒展開有一定差別,但只要自變數足夠小,實際差距也沒有多大。cordic演算法介紹,簡單容易明白!http://m.blog.csdn.net/blog/liyuanbhu_11109/8458769
Cordic
sicp上看來的。主要是,其他情況下我試了一下,當abs(aph)&<1e-14時令sin(aph)返回aph,大概就跟c函數math庫算出的一樣了。
static double Cube(double x){
return x*x*x;
}
static double p(double x){
return 3 * x - 4 * Cube(x);
}
static double ABS(double x){
return x &> 0 ? x : -x;
}
static double Sin(double angle){
if (ABS(angle) &<= 1e-14) {
return angle;
}
return p(Sin(angle / 3.0));
}
查表法,簡單粗暴!
https://github.com/golang/go/blob/master/src/math/sin.go
推薦閱讀:
※彈性力學裡的平面問題差分解中如何區分向前差分和向後差分?
※為什麼不動點迭代法的收斂與其導數有關?
※最近在學數值分析,有誰能跟我簡單講講啥是範數啊?
※學化學專業必修課程學數值分析的意義在哪?