# Rate of population growth # Nice values 2, 2.75, 3, 3.25, 3.5, 4 (chaos ensues!) set r 2.75 # Starting population set x .4 set init_x $x # Number of iterations set iterations 50 for {set i 1} {$i <= $iterations} {incr i} { set x [expr $r * $x * [expr 1 - $x]] lappend data $i lappend data $x } package require Tk package require emu_graph wm title . "Simple Chaos Theory (r=$r, x=$init_x, $iterations\ iterations)" canvas .c -width 500 -height 300 pack .c emu_graph::emu_graph graph -canvas .c -width 400 -height 225 graph data d2 -colour red -points 0 -lines 1 -coords $dataSetting r to 2.75 results in the following graph, where the population oscillates for a while before settling down to a steady state.




Error in startup script: floating-point value too large to represent while executing "expr $r * $x * [expr 1 - $x]"AM This is because then the iterate will grow in size indefinitely. Another chaotic function is presented at The Q functionDKF: The classic way of visualising the Simplified Malthusian fractal is to plot r (on a scatter graph) against the points in the "cycle", running the thing for some number of iterations (perhaps 100?) to let it settle. In general, this produces x points for each value of r, where x is the cycle length. Look out for cycles of length 3...WJR The above as a Tcl script:
# Rate of population growth # Nice values 2, 2.75, 3, 3.25, 3.5, 4 (chaos ensues!) set r [list 1 1.25 1.5 1.75 2 2.25 2.5 2.75 3 3.25 3.5 3.75 4] # Starting population set x .4 set init_x $x # Number of iterations set iterations 100 foreach r_value $r { for {set i 1} {$i <= $iterations} {incr i} { set x [expr $r_value * $x * [expr 1 - $x]] lappend data $r_value lappend data $x } } package require Tk package require emu_graph wm title . "Simple Chaos Theory" canvas .c -width 500 -height 300 pack .c emu_graph::emu_graph graph -canvas .c -width 400 -height 225 graph data d2 -colour red -points 1 -lines 0 -coords $dataThe results are shown below. Again, as r increases we see the data points split as they alternate between different levels, and eventually become chaotic.

Here's a slightly improved version of the script above:
# Starting population set x .4 # Number of iterations set iterations 100 for {set r_value 1} {$r_value <= 4} {set r_value [expr $r_value + .01]} { for {set i 1} {$i <= $iterations} {incr i} { set x [expr $r_value * $x * [expr 1 - $x]] lappend data $r_value lappend data $x } } package require Tk package require emu_graph wm title . "Simple Chaos Theory" canvas .c -width 500 -height 300 pack .c emu_graph::emu_graph graph -canvas .c -width 400 -height 225 graph data d2 -colour red -points 1 -lines 0 -coords $dataThe bifurcations in the resulting graph are much clearer and strongly resemble the graph on page 71 of the Gleick book:

foreach i [.c find withtag point] { foreach {a b c d} [.c coord $i] break .c create line $a $b [expr {$a+1}] [expr {$b+1}] -fill blue -tag ppoint .c delete $i }WJR: This results in:

# Starting population set x .4 # Number of iterations set iterations 400 package require Tk wm title . "Simple Chaos Theory" canvas .c -width 520 -height 500 pack .c for {set sx 10} {$sx < 510} {incr sx} { #set r_value [expr {($sx-10)/ 500.0 * 3.0 + 1.0} ] set r_value [expr { pow(($sx-10)/ 500.0, 0.25) * 3.0 + 1.0} ] for {set i 1} {$i <= $iterations} {incr i} { set x [expr {$r_value * $x * (1 - $x)}] # skip first 200 iterations ==> better picture if {$i > 200 } { set sy [expr {500 - 10 - $x*400}] .c create line $sx $sy [expr $sx+1] [expr $sy+1] } } update }RHS To see a pattern in the chaos, change the chart you're drawing from x/t axis to x(n)/x(n+1). Ie, the x position is the current x, and the y position is the previous x. Below is code that does this:
package require Tk proc main {} { buildGUI } proc buildGUI {} { set ::formula {3.7 * $x * (1 - $x)} canvas .canvas frame .bottombar button .bottombar.calc -text "Calculate" \ -command {doFormula .canvas ::formula} label .bottombar.x -text "X(t+1) =" entry .bottombar.formula -textvariable ::formula pack .bottombar.x -side left -expand false pack .bottombar.formula -side left -fill x -expand true pack .bottombar.calc -side left -expand false pack .canvas .bottombar -side top -fill both -expand true } proc doFormula {window var} { set formula [set $var] set width [$window cget -width] set height [$window cget -height] set x [expr {rand()}] .bottombar.calc configure -state disabled drawPoints $window $width $height $formula $x 0 10 1000 } proc drawPoints {window width height formula x currCount interval maxCount} { for {set i 0} {$i < $interval} {incr i} { if { [incr currCount 1] >= $maxCount } { .bottombar.calc configure -state normal return } # puts "X=$x" set x2 [expr $formula] $window create oval [getLine $width $height $x $x2] -width 1 -fill black set x $x2 } after idle [list drawPoints $window $width $height \ $formula $x $currCount $interval $maxCount] } proc getLine {width height x y} { set x2 [set x1 [expr {$x * $width}]] set y2 [set y1 [expr {$y * $height}]] list $x1 $y1 $x2 $y2 } main
DKF: When I asked about other methods of plotting, I was actually thinking in terms of using a photo image and putting pixels directly on it...slebetman: I just so happen to have an LCD emulator code lying around that uses a photo image as its canvas (faster than using a real canvas). Here's rai's code modified to draw directly onto a photo image:
# Implementation using image set width 640 set height 480 set pixelsize 1 set oncolor black set offcolor white image create photo CANVAS \ -width [expr {$width*$pixelsize}] \ -height [expr {$height*$pixelsize}] CANVAS put $offcolor \ -to 0 0 [expr {$width*$pixelsize}] [expr {$height*$pixelsize}] pack [label .l -image CANVAS] -fill both -expand 1 wm resizable . 0 0 proc setpixel {x y val} { global pixelsize set x [expr {$x*$pixelsize}] set y [expr {$y*$pixelsize}] set xx [expr {$x+$pixelsize}] set yy [expr {$y+$pixelsize}] CANVAS put $val -to $x $y $xx $yy } proc chaos {x iterations color} { global width height for {set sx 0} {$sx < $width} {incr sx} { set r_value [expr { pow(($sx*1.0)/$width, 0.25) * 3.0 + 1.0} ] for {set i 1} {$i <= $iterations} {incr i} { set x [expr {$r_value * $x * (1 - $x)}] # skip first half iterations ==> better picture if {$i > ($iterations/2)} { set sy [expr {$height - $x*$height}] setpixel [expr {int($sx)}] [expr {int($sy)}] $color } } update } } chaos .4 400 $oncolor
PO Here is another version of the above algorithm implemented with the Tcl3D extension using OpenGL point sprites and display lists.
package require tcl3d 0.3 set width 640 set height 480 set pixelsize 1 set oncolor [tcl3dName2rgbf "blue"] proc PrintInfo { msg } { if { [winfo exists .fr.info] } { .fr.info configure -text $msg } } proc SetPixel { x y val } { global height glColor3fv $val glVertex3f $x [expr {$height - $y}] 0.0 } proc Chaos { x iterations color } { global curWidth width height displayListBase for {set sx 0} {$sx < $width} {incr sx} { set r_value [expr { pow(($sx*1.0)/$width, 0.25) * 3.0 + 1.0} ] glNewList [expr {$sx + $displayListBase}] GL_COMPILE glBegin GL_POINTS for {set i 1} {$i <= $iterations} {incr i} { set x [expr {$r_value * $x * (1 - $x)}] # skip first half iterations ==> better picture if {$i > ($iterations/2)} { set sy [expr {$height - $x*$height}] SetPixel [expr {int($sx)}] [expr {int($sy)}] $color } } glEnd glEndList set curWidth $sx .fr.toglwin postredisplay update } } proc tclCreateFunc { toglwin } { global width displayListBase glClearColor 1.0 1.0 1.0 0.0 glPointSize $::pixelsize set displayListBase [glGenLists $width] } proc tclReshapeFunc { toglwin w h } { glViewport 0 0 $w $h glMatrixMode GL_PROJECTION glLoadIdentity glOrtho 0.0 $w 0.0 $h -1.0 1.0 glMatrixMode GL_MODELVIEW glLoadIdentity } proc tclDisplayFunc { toglwin } { global curWidth width displayListBase glClear GL_COLOR_BUFFER_BIT for { set x 0 } { $x < $curWidth } { incr x } { glCallList [expr {$displayListBase + $x}] } $toglwin swapbuffers } frame .fr pack .fr -expand 1 -fill both togl .fr.toglwin -width $width -height $height \ -double true \ -createproc tclCreateFunc \ -reshapeproc tclReshapeFunc \ -displayproc tclDisplayFunc label .fr.info grid .fr.toglwin -row 0 -column 0 -sticky news grid .fr.info -row 1 -column 0 -sticky news grid rowconfigure .fr 0 -weight 1 grid columnconfigure .fr 0 -weight 1 wm title . "Tcl3D demo: Simple Chaos Theory" wm protocol . WM_DELETE_WINDOW "exit" bind . <Key-Escape> "exit" PrintInfo [format "Running on %s with a %s (OpenGL %s, Tcl %s)" \ $tcl_platform(os) [glGetString GL_RENDERER] \ [glGetString GL_VERSION] [info patchlevel]] Chaos 0.4 400 $oncolor

- Implementation of slebetman's nice shading idea.
- Interactive selection of chaos parameters.
- Speed improvements by using a column cache.
- Switch online between use of OpenGL or photo image for drawing.
slebetman Another variant, this time shaded - the more times a pixel is hit the darker it gets:
# Implementation using image set width 640 set height 480 image create photo CANVAS -width $width -height $height pack [label .l -image CANVAS] -fill both -expand 1 wm resizable . 0 0 proc incrpixel {x y colorspec {reverse {}}} { foreach {RR GG BB} $colorspec break foreach {r g b} [CANVAS get $x $y] break set orig [color $r $g $b] foreach {c CC} {r RR g GG b BB} { set CC [set $CC] if {$reverse == "-reverse"} { set CC -$CC } set $c [expr {[set $c]-$CC}] if {[set $c] > 255} {set $c 255} if {[set $c] < 0} {set $c 0} } set CC [color $r $g $b] # don't need to waste CPU cycles if color doesn't change if {$CC != $orig} { CANVAS put $CC -to $x $y } } proc color {r g b} { return "#[format %02x $r][format %02x $g][format %02x $b]" } proc chaos {iterations colorspec {reverse {}}} { global width height set x .4 if {$reverse == "-reverse"} { CANVAS put black -to 0 0 $width $height } else { CANVAS put white -to 0 0 $width $height } for {set sx 0} {$sx < $width} {incr sx} { set r_value [expr { pow(($sx*1.0)/$width, 0.25) * 3.0 + 1.0} ] for {set i 1} {$i <= $iterations} {incr i} { set x [expr {$r_value * $x * (1 - $x)}] # We're shading, no need to skip anything the starting # bits will simply be less dark if not near attractors. set sy [expr {int($height - $x*$height)}] incrpixel $sx $sy $colorspec $reverse } update } } chaos 800 {24 24 24} # uncomment the following to save image to file: #CANVAS write chaos.gif -format gifYou can also draw on a black canvas by specifying -reverse. And you can change colors by playing with the colorspec parameter. For example, this is red:
chaos 800 {10 24 24}and this is red on a black background:
chaos 800 {24 10 10} -reverseIncreasing the colorspec values increases the overall brightness of the image. Increasing iteration increases the white point. Thus low colorspec numbers coupled with high number of iteration results in a high contrast image. Which leads to my favourite combination so far:
chaos 2000 {8 10 14} -reverseresulting in:

chaos 6300 {3 3 3} -reverse
