Logistic map

from Essential Mathematica for Students of Science
© James J. Kelly, 1998
Last revised: January, 2006

Introduction

The logistic equation provides a simple model of the evolution of a population for which successive generations do not overlap, which is a characteristic of many insect species.  For example, if eggs are laid in the fall and hatch in the spring, each generation is distinct and the population dynamics may be analyzed using discrete models.  Suppose that there is a maximum population that can be supported by a habitat, called its carrying capacity, and let (Subscript[x, n]) be the fraction of that carrying capacity that is present in generation n.  If (Subscript[x, n]) is relatively small, it is reasonable to assume that the size of the next generation will be directly proportional to the size of the present generation with a species-dependent growth factor designated as μ.  However, if the population approaches saturation of the habitat, competition for resources suppresses growth and may lead to a decline in the size of the next generation.  In the mid-1840's, the Belgian mathematician Pierre Verhulst proposed that this behavior can be represented using the one-parameter nonlinear recursive equation

(Subscript[x, n])  ==  μ (Subscript[x, n - 1]) (1  -  
            (Subscript[x, n - 1]))

known as the logistic equation.  The logistic equation is an example of a quadratic map in which successive iterates are obtained by recursive substitution into a quadratic expression.  The logistic equation provides a simple but insightful illustration of many characteristics of models of nonlinear phenomena.

The fate of the species is determined primarily by its growth parameter.  Clearly if μ < 1 the species fails to replace itself and eventually dies out.  Thus,

μ < 
        1  ⟹   (lim_(n -> ∞)) 
            (Subscript[x, n]) =  0

and we describe this situation by saying that the population reaches a fixed point, (Subscript[x, ∞]) = 0 for μ < 1.  For μ exactly 1, the fixed point is the initial population itself, such that μ = 1 ⟹ 
            (Subscript[x, ∞]) = (Subscript[x, 0]).  A more successful species will have μ > 1, but not too large, so that the species grows slowly without immediately saturating its habitat.  Under those happy circumstances, one might expect the population to reach a stable fixed point which depends primarily upon μ but which may be larger or smaller than (Subscript[x, 0]) depending upon the initial population.  However, for very large μ, we might anticipate an unstable situation in which the population oscillates wildly from one generation to the next.  Suppose that the initial population is small but the growth parameter is so large that the next generation nearly saturates its environment.  There will then be a population crash with a very small third generation.  Freed from competitive pressures, the third generation will sire a very large fourth generation, producing another crash at the fifth.  Under these conditions it becomes difficult to predict whether or not a stable situation is ever reached.  This notebook and cobweb.nb investigate the periodic and chaotic regimes of the discrete logistic equation in considerable detail.

Note: the output for this notebook was omitted to reduce the file size.  Evaluation of the entire notebook only takes a few minutes with current versions of Mathematica on modern PCs.

Initialization

ClearAll["Global`*"];
Off[General::spell,  General::spell1, General::ovfl];
$TextStyle = {FontFamily -> "Times", 
          FontSize -> 12};

Needs["Graphics`"];
Needs["Statistics`"]

To enhance computation speed, it is useful to define compiled functions which iterate either scalar or list arguments.

\(\(\(Verhulst::usage = "Verhulst[μ,x] = μ x (1-x) computes the next iterate of the logistic equation.";\)\(
\)
    \)\)
Verhulst = 
        Compile[{μ, x},  μ x (1 - x)];

\(\(\(VerhulstList::usage = "VerhulstList[μ,x] = μ x (1-x) produces a list of iterates of the logistic equation given lists μ and x of equal length.";\)\(
\)
    \)\)
VerhulstList = 
        Compile[{{μ, _Real, 1}, {x, _Real, 
              1}},  μ x (1 - x)];

Notice that VerhulstList uses a declaration syntax {(Subscript[x, i]), (Subscript[t, i]), (Subscript[n, i])} within Compile  in which variable (Subscript[x, i]) is declared to be list of rank (Subscript[n, i]) containing elements of type (Subscript[t, i]).  Thus, (Subscript[n, i]) = 1 indicates a vector (one-dimensional list), (Subscript[n, i]) = 2 indicates a matrix, etc., where each element of list (Subscript[x, i]) has type (Subscript[t, i]).

Fixed points

We begin our study of the logistic equation by searching for conditions under which a stable final population is achieved.  This can be done using the function FixedPoint.  FixedPoint[f,expr] starts with expr, then applies f repeatedly until the result no longer changes.  FixedPoint[f,expr,n] stops after at most n steps.  However, because FixedPoint always returns its last result whether or not it is a true fixed point, we use a slightly more sophisticated function which tests the output of FixedPoint for constancy and returns "none" if constancy is not actually achieved.

finalPopulation[μ_, x0_, nmax_]  :=  
      Module[{test,  result}, 
        test  =  FixedPoint[ μ # (1 - #) &,  x0,  nmax]; 
        result  =  μ # (1 - #) & @ test; 
        Which[result < $MachineEpsilon, 0. , 
          Abs[test - result] < 10*$MachineEpsilon,  result, 
          True,  "none"]]

TableForm[ 
      Table[{μ, finalPopulation[μ, 0.5, 1000]}, {μ, 0, 4, 0.1}], 
      TableHeadings -> {None, {"μ", "fixed point"}}]

Thus, we find that the population tends to die out when μ < 1, but reaches stable final values for 1  <  μ  <  3.  The maximum stable population is approximately 0.6552.  The behavior of this model becomes more interesting for μ > 3.  For example, μ=3.2 produces a sequence in which two values alternate after some initial transients die out.  As μ increases further, the 2-cycle splits into a 4-cycle, then an 8-cycle, with subsequent period doublings occuring ever more rapidly until a chaotic regime is reached.  Surprisingly, this chaotic regime is interrupted for some narrow ranges of μ in which islands of periodic behavior emerge.  Finally, for μ > 4 iteration can lead to infinite values of x; although the logistic equation for μ > 4 ceases to be represent a viable model for population dynamics, it still offers phenomena of mathematical interest.  The table above does not distinguish between periodic or chaotic behavior, for which we require more sophisticated tools.

Stable cycles

The evolution of the system for growth parameters in the range that promotes stable cycles can be illustrated by plotting successive generations in the form of a list plot.  For example, with μ ≈ 3.2 one finds a stable 2-cycle.

ListPlot[NestList[ 3.2 # (1 - #) &,  0.1,  100] , 
        PlotRange -> {Automatic, {0, 1}}];

For somewhat larger μ one finds a 4-cycle.

ListPlot[NestList[ 3.5 # (1 - #) &,  0.1,  100],  
        PlotRange -> {Automatic, {0, 1}}];

This phenomenon is known as period doubling and a value of μ at which the period suddenly doubles is called a bifurcation point.

A convenient method for determining the length and values of a stable cycle is to use the Union function to produce an ordered list of unique elements after allowing transients to decay.  The following function performs nstart initial iterations of the mapping function f to suppress transients.  This result initializes the NestList command, which then produces a list of nmax iterates.  Finally, the unique elements are selected by Union where we use the SameTest option to specify the numerical precision for nominally identical elements.

\(\(\(FindCycle::usage = "produces a cycle of maximum length nmax given a mapping function f[μ,x], growth parameter μ, and initial value x0.  The first nstart iterations are discarded to allow transients to decay.";\)\(
\)
    \)\)
FindCycle[f_, μ_, x0_, nstart_, nmax_] := 
        Union[NestList[f[μ, #] &, Nest[f[μ, #] &, x0, nstart], nmax], 
          SameTest -> (Abs[#2 - #1] < 
                  10 $MachineEpsilon &)];

CycleLength[f_, μ_, nstart_, nmax_] := 
      Length[FindCycle[f, μ, Random[], nstart, nmax]]

Thus, we isolate a 2-cycle for μ=3.2

FindCycle[Verhulst, 3.2, 0.5, 100, 1000]

or a 4-cycle for μ=3.5

FindCycle[Verhulst, 3.5, 0.5, 100, 1000]

but for μ=3.6

CycleLength[Verhulst, 3.6, 100, 1000]

we find that there is no stable cycle shorter than the 1000 iterations attempted.  It is important to recognize that stable cycles are independent of starting values.

FindCycle[Verhulst, 3.5, Random[], 100, 1000] - 
      FindCycle[Verhulst, 3.5, Random[], 100, 1000]

Of course, this method may break down when the period approaches nmax.  For large periods or for μ very close to a bifurcation point where convergence becomes very slow, it may be necessary to increase nstart and/or the numerical precision of the calculations.

More insight into the emergence of stable cycles can be obtained using a cobweb diagram of the iteration process.  Because the procedure for producing cobweb diagrams is itself an interesting Mathematica application, it is presented in a separate notebook cobweb.nb to which we invite you to refer now or after finishing this notebook.

Order within chaos

A more complete picture of the behavior of the logistic equation is provided by the function CycleList that is defined below.  For efficient operation we use a mapping function f that is a compiled function whose inputs are lists of equal length.  Thus, CycleList accepts a list μ specifying the growth parameters and constructs a corresponding list of initial conditions, which are all taken to be 0.5 because initial transients are suppressed during the first nstart iterations anyway.  The Nest and NestList functions are applied to all elements of these lists in parallel, which is much more efficient than sequential processing of loop structures like Do.  A list of cycles is then obtained by mapping the Union function onto the two-dimensional list of iterates.  The Thread and MapThread functions are used to associate the proper element of the μ list with each member of each cycle so that the final result is suitable for display using ListPlot.  Note that it is not necessary to eliminate duplicated points from the data passed to ListPlot.  To understand how these steps work, I suggest that you take this function apart, examine each step, and reassemble it from the inside out.

CycleList[f_, μ_List, nstart_, nmax_] := 
      Module[{temp, x0}, 
x0 = Table[0.5, {Length[μ]}]; 
        temp = Nest[f[μ, #] &, x0, nstart]; 
        temp = NestList[f[μ, #] &, temp, nmax]; 
        Flatten[MapThread[
            Thread[{#1, #2}] &, {μ, Union /@ Transpose[temp]}], 
          1]]

The figure below illustrates the behavior of the logistic equation for a wide range of growth parameters.

\(\(\(data = 
        CycleList[VerhulstList, Range[2.5, 4.0, 0.001], 5000, 1000];\)\(
\)
    \)\)
ListPlot[data, Frame -> True,  
        FrameLabel -> {"μ",  "x"}, 
        PlotLabel -> "Bifurcation Diagram for Logistic Map", 
        PlotStyle -> AbsolutePointSize[0.1]];

This figure reveals some of the most important characteristics of nonlinear phenomena in which order yields to chaos but retains domains of dominance.  In this case, as μ increases there is a transition from stable deterministic behavior to a simple but less easily predictable two-cycle for  3 ≲ μ ≲ 3.4.   A second period doubling occurs for μ~3.4 and a third for μ~3.5.  Eventually the bifurcation points are so closely spaced that the plot fills in.  Surprisingly, however, there are regions of μ for which low-period cycles emerge again as order stakes out some territory within chaos!

Self-similarity

You might notice that one of the branches extends into the chaotic region as a long sweeping arc toward the lower-right corner.  As that branch crosses a relatively wide white region, there appears another bifurcation structure.  Using the cursor to identify the corners of a box centered upon that region, we can zoom in and observe that a small region appears qualitatively similar to the entire map.  This phenomenon of self-similarity at various scales is a common feature of fractal phenomena.

\(\(\(data = 
        CycleList[VerhulstList, Range[3.825, 3.855, 0.00002], 10000, 
          1000];\)\(
\)
    \)\)
ListPlot[data, PlotRange -> {All, {0.44, 0.52}}, 
        Frame -> True,  FrameLabel -> {"μ",  "x"}, 
        PlotLabel -> "Self-Similarity within Logistic Map", 
        PlotStyle -> AbsolutePointSize[0.5]];

Entropy

Entropy is defined by

S  =  - (∑_k) (Subscript[p, k]) 
            Log[(Subscript[p, k])]

where (Subscript[p, k]) is the probability with which a system is found in state k.  Thus, for our system we divide the allowed range 0 ≤ x ≤ 1 into bins and compute the frequencies with which the system visits each bin.  Recognizing that p Log[p] -> 0 in the limit p -> 0, the function

entropy[prob_?VectorQ] := 
      Module[{p}, 
p = 
          Select[prob, Positive]; 
-p . 
            Log[p]]

uses Select to eliminate empty bins and thereby to avoid numerical errors in evaluation of the entropy.  The following function evaluates entropy for the logistic map given μ, the total number of iterations, the number of bins.  We also drop initial iterations to suppress transients.

VerhulstEntropy[μ_, nmax_, ndrop_, nbins_] := 
      Module[{prob, data}, 
data = 
          Drop[NestList[μ  # (1 - #) &, 0.5, nmax], 
            ndrop]; 
prob = (1. /Length)[data] 
            BinCounts[
              data, {0. , 1. , (1. /nbins)}]; 
entropy[
          prob]]

The dependence of logistic entropy upon μ is examined below with fairly high resolution (small spacing in μ).

sdata = 
        Table[{μ, VerhulstEntropy[μ, (10^5), (10^3), (10^3)]}, {μ, 
            2.9, 4, 0.001}];

EntropyPlot = 
        ListPlot[sdata, PlotJoined -> True, Frame -> True, 
          FrameLabel -> {"μ", "entropy"}];

The upward steps for 3 < μ ≲ 3.58 represent bifurcations that increase the length of periodic cycles and thereby the entropy of the system.  The relatively broad dips, such as the one centered near 3.83, represent windows of smaller entropy that are easily visible in preceding population diagrams.  Are the sharp spikes also meaningful or are they numerical artifacts of the binning used to compute entropy?  Let's look closer at the first narrow dip.  The feature that appears as a downward spike in the figure above is shown below to have considerable substructure both in entropy and in the population data.  The broader dips within the entropy substructure are visible as windows in the population diagram.  Even the bifurcations near μ = 3.583 are visible as steps in the entropy diagram.

sdata1 = 
        Table[{μ, VerhulstEntropy[μ, (10^5), (10^3), (10^3)]}, {μ, 
            3.575, 3.59, 0.00002}];

ListPlot[sdata1, PlotJoined -> True, PlotRange -> All, 
        Frame -> True, 
        FrameLabel -> {"μ", "entropy"}];

data = 
        CycleList[VerhulstList, Range[3.575, 3.59, 0.00002], 5000, 
          1000];
ListPlot[data, Frame -> True,  
        FrameLabel -> {"μ",  "x"}];

Looking even closer, the sharp entropy dip near μ = 3.5863 is visible in the population diagram if we use sufficient resolution.  Therefore, we conclude that most, if not all, of the sharp structures in the entropy diagram are real features of the logistic map; there is no evidence for serious numerical artifacts.

data = 
        CycleList[VerhulstList, Range[3.585, 3.589, 0.000002], 5000, 
          1000];
ListPlot[data, Frame -> True,  
        FrameLabel -> {"μ",  "x"}];

One defect of this definition of entropy is its dependence upon bin size.  Unlike systems with a denumerable set of distinct states, there is no natural bin size for the continuous variable x.  Suppose that we scale the bin size \(TraditionalForm\`Δ x -> Δ x/m\) such that the total number of bins increases n -> n m by the factor m.  Then

\(TraditionalForm\`\(\(\ \)\(∑\+\(k = 1\)\%n\( p\_k\) 
          Log[p\_k]\ ⟶\ \(∑\+\(k = 1\)\%\(n\ m\)\(p\_k\/m\) Log[p\_k\/m]\)\  ≈ \ 
      1\/m\ \(\(∑\+\(k = 1\)\%\(n\ m\)\)\((\(p\_k\) Log[p\_k] - \(p\_k\) 
                Log[m])\)\(\ \)\)\)\)\)

shows that the entropy is increased according to

\(TraditionalForm\`Δ x -> \(Δ x/
            m\ \ ⟹\ \ \ S\  -> 
        S\  + \ Log[m]\)\)

Lyapunov exponent

Perhaps the most important characteristic of a chaotic system is its sensitivity to initial conditions — if we start two identical systems from slightly different initial conditions, x and x + ε, their states after n iterations, (Subscript[f, n])[x] and (Subscript[f, n])[x + ε], will eventually diverge and their subsequent histories will appear unrelated.  The rate of divergence between these orbits is measured by the Lyapunov exponent λ defined by the asymptotic behavior

\(TraditionalForm\`lim\+\(ε -> 0\)\ |\(f\_n[x + ε] - f\_n[x]\)\/ε| ≃ \ e\^\(n\ λ\)\)

for large n.  The orbits converge when λ < 0 and are insensitive to initial conditions, whereas when λ > 0 the orbits diverge exponentially and are extremely sensitive to initial conditions.  The special case of λ = 0 can be described as metastability or neutral equilibrium, but is very difficult to realize in practice.

To estimate λ, first consider finite n for which

\(TraditionalForm\`λ\_n\  = \ 
      1\/n\ |df\_n[x]\/dx|\)

The asymptotic limit for large n can then be evaluated using

\(TraditionalForm\`λ\  = \ 
      lim\+\(n -> ∞\)1\/n\ \(∑\+\(k = 0\)\%\(n - 1\)Log |df\_k[x]\/dx|\)\)

In practice we evaluate the right-hand side for a large fixed value of n and may compare several values of n to test for convergence, but usually we do not attempt to compute a rigorous limit.  The result should be nearly independent of the initial conditions, provided that we avoid vanishing derivatives, and its accuracy should improve as n increases.  The function below employs this algorithm for the logistic map.

VerhulstLyapunov[μ_, x0_, nmax_] := 
      Module[{data}, 
data = (μ (1 - 
                      2  #) &) /@  
            NestList[μ  # (1 - #) &, x0, 
              nmax]; 
(1. /Length)[data] 
          Plus @@ Log[Abs[data]]]

LyapunovPlot = 
        ListPlot[
          Table[{μ, VerhulstLyapunov[μ, 0.1, 10000]}, {μ, 2.5, 4, 
              0.001}], PlotJoined -> True, Frame -> True, 
          FrameLabel -> {"μ", "λ"}, 
          PlotLabel -> "Lyapunov exponent"];

The figure below compares the entropy and the Lyapunov exponent for the logistic map.  The next two figures for λ examine a couple of interesting parameter ranges with higher resolution.  The pitchfork bifurcations appear as upward steps in entropy or points where λ is tangent to the line λ = 0 with λ < 0 nearby.  The strong dips in λ can be described as superstable orbits about midway between bifurcations.  Beyond μ ≳ 3.57 we observe λ is mostly positive, and increasing, except for narrow windows of stability.  The relatively broad window around μ = 3.83 is observed in both S and λ with similar substructure.  Self-similarity is observed as we view transition regions with higher resolution.

DisplayTogether[
ListPlot[
          Table[{μ, VerhulstEntropy[μ, (10^4), (10^3), (10^2)]}, {μ, 
              2.5, 4, 0.001}], PlotJoined -> True, 
          PlotStyle -> RGBColor[1, 0, 0]], 
ListPlot[
          Table[{μ, VerhulstLyapunov[μ, 0.1, 10000]}, {μ, 2.5, 4, 
              0.001}], PlotJoined -> True, 
          PlotStyle -> 
            RGBColor[0, 0, 1]], 
Frame -> True, 
        FrameLabel -> {"μ", "entropy, Lyapunov"}, 
        Epilog -> {{RGBColor[1, 0, 0], 
              Text["entropy", {2.6, 3}, {-1, 0}]}, {RGBColor[0, 0, 
                1], Text["Lyapunov exponent", {2.6, 2.5}, {-1, 
                  0}]}}];

ListPlot[
        Table[{μ, VerhulstLyapunov[μ, 0.1, 10000]}, {μ, 3.57, 
            3.6, 0.0001}], PlotJoined -> True, Frame -> True, 
        FrameLabel -> {"μ", "λ"}, 
        PlotLabel -> "Lyapunov exponent"];

ListPlot[
        Table[{μ, VerhulstLyapunov[μ, 0.1, 10000]}, {μ, 3.82, 
            3.86, 0.0001}], PlotJoined -> True, Frame -> True, 
        FrameLabel -> {"μ", "λ"}, 
        PlotLabel -> "Lyapunov exponent"];

Logistic Cantor sets

So far we have confined ourselves to μ ≤ 4 for which all of the action can be confined to the unit square, such that 0 ≤ (Subscript[x, n]) ≤ 
        1 ⟹ 0 ≤ (Subscript[x, n + 1]) ≤ 
        1, as befitting a population model.  Interesting new dynamics emerges for larger μ.  One interesting question concerns the fate of points 0 < (Subscript[x, 0]) < 1 subject to the iteration (Subscript[x, n + 1]) = μ (Subscript[x, n])(1 - (Subscript[x, n])) — which points are confined to a limited range of x and which escape to infinity?  Clearly, any (Subscript[x, n]) which strays outside the interval (0, 1) will soon be driven to ±∞ by further iteration.  The prisoner set (Subscript[P, μ]) = {(Subscript[x, 0]) : 
            0 ≤ (Subscript[x, ∞]) ≤ 1} is defined as the set of points which remain in the unit interval as the number of iterations approaches infinity.  An obvious but inefficient method to construct an approximation to a prisoner set is to choose a large number of points within the unit interval and to iterate the logistic mapping for each point until either it escapes or until an iteration limit is reached.  However, we shall soon observe that prisoner sets for μ > 4 contain an infinite number of disconnected points, making it rather unlikely that we would select points that would remain after many iterations.

Inverse iteration offers a more efficient algorithm for approximating prisoner sets.  First, we construct a set (Subscript[Q, μ]^((0))) containing seeds 0 < (Subscript[x, 0]) < 1 within the unit interval.  Then, for each member of (Subscript[Q, μ]^((0))) we determine two solutions to the inverse mapping

Solve[(Subscript[x, n + 1]) == μ (Subscript[x, n]) (1 - (Subscript[x, n])), (Subscript[x, n])] // 
      Simplify[#, μ > 0] &

The pre-image of (Subscript[Q, μ]^((-1))) is then constructed by retaining the points (Subscript[x, -1]) that remain within the unit interval.  Repeated application of inverse mapping and pruning produces a sequence of pre-images (Subscript[Q, μ]^((-k))).  The inverted tree structure shown below illustrates a sequence of pre-images that might be obtained from a single seed, assuming that no branches need pruning.

The principle underlying this algorithm is that one can be certain that every member (Subscript[Q, μ]^((-k))) will remain within the unit interval after k forward iterations.  Therefore, as k -> ∞, the pre-image of any seed set approaches the prisoner set, such that (Subscript[Q, μ]^((-k))) -> (Subscript[P, μ]).

The following functions use inverse iteration to construct the preimage of a seed set

InverseMap[μ_] = {(1/2) (1 - (Sqrt[1 - ((4 #)/μ)])), 
            (1/2) (1 + (Sqrt[1 - ((4 #)/μ)]))} &;

Preimage[μ_, x_, n_] := 
        Cases[Flatten[Nest[InverseMap[μ], x, n]], 
          y_Real /; (0 < y < 1)];

while

DisplayPreimage[set_, {xmin_, xmax_}, opts___Rule] := 
        ListPlot[{#, 0} & /@ set, opts, Axes -> False, 
          PlotRange -> {{xmin, xmax}, Automatic}, 
          AspectRatio -> 0.2, PlotStyle -> {PointSize[0.001]}, 
          Prolog -> {Line[{{xmin, -0.1}, {xmin, 0.1}}], 
              Text[ToString[xmin], {xmin, -0.3}, {-1, 0}], 
              Line[{{xmax, -0.1}, {xmax, 0.1}}], 
              Text[ToString[xmax], {xmax, -0.3}, {1, 0}]}];

displays a portion of the set.

Below we illustrate this procedure using a suitable choice of μ > 4.  To avoid any possible bias due to the seed set, we select random numbers in the unit interval.  The figure displays our approximation to the prisoner set using several steps of magnification by factors of 10.

set1 = Preimage[5. , Table[Random[], {100}], 10];

plot1 = 
        DisplayPreimage[set1, {0, 1}, 
          DisplayFunction -> Identity];
plot2 = 
        DisplayPreimage[set1, {0, 0.1}, 
          DisplayFunction -> Identity];
plot3 = 
        DisplayPreimage[set1, {0, 0.01}, 
          DisplayFunction -> Identity];
plot4 = 
        DisplayPreimage[set1, {0, 0.001}, 
          DisplayFunction -> Identity];
Show[GraphicsArray[{{plot1}, {plot2}, {plot3}, {plot4}}], 
        DisplayFunction -> $DisplayFunction];

At unit magnification (Subscript[P, μ]) appears to consist of a sprinkling of small line segments, but each successive magnification reveals that subsets which looked like line segments are actually populated rather sparsely, resembling the entire set.  Thus, the prisoner set resembles the famous Cantor set constructed by infinite iteration of removal of the middle third of any line segment.  The Cantor set is strictly self-similar at any magnification scale, while logistic prisoner sets (Subscript[P, μ]) for μ > 4 display structural self-similarity.  Our choice of seeds and iteration depth do not significantly affect the appearance of these sets — their similarity to the Cantor set is an intrinsic property of the logistic map for μ > 4.