Scheme 余弦函数计算格式
我正在制作一个计算 cos(x)=1-(x^2/2!)+(x^4/4!)-(x^6/6!) 完成这个程序最有效的方法是什么?你会怎么做交替加减法,这就是我用模来做的,但不适用于0和1(前两项)。x是x的初始值,num是项数Scheme 余弦函数计算格式,scheme,Scheme,我正在制作一个计算 cos(x)=1-(x^2/2!)+(x^4/4!)-(x^6/6!) 完成这个程序最有效的方法是什么?你会怎么做交替加减法,这就是我用模来做的,但不适用于0和1(前两项)。x是x的初始值,num是项数 (define cosine-taylor (lambda (x num) (do ((i 0 (+ i 1))) ((= i num)) (if(= 0 (modulo i 2)) (+ x (/ (pow-tr2 x (
(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))
一些建议:
while(abs(delta)>precision):
语句控制精度)
除此之外,我帮不了你多少忙,因为我不熟悉scheme…你的问题:
n
相加几次,那么您的迭代方法就可以了。我在下面对它进行了改进;但是你的算法很好。其他人则指出了可能出现的精度损失问题;我的方法见下文
odd?
布尔值的“参数/局部变量”,并使用not
进行替换。当奇数?
非奇数?
相加时进行减法
do
与i
、result
和odd?
的三个局部变量的对应关系
关于数字精度的损失-如果您真的想解决精度问题,请将x
转换为“精确”数字,并使用精确数字进行所有计算。通过这样做,您可以得到一个自然的、具有“完美”精度的Scheme-ly算法
> (cosine-taylor (exact 1.0) 100)
3982370694189213112257449588574354368421083585745317294214591570720658797345712348245607951726273112140707569917666955767676493702079041143086577901788489963764057368985531760218072253884896510810027045608931163026924711871107650567429563045077012372870953594171353825520131544591426035218450395194640007965562952702049286379961461862576998942257714483441812954797016455243/7370634274437294425723020690955000582197532501749282834530304049012705139844891055329946579551258167328758991952519989067828437291987262664130155373390933935639839787577227263900906438728247155340669759254710591512748889975965372460537609742126858908788049134631584753833888148637105832358427110829870831048811117978541096960000000000000000000000000000000000000000000000000
> (inexact (cosine-taylor (exact 1.0) 100))
0.5403023058681398
我们应以迭代方式计算这些项,以防止因分割非常大的数字而导致精度损失:
(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))))))
然后,应该很容易构建一个函数来生成第n个项,或者将这些项相加,直到一个项小于预设的精度值:
(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
为了让您总体享受,下面是一个流实现。流根据提供的
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
这是我能想到的最精简的函数 它利用了这样一个事实,即每个项乘以(-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)))
在进行测试
以下是一些答案:
(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
(余弦泰勒1.02)
=> 0.5416666666666666
(余弦泰勒1.04)
=> 0.5403025793650793
(余弦泰勒1.06)
=> 0.5403023058795627
(余弦泰勒1.08)
=> 0.5403023058681398
(余弦泰勒1.0 10)
=> 0.5403023058681397
(余弦泰勒1.0 20)
=> 0.5403023058681397
泰勒太慢了,无法收敛。看看切比雪夫多项式。或者牛顿-拉斐逊方案,你可以把算符作为参数传递。我正在看我的小Schemer(伟大的书)的副本,因为我认为它们在后面的章节中有这样一个函数。代码不可能编译(因为
let
用法),或者肯定会失败。你试了多努力?是的,没错,那句话是我试过的一次错误的;此外,在除法之前,计算幂和阶乘将很快失去控制,产生非常大的结果数,并随之失去精度(翻译:错误的结果)。只有这样,迭代计算才是可行的,当我们将中间值乘以x^2
除以(k+1)*(k+2)
的结果时。实际上,如果你想最小化精度损失,最好从最小的数开始——这需要计算一组值,然后“按相反顺序”求和。但这确实是一个调整。当你将数据降到-pi/2-pi/2域时,你就赢得了99%的战斗胜利。当然,将一个值降到域中应该是一个单独函数的任务,这就是为什么我没有在我的答案中包括它。余弦的域很幸运,但例如菲涅耳积分-没有那么多。:)@我不同意,如果你想计算余弦,你不需要调用两个函数。现在,你的余弦函数可能会调用另一个函数来缩小域,但我认为它的值有限。我的意思是,范围缩小与余弦计算是分开的,就像泰勒级数一样。它本身不是一个单独的布尔值和一个1/-1
select,为什么不在每次迭代时用一个符号变量乘以-1…谢谢。用不同的方法更新。根据奇数?
+1选择+
或-
,以显示精确/不精确。我猜在-pi/2…pi/2的范围内,精度问题无论如何都是没有意义的,尽管从概念上来说,我认为术语的计算应该是迭代的(而且我认为do
很好)。如果你能说明如何限制精度(比如说,1/10^20),那就太好了(用某种方法将精确值近似为1/10^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)))
(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