Problem: Given the magnitude of an angle, expressed in radians, compute the angle's sine.
Ideally, we should be able to figure the sine of x as the limit of
the infinite series x/1! - x^3/3! + x^5/5! -
x^7/7! + ... . It is convenient to represent a series in Scheme as
a procedure that takes any positive integer k as an argument and
returns the value of the kth term. If we had a procedure
limit-of-series
that took take any convergent series as its
argument and return the limit of that series, we could simply write
as a direct translation of the mathematical definition.(define sine (lambda (x) (let ((sine-series (lambda (n) (let ((index (+ n n -1))) ((if (odd? n) + -) (/ (expt x index) (factorial index))))))) (limit-of-series sine-series))))
Unfortunately, computing the limit of a series arithmetically requires an infinite amount of time. In many cases, however, one can halt either when a specified number of terms has been computed or when sucessive partial sums differ by a sufficiently small amount. In either case, the limit will be computed only approximately; but in general the arguments to the sine function will be inexact numbers anyway.
Here is a version of limit-of-series
that computes the value
of the series at the twelfth term:
In this case the computation has been arranged so that the terms that are likely to have the least absolute values are summed first, since this will postpone the loss of precision.(define limit-of-series (let ((number-of-terms 12)) (lambda (series) (let loop ((total 0) (k number-of-terms)) (if (zero? k) total (loop (+ total (series k)) (- k 1)))))))
Here is a version of limit-of-series
that continues the series
until successive partial sums differ by less than 0.000001:
Either approach gives a sine function that yields plausible values for arguments close to zero, rapidly deteriorating in accuracy once |x| exceeds 1. (The difficulty when |x| > 1 is that the computation involves adding inexact values of large, nearly equal magnitude and opposite sign, so that many or all of the significant digits are cancelled out.)(define limit-of-series (let ((epsilon 1e-6)) (lambda (series) (let loop ((total (series 1)) (previous-total 0) (k 2)) (if (< (abs (- total previous-total)) epsilon) total (loop (+ total (series k)) total (+ k 1)))))))
This document is available on the World Wide Web as
http://www.math.grin.edu/~stone/events/scheme-workshop/sines.html