In the November edition of Scientific American was an article titled "Is Inequality Inevitable?". The author, Bruce Boghosian, is a math professor at Tufts Univerisity and he makes a good case for how wealth trickles up to the top pretty much automatically.

As an example he uses a coin flip that wins you 20% if you win and costs you just 17% if you lose. So if put down $100, and then the coin is flipped. If it comes up heads your opponent adds $20 to your $100. If the coin come up tails, then $17 dollars comes off your pile and onto his. You may both continue as long as you want. Each time you win the flip, your stake grows 20%. Each time you 17% comes off.

In a series of flips you would probably expect that the money on the table would grow over time. But this is not the case. Here's why. If you lose the first flip you have $83 on the table. If you then win the second flip you might expect that you now have $103. But instead you have $99.60!

Because 20% of $83 doesn't cover the loss.

And if you win the first flip, leaving you with $120 on the table, but then lose the second flip you also have $99.60. 17% of $120 is slighty more than $20.

No real magic here. Both yield the same result simply because "x*y" is equal to "y*x".

The calulations for the two scenarios are

(100+20)*0.83 is 99.60 - win then loss (100-17)*1.20 is 99.60 - loss then win

So, you are 40 cents down either way.

Now, this might be disturbing. If both you and your opponent have the same stake, you can't both be down 40 cents. And you won't. With each flip the bet is based on 20/17 of the smaller pile. If your opponent has a much bigger pile than you, then the size of the bet is always based on your pile. Let's assume your opponent initally put up $1000.

you opponent init $100.00 $1000.00 loss $ 83.00 down 17% of $100 $1017.00 win $ 99.60 up 20% of $ 83 $1000.40

Let's play 10 games with 20 flips in each game. You might win anywhere from zero to 10 games but the extremes are unlikely. Here a table of some results and your standing at the end. The first column adds to 10 and is the number of games that come up with the same number of heads in the second column.

1 006 heads in 20 flips $ 21.99 1 008 heads in 20 flips $ 45.96 3 009 heads in 20 flips $ 66.45 2 010 heads in 20 flips $ 96.07 1 011 heads in 20 flips $ 138.90 1 012 heads in 20 flips $ 200.82 1 013 heads in 20 flips $ 290.34

Right in the middle, if you win 10 times out of 20 flips, you end up with 96.07 which is just a rounding error away from ten times the 40 cents above.

Another scenario that is described in the article involves multiple players each starting out with $100 and playing randomly with each other, always using the smaller balance to set the size of the bet. In the plots below each player is represented by a certain color. It's interesting to see the chaos as the playing proceeds. But notice how one player, Diane, has almost all the money after 1000 transactions.

$ python group.py trades=1000 seed=8623 rates 0.2 0.17 437.39 Diane orange 133.70 Betty green 28.41 Erica yellow 0.48 Alan red 0.02 Charlie blue 0.00 Frank purple

Next we show two plots of the same sequence of wins (seeding the random number generator) for both 500 and 1000 flips. You can see how the inequality keeps increasing over time.

$ python group.py trades=500 seed=3795 rates 0.2 0.17 270.35 Alan red 265.92 Diane orange 31.30 Frank purple 19.57 Charlie blue 12.00 Erica yellow 0.86 Betty green $ python group.py trades=1000 seed=3795 rates 0.2 0.17 454.61 Alan red 96.64 Diane orange 38.32 Frank purple 10.11 Charlie blue 0.24 Erica yellow 0.08 Betty green

A wealth tax is often mentioned as a way to put a brake on this natural upflow of money. Here we simulate a 5% tax on wealth over $200. The tax collected is then spread evenly over all players (including the one taxed).

There are two plots. The first with 500 flips, and the second 1000. The game is still pretty chaotic. But notice how Charlie in blue came from the bottom in the first 500 flips to the top in the next 500. Peak wealth is held at just under $300.

$ python group.py trades=500 seed=3795 taxRate=.05 rates 0.2 0.17 228.41 Alan red 125.66 Diane orange 99.63 Erica yellow 72.08 Frank purple 43.75 Betty green 30.48 Charlie blue $ python group.py trades=1000 seed=3795 taxRate=.05 rates 0.2 0.17 178.59 Charlie blue 143.42 Betty green 84.48 Alan red 77.01 Frank purple 68.76 Diane orange 47.75 Erica yellow

The class Player is used only to supply attributes for each player. There are no methods in the class. the players name, and money are obvious. color and hist are kept for making the plots.

Python's random number generator supplies the "heads" and "tails" calls. The function *flipcoin* takes 2 players and the win/loss rates. Even odds for heads or tails (line 10) and then the money is adjusted for each player. The function returns *True* if it was heads or *False* if tails.

The function *randomSeed* gives us the option to seed the random number generator. This lets us reproduce earlier runs of the program. A value like "seed=1234" may be passed on the command line. If it's not then *randomSeed* will choose a seed on its own and print the seed so that you can reuse the future runs.

Here is a listing of *common.py* that contains these features.

03 import random, sarg 04 05 class Player : 06 def __init__ (s, name, color, money=100.0) : 07 s.name = name; s.color=color; s.money=money; s.hist=[money] 08 09 def flipCoin (player1, player2, winRate, lossRate) : 10 heads = (random.random() >= .5) 11 amt = min(player1.money, player2.money) 12 if heads : 13 player1.money += amt*winRate 14 player2.money -= amt*winRate 15 else : 16 player1.money -= amt*lossRate 17 player2.money += amt*lossRate 18 return heads 19 20 def randomSeed() : 21 seed = sarg.Int("seed",0) 22 if seed == 0 : 23 seed = int(random.random()*10000) # choose a seed 24 print "--- seed=%d !" % seed 25 random.seed(seed)

You might notice on line 21 a call that picks up a seed for the random number generator from the command line. Documentation for *sarg.py* (simple arguments)
can be found in the pglib project

The following listing contains the group playing program above. We'll explain it in chunks but since we have already seen it run, the code should be quite understandable.

01 #!/usr/local/bin/python 02 # 03 # group.py 04 # 05 import random, sarg 06 from common import Player, flipCoin, randomSeed 07 08 names = ['Alan', 'Betty', 'Charlie', 'Diane', 'Erica', 'Frank'] 09 colors= [ 'red', 'green', 'blue','orange','yellow', 'purple'] 10 11 def groupPlay() : 12 players = [Player(names[i],colors[i]) for i in range(len(names))]

The array *players* contains the 5 players. Their names and colors are assigned here. They are automatically given $100 to start

13 winRate = sarg.Float("winRate", .20) # fraction player gets for win 14 lossRate = sarg.Float("lossRate",.17) # fraction player pays for loss 15 taxRate = sarg.Float("taxRate", .00) # tax over $200 each transaction 16 print "rates", winRate, lossRate 17

The *sarg* module picks up the variables for the run from the command line or just uses the default in the second argument.

18 for j in range(sarg.Int("trades",500)): 19 random.shuffle(players) 20 isHeads = flipCoin(players[0], players[1], winRate, lossRate) 21 wealthTax(players, taxRate) 22 for player in players : # for each player 23 player.hist.append(round(player.money,2))

The *for* loop at line 18 handles the tradeing. The players are shuffled like cards, and the top two flip the coin. If there is a wealth tax it's applied. The *for* loop at line 22 saves the money history for each player.

24 ranking = [(p.money, p) for p in players] 25 ranking.sort() 26 for bal,player in reversed(ranking) : 27 print "%8.02f %-8s %s" % (bal, player.name, player.color) 28 plotMoney(players)

Finally, the players are sorted by wealth with the richest at the top. This is then printed as we saw above and then plotted.

29 30 def wealthTax(players,taxRate) : 31 # if wealth exceed 200, tax the excess at the taxRate 32 totTax = 0.0 33 for player in players : 34 tax = max(0., player.money-200)*taxRate 35 totTax += tax 36 player.money -= tax 37 dist = totTax/len(players) # spread evenly to all players 38 for player in players : player.money += dist

The wealth tax may be applied if specified in the command but the default is zero. The rich are taxed at line 34 and the proceeds distributed in line 38.

39 40 def plotMoney(players) : 41 import matplotlib.pyplot as plt 42 plt.suptitle("Money over time", size=16) 43 for player in players : 44 xps = list(range(len(player.hist))) 45 yps = player.hist 46 plt.plot(xps,yps, player.color) 47 plt.show()

I won't go into detail on the plotting. Each player is drawn in its color with lines connecting one history point to the next. Good documentation on matplotlib is available on the web.

48 49 if __name__ == "__main__" : 50 randomSeed() 51 groupPlay()

And finally, the program is kicked off. You can download the above code here

If you have comments or suggestions You can email me at mail me

Copyright © 2020 Chris Meyers