Logistic Chaos1

Introduction

The Logistic driver calculates samples using the logistic equation.

Xk+1 = λXk(1 - Xk).

Where the control parameter λ ranges from 3 (exclusive) to 4 (exclusive). The process makes no use of the random generator and is thus unaffected by changes to the shared random seed.

The other driver implementing a chaotic process is Baker.

Profile

Figure 1 illustrates five examples of Logistic output with a sequence of 200 samples generated. All five examples were generated using a random seed of 1, an initial value of 0.5, and a rolloff parameter of 1.5. The only difference is the lambda parameter, which varies as indicated.


Figure 1: Sample output from Logistic.next() with representative λ settings. The left graph in each row displays samples in time-series while the right graph in the same row presents a histogram analyzed from the same samples.

The vertical x axes for the two graphs in each row represent the driver domain from zero to unity; the horizontal k axis of the time-series graph (left) plots ordinal sequence numbers; the horizontal f(x) axis of the histogram (right) plots the relative concentration of samples at each point in the driver domain.

The most characteristic of the sequences in Figure 1 is the first, which has samples clustering in various regions of the driver domain. As the λ parameter dials down to zero, the behavior comes to resemble uniform randomness (i.e., that produced by Lehmer).

Transitions

Figures 3 (a) through 3 (e) plot the range of sample-to-sample differences along the vertical Δx axis against the relative concentrations of these values along the horizontal fx) axis.


Figure 3 (a): Histogram of sample-to-sample differences from Logistic.next() with lambda λ=3.9299 after 10,000 consecutive samples.

Figure 3 (b): Histogram of sample-to-sample differences from Logistic.next() with lambda λ=3.8590 after 10,000 consecutive samples.

Figure 3 (c): Histogram of sample-to-sample differences from Logistic.next() with lambda λ=3.7477 after 10,000 consecutive samples.

Figure 3 (d): Histogram of sample-to-sample differences from Logistic.next() with lambda λ=3.5607 after 10,000 consecutive samples.

Figure 3 (e): Histogram of sample-to-sample differences from Logistic.next() with lambda λ=3.2804 after 10,000 consecutive samples.

Figure 4: Divergence of 4-nibble pattern counts from Logistic.next() with lambda λ=3.9299 after 10,000 samples per pattern.

Independence

Figure 4 presents a trend graph of histogram tallies for 4-nibble patterns generated using Logistic.next(). My analysis program decided to exclude low-frequency patterns by limiting the graph to all but the 200 largest tallies. The most frequent patterns were:

138151
137151
151412
71514

The tallies for these four patterns descended in equal increments from under 3% to under 2% presence.

The conclusion from Figure 4 is that the Logistic driver fails the 4-nibble independence test.

/**
    * Instances of the {@link Logistic} class generate a driver sequence using logistic chaos.
    * @author Charles Ames
    */
   public class Logistic extends DriverBase {
      private double lambda;
      /**
       * Constructor for {@link Logistic} instances with container.
       * @param container An entity which contains this driver.
       */
      public Logistic(WriteableEntity container) {
         super(container);
         setIDQuality(AttributeQuality.MODIFIABLE);
         setNameQuality(AttributeQuality.MODIFIABLE);
         lambda = Double.NaN;
      }
      /**
       * Constructor for {@link Logistic} instances without container.
       */
      public Logistic() {
         this(null);
      }
      /**
       * Getter for {@link #lambda}.
       * @return The assigned {@link #lambda} value.
       */
      public double getLambda() {
         if (Double.isNaN(lambda))
            throw new UninitializedException("Lambda not initialized");
         return lambda;
      }
      /**
       * Setter for {@link #lambda}.
       * @param lambda The intended {@link #lambda} value.
       * @return True if lambda has changed; false otherwise.
       * @throws IllegalArgumentException when the value falls outside the range from 3 (exclusive) to 4 (exclusive).
       */
      public boolean setLambda(double lambda) {
         if (this.lambda != lambda) {
            checkLambda(lambda);
            this.lambda = lambda;
            makeDirty();
            return true;
         }
         return false;
      }
      /**
       * Check if the indicated value is suitable for {@link #lambda}.
       * @param lambda The indicated value.
       */
      public void checkLambda(double lambda) {
         if (lambda <= 3. || lambda >= 4.)
            throw new IllegalArgumentException("Lambda " + MathMethods.df3.format(lambda) + " outside range from 3 (exclusive) to 4 (exclusive)");
      }
      @Override
      protected double generate() {
         double value = getValue();
         return lambda * value * (1. - value);
      }
   }
Listing 1: The Logistic implementation class.

Coding

The type hierarchy for Logistic is:

Listing 1 provides the source code for the Logistic class. The sequential process described at the top of this page is implemented by generate(), which is not public facing. Instead, generate() is called by DriverBase.next().

DriverBase.next() also takes care to store the new sample in the field DriverBase.value, where generate() can employ DriverBase.getValue() to pick this (now previous) sample up for the next sample iteration. DriverBase also offers setValue() and randomizeValue() methods to establish the initial sequence value.

Comments

  1. The present text is adapted from my Leonardo Music Journal article from 1992, "A Catalog of Sequence Generators". The heading is "Logistic Chaos", p. 62.

© Charles Ames Page created: 2022-08-29 Last updated: 2022-08-30