## The Logistic Map

The *Logistic Map* provides a well known demonstration of chaos theory and, specifically, the *butterfly effect*. It is defined as follows:

x_{n+1}= rx_{n}(1 - x_{n}) eq 1 where x in range (0, 1.0)

The Logistic Map is a discrete variant of the *Logistic Function*, often used to model populations. The mapping was proposed by Robert May in 1976.

Here, the equation is applied iteratively, with `x`

calculated from the result of the previous iteration. In this way, we can generate a sequence of values starting from an initial _{n+1}`x`

in the range (0, 1.0) and a chosen value for the constant _{0}`r`

. Now, the interesting thing about this is that tiny differences in values for `x`

, no matter how small, will eventually give rise the radically different sequences further down the line._{0}

## A Question of Encoding

I suspected, therefore, that the numeric encoding used to perform the calculation may have a dramatic effect on results due to the rounding of irrational numbers. If, for example, we used a hand calculator to crank through the numbers, our encoding would effectively be decimal with, say, 10 digits of accuracy. If, however, we were to automate the calculation programmatically, using the IEEE 64-bit `double`

type, we may expect very different results after a given number of iterations.

I constructed a trivial C# program to test this, below, to print out the first 200 results of the Logistic Map. Shown, our number type is that of `double`

, but note that we can easily substitute for `float`

, or in the case of C#, the 31 digit `decimal`

type. Here, we are using a value of 3.9999 for the constant `r`

, which puts us in the chaotic domain, and an initial start value of 0.3 for `x`

._{0}

```
using System;
namespace ConsoleApp1
{
class Program
{
const double ConstR = 3.9999d;
static void Main(string[] _)
{
// Initial x(0)
double x = 0.3d;
for (int n = 0; n <= 200; ++n)
{
Console.WriteLine("{0}: {1}", n, x);
// Logistic map: x(n+1) = rx[n] * (1 - x[n])
x = ConstR * x * (1 - x);
}
}
}
}
```

## Results

I ran this for the three numeric types, and obtained the following results. Note the iteration numbers `n`

, in the left column, are not consecutive.

n | float (32-bit) | double (64-bit) | decimal (31 digits) |
---|---|---|---|

0 | 0.3 | 0.3 | 0.3 |

1 | 0.83997905 | 0.8399789999999999 | 0.839979 |

5 | 0.08851373 | 0.08851501878806475 | 0.0885150187880626578796468323 |

10 | 0.057348635 | 0.057382099332860134 | 0.0573820993328049415122261620 |

15 | 0.98664975 | 0.987172308306826 | 0.9871723083059726654843130168 |

20 | 0.28575143 | 0.22185090539890087 | 0.2218509054996962578902708035 |

25 | 0.5171947 | 0.000299278332459266 | 0.0002992782229036668999226591 |

30 | 0.27864563 | 0.27639930220409714 | 0.2763992115682287626326181774 |

35 | 0.93201506 | 0.8220746530246507 | 0.8220771324777196905528489424 |

40 | 0.6932408 | 0.9594516054483151 | 0.9594107089523073396509840068 |

45 | 0.0047752936 | 0.041709414356855455 | 0.0430392813430136851467640733 |

50 | 0.29396194 | 0.08671682367895554 | 0.1547852169753980697798204914 |

55 | 0.24106489 | 0.01955595582987212 | 0.1341293467710280768209220547 |

Shown in red, are the approximate points where the sequences are deemed to diverge from a visual inspection to 2 decimal places. We can see the 64-bit `double`

and `decimal`

sequences track closely to around 50 iterations, whereas the `float`

results diverged much earlier at iteration 20. Given that the `float`

and `double`

types equate to 6-7 and 15-16 significant digits respectively, compared with the `decimal`

‘s 31, this is to be expected.