The taylor series is a mechanism for approximating a function by summing polynomial terms.
First, some definitions:
f(x) | the function to approximate |
fn(x) | the nth derivative of f (note that f0(x) = f(x)) |
n! | n factorial, n! = n * (n - 1) * (n - 2) * ... * 2 |
The taylor series expansion of f(x) around a point (c) is then written:
n -> infinity +---- \ fn(c) * (x - c) n f(x) = + ----------------- / n! +---- n = 0
As an example, consider the case where:
f(x) | => | exp(x) |
c | => | 0 |
Since the derivative of exp(x) is exp(x) we know that fn(x) = exp(x) for all n.
Here are the first few terms of the series:
value of n | term | value of term |
---|---|---|
0 | exp(0) * (x - 0)0 / 0! | 1 |
1 | exp(0) * (x - 0)1 / 1! | x |
2 | exp(0) * (x - 0)2 / 2! | x2 / 2 |
3 | exp(0) * (x - 0)3 / 3! | x3 / 6 |
4 | exp(0) * (x - 0)4 / 4! | x4 / 24 |
We can write the expansion of exp(x) around c = 0 more concisely as follows:
n -> infinity +---- \ xn f(x) = + -- / n! +---- n = 0
Now lets look at how accurate the approximation is as we add more terms. In this case, lets use x = 3.
exp(3) | n | sum | approximation | difference |
---|---|---|---|---|
20.08553692318766774092 | 0 | 1 | 1 | 19.08553692318766774092 |
20.08553692318766774092 | 1 | 1 + x | 4 | 16.08553692318766774092 |
20.08553692318766774092 | 2 | 1 + x + x2/2 | 8.5 | 11.58553692318766774092 |
20.08553692318766774092 | 3 | 1 + x + x2/2 + x3/6 | 13 | 7.08553692318766774092 |
20.08553692318766774092 | 4 | 1 + x + x2/2 + x3/6 + x4/24 | 16.375 | 3.71053692318766774092 |
As you can see, the approximation converges pretty rapidly on the actual value of exp(x).
Ok, so how do we apply this to generating a fractal? Firstly, all the statements about 'x' above apply equally well to 'z' where z is the complex number (x + iy).
In our case, assume 'current' is the point 'z' for which we wish to create an approximation of exp(z). Our algorithm (in pseudo code) looks like this:
set actualValue = exp(current) set maxIterations = 20 set tolerance = .00001 set count = 0 set zSum = [0, 0] set denominator = 1 while count is less than maxIterations and the magnitude of the difference between actualValue and zSum is greater than tolerance do zSum = zSum + currentcount / denominator if count is greater than 0 denominator = denominator * (count + 1) endif count = count + 1 done
In this example, our expansion point (c) is zero, we can readily generalize our algorithm to handle other values of (c)
set actualValue = exp(current) set maxIterations = 20 set tolerance = .00001 set count = 0 set zSum = [0, 0] set denominator = 1 set c = [2, 3] set cValue = exp(c) while count is less than maxIterations and the magnitude of the difference between actualValue and zSum is greater than tolerance do zSum = zSum + cValue * (current - c)count / denominator if count is greater than 0 denominator = denominator * (count + 1) endif count = count + 1 done
Finally, here is a full-on fractorama formula file demonstrating this technique
fractal { mapping { (-2.33672316384180778215, 3.41009999999999990905, 1.18621468926553697010, 8.00000000000000000000) => (150, 195) } formula { // The value we wish to approximate zvalue = exp(current); $xavg = 0; $yavg = 0; $mavg = 0; $xmax = 0; $ymax = 0; $mmax = 0; $xtot = 0; $ytot = 0; $mtot = 0; $maxIterations = 15; $tolerance = 1e-6; zSum = 0; $denominator = 1; // The point around which we'll create our expansion. We use // exp(current) here but we could just as well have used: // // point = [2, 3] // point = sin(current) // etc. // // There is no special significance to the fact that we're using // exp(current) and we're attempting to approximate exp. point = exp(current); // Loop until we've been through the loop '$maxIterations' times // or we deem that zSum is close enough to zvalue while($count < $maxIterations && ssq(zvalue - zSum) > $tolerance) { zSum += exp(point) * ((current - point) ^ $count) / $denominator; if($count > 0) { $denominator *= ($count + 1); } value = cot(zSum); $x = mag(value ^ .1); $y = mag(value ^ .2); $m = mag(value ^ .4); $xavg = ($x + $xavg * $count) / ($count + 1); $yavg = ($y + $yavg * $count) / ($count + 1); $mavg = ($m + $mavg * $count) / ($count + 1); $xdev = sqrt(($x - $xavg) * ($x - $xavg)); $ydev = sqrt(($y - $yavg) * ($y - $yavg)); $mdev = sqrt(($m - $mavg) * ($m - $mavg)); $xmax = max($xmax, $xdev); $ymax = max($ymax, $ydev); $mmax = max($mmax, $mdev); $xtot += $xdev; $ytot += $ydev; $mtot += $mdev; } $r = 0; $g = 0; $b = 0; if($count > 0) { $r = ($xtot/$count) / $xmax; $g = ($ytot/$count) / $ymax; $b = ($mtot/$count) / $mmax; $r = get_sin_color($r, 255, 1); $g = get_sin_color($g, 255, 1); $b = get_sin_color($b, 255, 1); } set_color($r, $g, $b); } }
And here is the resulting image: