[Arjen Markus] (4 january 2015) Parrondo's Paradox, described in some detail in this [http://en.wikipedia.org/wiki/Parrondo%27s_paradox%|%Wikipedia page%|%] and clearly analysed in [http://www.nature.com/srep/2014/140228/srep04244/full/srep04244.html%|%this article%|%] illustrates how statistics can be highly unintuitive. The paradox is shown here via two simple games: * In game A a single unfair coin is tossed: there is a probability of 49.5% that it is "heads". This increases your capital by 1, whereas "tails" means you lose 1. Clearly, in the long run you will have a negative capital. * In game B two coins are tossed. If your current capital is divisible by three, coin 1 is used, otherwise coin 2 is tossed. Again "heads" increases your capital by 1 and "tails" decreases it by the same amount. Coin 1 gives "heads" with a 9.5% probability and coin 2 gives "heads" in 74.5% of the tosses - but it is used less. Again the expected outcome in the long run is negative. Now we combine the two games: we throw an unbiased coin to select game A or game B, so 50% percent of the time we play game A and 50% percent of the time we play game B. The surprising thing is, that this ''combined game'' is a winning game: -- picture -- I have implemented this using the [vectcl] package, as playing a large number of these games in parallel is required to estimate the mean outcome. Here is the code, it is only one possibility of course. Note the way [Plotchart] is integrated with the calculation. ====== # parrondo.tcl -- # Simulation of Parrondo's Paradox: two losing games combined give a winning game. # # The simulation concerns 10000 instances of the separate games and the # combined game over 500 steps. # # Note: not really optimised yet # package require vectcl lappend auto_path c:/tcl/lib package require Plotchart # fillzero -- # Return an array of integer zeros (constfill returns doubles) # # Arguments: # sz Size of the array # # Returns: # Array of "sz" integer zeros # proc fillzero {sz} { set array {} for {set i 0} {$i < $sz} {incr i} { lappend array 0 } return $array } # randomNumbers -- # Return a list of random numbers # # Arguments: # sz Size of the list # # Returns: # Array of "sz" random numbers # proc randomNumbers {sz} { set r {} for {set i 0} {$i < $sz} {incr i} { lappend r [expr {rand()}] } return $r } # gameA -- # Determine the outcome according to game A # # Arguments: # capitalName Current capital for all the instances (name!) # # Returns: # Nothing # # Side effects: # The new values are stored in the array "capitalName" # proc gameA {capitalName} { upvar 1 $capitalName capital vexpr { sz = shape(capital) random = randomNumbers(sz) capital = capital + (random <= 0.495) - (random > 0.495) } } # gameB -- # Determine the outcome according to game B # # Arguments: # capitalName Current capital for all the instances (name!) # # Returns: # Nothing # # Side effects: # The new values are stored in the array "capitalName" # proc gameB {capitalName} { upvar 1 $capitalName capital vexpr { sz = shape(capital) random = randomNumbers(sz) select = (capital%3 != 0) capital = capital + select .* ((random <= 0.745) - (random > 0.745)) \ + (1-select) .* ((random <= 0.095) - (random > 0.095)) } } # gameAB -- # Determine the outcome according to the combination of games A and B # # Arguments: # capitalName Current capital for all the instances (name!) # # Returns: # Nothing # # Side effects: # The new values are stored in the array "capitalName" # proc gameAB {capitalName} { upvar 1 $capitalName capital vexpr { sz = shape(capital) random1 = randomNumbers(sz) random2 = randomNumbers(sz) selectA = (random1 > 0.5) select2 = (capital%3 != 0) capital = capital + selectA .* ((random2 <= 0.495) - (random2 > 0.495)) \ + (1-selectA) \ .* ( select2 .* ((random2 <= 0.745) - (random2 > 0.745)) \ + (1-select2) .* ((random2 <= 0.095) - (random2 > 0.095)) ) } } # plot -- # Plot the current values with an appropriate colour # # Arguments: # series Series identifier for colours # x X-value # y Y-value # proc plot {series x y} { $::p plot $series $x $y } # main -- # Actual simulation # # # Set up the plot # toplevel .t pack [canvas .t.c -width 600 -height 400] set p [::Plotchart::createXYPlot .t.c {0 500 100} {-10 10 5}] $p dataconfig gameA -colour blue -type both $p dataconfig gameB -colour lime -type both $p dataconfig gameAB -colour red -type both # Just mark the y-axis $p plot yaxis 0 0 $p plot yaxis 500 0 $p legendconfig -position top-left $p legend gameA "Game A" $p legend gameB "Game B" $p legend gameAB "Combined game" set capitalName "capital" ;# Workaround - vectcl does not like strings ;) set zeros [fillzero 10000] puts "Game A:" set series "gameA" vexpr { capital = zeros for i=1:500 { gameA(capitalName) if i == 1 || i % 50 == 0 { plot(series,i,sum(capital)/(shape(capital)+0.0)) } } } puts [vexpr {sum(capital)/(shape(capital)+0.0)}] puts "Game B:" set series "gameB" vexpr { capital = zeros for i=1:500 { gameB(capitalName) if i == 1 || i % 50 == 0 { plot(series,i,sum(capital)/(shape(capital)+0.0)) } } } puts [vexpr {sum(capital)/(shape(capital)+0.0)}] puts "Game AB:" set series "gameAB" vexpr { capital = zeros for i=1:500 { gameAB(capitalName) if i == 1 || i % 50 == 0 { plot(series,i,sum(capital)/(shape(capital)+0.0)) } } } puts [vexpr {sum(capital)/(shape(capital)+0.0)}] ====== <>Category Mathematics|Category Numerical Analysis|Category Statistics