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: