2014-03-18 109 views
2

進行計算程序,計算出 cos(x)= 1-(x^2/2!)+(x^4/4!) - (x^6/6!).. .....餘弦函數計算方案

什麼是最有效的方式來完成程序,你將如何做交替加減法,這就是我使用的模數,但不適用於0和1(前兩項)。爲x和num的初值是項數

(define cosine-taylor 
(lambda (x num) 
    (do ((i 0 (+ i 1))) 
     ((= i num)) 
     (if(= 0 (modulo i 2)) 
      (+ x (/ (pow-tr2 x (* i 2)) (factorial (* 2 i)))) 
      (- x (/ (pow-tr2 x (* i 2)) (factorial (* 2 i)))) 
    )) 
    x)) 
+0

泰勒是太緩慢收斂。看看切比雪夫多項式。或者牛頓 - 拉夫森計劃。 – Bathsheba

+1

您始終可以將操作符作爲參數傳遞。我正在看我的小小模擬器(好書),因爲我認爲他們在後面的章節中有這樣的功能。 –

+0

代碼不會編譯(因爲'let'的使用)或肯定會失敗。你有多難過? – GoZoner

回答

2

您的問題:

  1. 什麼是完成程序的最有效方式?假設您想要使用泰勒級數展開式,並簡單地總結條件n次,那麼您的迭代方法就很好。我已經在下面提煉了它;但你的算法很好。其他人指出可能會損失精確度問題;請參閱下面的方法。

  2. 你會怎麼做交替加減法?使用odd?的另一個「參數/局部變量」,一個布爾值,並使用not替代它。當odd?加減時不加odd?

(define (cosine-taylor x n) 
    (let computing ((result 1) (i 1) (odd? #t)) 
    (if (> i n) 
     result 
     (computing ((if odd? - +) result (/ (expt x (* 2 i)) (factorial (* 2 i)))) 
        (+ i 1) 
        (not odd?))))) 

> (cos 1) 
0.5403023058681398 
> (cosine-taylor 1.0 100) 
0.5403023058681397 

不壞?

以上是執行'do'循環的Scheme-ish方式。您應該可以輕鬆地看到do與三個當地人的對應關係iresultodd?

關於數字精度損失 - 如果你真的解決精度問題,然後將其轉換x到一個「精確」號,並使用確切的數字做所有計算。通過這樣做,您可以獲得自然的Scheme-ly算法,並具有「完美」的精度。

> (cosine-taylor (exact 1.0) 100) 
3982370694189213112257449588574354368421083585745317294214591570720658797345712348245607951726273112140707569917666955767676493702079041143086577901788489963764057368985531760218072253884896510810027045608931163026924711871107650567429563045077/7370634274437294425723020690955000582197532501749282834530304049012705139844891055329946579551258167328758991952519989067828437291987262664130155373390933935639839787577227263900906438728247155340669759254710591512748889975965372460537609742126858908788049134631584753833888148637105832358427110829870831048811117978541096960000000000000000000000000000000000000000000000000 
> (inexact (cosine-taylor (exact 1.0) 100)) 
0.5403023058681398 
+0

而不是一個單獨的布爾和1/-1選擇,爲什麼不有一個'sign'變量,你乘以-1在每次迭代... – Floris

+0

謝謝。用不同的方法更新。根據「奇怪?」選擇「+」或「-'。 – GoZoner

+1

+1表示準確/不精確。我猜在範圍-pi/2 ...pi/2的精度問題無論如何都是無關緊要的,儘管從概念上講,我認爲術語的計算應該是迭代的(並且「對我來說沒問題」)。如果你會展示如何限制精度(比如說1/10^20),那將會很好(某種方法可以將精確值近似爲1/10^n的精確倍數)。 –

1

幾點建議:

  1. 降低輸入模2π - 最多項式展開收斂速度很慢有大量
  2. 保持追蹤你的階乘因子,而不是每次都從頭開始計算它們(一旦你有4 !,你得到5!乘以5等等)
  3. 同樣,你所有的力量都是x^2的力量。只計算x^2一次,然後乘以這個數字(x2)到目前爲止的「x功率」,而不是將x乘以第n個功率

下面是一些實現這一點的Python代碼 - 它收斂除了極少數條款(你可以控制與while(abs(delta)>precision):聲明精度)

from math import * 

def myCos(x): 
    precision = 1e-5 # pick whatever you need 
    xr = (x+pi/2) % (2*pi) 
    if xr > pi: 
    sign = -1 
    else: 
    sign = 1 
    xr = (xr % pi) - pi/2 
    x2 = xr * xr 
    xp = 1 
    f = 1 
    c = 0 
    ans = 1 
    temp = 0 
    delta = 1 
    while(abs(delta) > precision): 
    c += 1 
    f *= c 
    c += 1 
    f *= c 
    xp *= x2 
    temp = xp/f 
    c += 1 
    f *= c 
    c += 1 
    f *= c 
    xp *= x2 
    delta = xp/f - temp 
    ans += delta 
    return sign * ans 

除此之外,我也幫不了你太多,因爲我不熟悉的方案...

+1

很好的答案;此外,在劃分之前,計算權力和因子很快就會與非常大的結果數字*發生衝突,並導致精度的損失(翻譯:錯誤的結果)。只有這樣,當我們用'(k + 1)*(k + 2)'乘以'x^2'的分割結果來臨時值時,迭代計算纔是可行的。 –

+0

如果要將精度損失降至最低,實際上最好從最小數開始 - 這需要計算一組值,然後將它們按「相反順序」進行求和。但這確實是一個調整。當你將數據記錄到-pi/2-pi/2域時,你贏得了99%的戰鬥勝利。 – Floris

+0

當然,將值減少到域中應該是單獨功能的任務,這就是爲什麼我沒有將它包含在我的答案中。 * Cosine *對其域名很幸運,但例如菲涅耳積分 - 不是那麼多。 :) –

2

我們應該計算在迭代時尚的條款,以防止將非常大量的精度損失:

(define (cosine-taylor-term x) 
    (let ((t 1.0) (k 0)) 
    (lambda (msg) 
     (case msg 
     ((peek) t) 
     ((pull) 
      (let ((p t)) 
      (set! k (+ k 2)) 
      (set! t (* (- t) (/ x (- k 1)) (/ x k))) 
      p)))))) 

那麼它應該很容易建立一個函數產生一個ñ階段,或將術語總和直到術語小於預設的精度值:

(define t (cosine-taylor-term (atan 1))) 
;Value: t 

(reduce + 0 (map (lambda(x)(t 'pull)) '(1 2 3 4 5))) 
;Value: .7071068056832942 

(cos (atan 1)) 
;Value: .7071067811865476 

(t 'peek) 
;Value: -2.4611369504941985e-8 
+0

感謝您的詳細解答,我需要詳細閱讀才能完全理解您的代碼 – user3241846

0

爲了您的一般享受,這裏是一個流實現。該流根據所提供的func返回泰勒項的無限序列。用當前索引調用func

(define (stream-taylor func) 
    (stream-map func (stream-from 0))) 
(define (stream-cosine x) 
    (stream-taylor (lambda (n) 
        (if (zero? n) 
         1 
         (let ((odd? (= 1 (modulo n 2)))) 
         ;; Use `exact` if desired... 
         ;; and see @WillNess above; save 'last'; use for next; avoid expt/factorial 
         ((if odd? - +) (/ (expt x (* 2 n)) (factorial (* 2 n))))))))) 
> (stream-fold + 0 (stream-take 10 (stream-cosine 1.0))) 
0.5403023058681397 
0

這是我能想出的最簡化的功能。

它利用了每一項乘以(-x^2)併除以(i + 1)*(i + 2)以提出文本項的事實。

它還利用了我們計算因子爲2,4,6等等的事實。所以它將位置計數器增加2,並將其與2 * N進行比較以停止迭代。

(define (cosine-taylor x num) 

    (let ((mult (* x x -1)) 
      (twice-num (* 2 num))) 

     (define (helper iter prev-term prev-out) 
     (if (= iter twice-num) 
      (+ prev-term prev-out) 
      (helper (+ iter 2) 
        (/ (* prev-term mult) (+ iter 1) (+ iter 2)) 
        (+ prev-term prev-out)))) 

     (helper 0 1 0))) 

測試repl.it

這裏有一些答案:

 
    (cosine-taylor 1.0 2) 
=> 0.5416666666666666 
    (cosine-taylor 1.0 4) 
=> 0.5403025793650793 
    (cosine-taylor 1.0 6) 
=> 0.5403023058795627 
    (cosine-taylor 1.0 8) 
=> 0.5403023058681398 
    (cosine-taylor 1.0 10) 
=> 0.5403023058681397 
    (cosine-taylor 1.0 20) 
=> 0.5403023058681397