Does Number Encoding Affect Logistic Map Results?

The Logistic Map

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

xn+1 = rxn(1 - xn)    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 xn+1 calculated from the result of the previous iteration. In this way, we can generate a sequence of values starting from an initial x0 in the range (0, 1.0) and a chosen value for the constant r. Now, the interesting thing about this is that tiny differences in values for x0, no matter how small, will eventually give rise the radically different sequences further down the line.

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 x0.

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.

nfloat (32-bit)double (64-bit)decimal (31 digits)
00.30.30.3
10.839979050.83997899999999990.839979
50.088513730.088515018788064750.0885150187880626578796468323
100.0573486350.0573820993328601340.0573820993328049415122261620
150.986649750.9871723083068260.9871723083059726654843130168
200.285751430.221850905398900870.2218509054996962578902708035
250.51719470.0002992783324592660.0002992782229036668999226591
300.278645630.276399302204097140.2763992115682287626326181774
350.932015060.82207465302465070.8220771324777196905528489424
400.69324080.95945160544831510.9594107089523073396509840068
450.00477529360.0417094143568554550.0430392813430136851467640733
500.293961940.086716823678955540.1547852169753980697798204914
550.241064890.019555955829872120.1341293467710280768209220547
Logistic Map results for different numeric encoding types. Constant r = 3.9999.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *