But that doesn't help me win the lottery :-)

On Feb 9, 2011 6:21am, Ken Wesson <kwess...@gmail.com> wrote:
It's likely that many of you have read, or at least encountered links

to, this Wired article recently:



http://www.wired.com/magazine/2011/01/ff_lottery/all/1



In it, a particular tic-tac-toe themed scratch lottery is examined in

detail. A mathematician, Mohan Srivastava, discovered weaknesses in it

and many similar lotteries. The article claims:



> Fundamentally, he believes that creating impregnable tickets is

> extremely difficult, if not impossible. “There is nothing random about

> the lottery,” he says. “In reality, everything about the game has been

> carefully designed to control payouts and entice the consumer.” Of

> course, these elaborate design elements mean that the ticket can be

> undesigned, that the algorithm can be reverse-engineered. The veneer of

> chance can be peeled away.



I had my doubts whether it was impossible to make it secure while

still controlling payouts so I took a stab at writing my own algorithm

to generate tic-tac-toe cards that follow the same rules as the ones

depicted in the Wired article.



Particularly, the player gets 24 hidden numbers, selected from the

numbers between 1 and 39 inclusive, and eight tic-tac-toe boards with

exposed numbers. No board has two of the same number; no board has

more than one winning row, column, or diagonal; and all of the rows,

columns, and diagonals on the board have the same prize values.



My thought was to use a two stage process.



First, generate the part of the ticket visible without scratching anything.



Then, generate the 24 hidden numbers, in such a way that the same

visible-part can be made a winner or a loser.



Of course it's a bit more complex than just that. The 8 visible

tic-tac-toe grids cannot be truly random without it being impossible

to get a desired statistical distribution of prizes. So the grid

generation has to be constrained somewhat. Eventually I came up with a

program that produces visible-parts that the user can, with some

difficulty, assort into five distinguishable species with three

subspecies each, but I tuned the hidden-number algorithm so that the

average payout for all combinations is roughly a dollar (with the

ticket price being three, and also the minimum non-zero prize, so the

house should make about two bucks a ticket on average, coming out in

the black). Therefore the user cannot game the distinguishable species

and subspecies to cherry-pick any subset of tickets with a

statistically significantly higher expected payout. In particular they

can expect to lose a couple of bucks on each ticket no matter what

numbers they see in the uncovered part of that ticket.



Here is a sample ticket generated with this program by calling (create-ticket):







[(3 4 6 11 13 14 15 16 17 18 19 20 ; hidden numbers

21 24 27 28 29 30 32 33 34 35 37 39)



(((37 33 10)

(35 25 17) ; upper left tic-tac-toe grid

(26 15 3))



((10 3 37)

(17 25 33) ; upper right grid

(34 15 26))



((23 18 39)

(14 24 5) ; second-to-top left

(22 31 27))



((37 3 10)

(33 25 17)

(26 15 27))



((6 13 7)

(20 1 2)

(8 11 30))



((37 33 10)

(29 25 17)

(26 15 35))



((28 34 38)

(36 19 30) ; lower left

(13 9 12))



((6 18 7)

(4 1 2) ; lower right

(8 11 34)))]







There are a few patterns that might leap out at more mathematically

inclined readers; certain of the number positions, such as the center

and upper corners, have only four possible values with two of them

more common than the other two. This is unavoidable but it is also the

case that these patterns cannot be used to guess the prize amount

without knowing the hidden numbers. In fact the most that can be

determined from them is whether it has about a 1 in 3 chance of being

a $3 winner and a 2 in 3 chance of losing, an almost certain loser

with a 1% chance of winning $100, or able to generate a variety of

prizes other than $3 and averaging $1 per ticket. The most serious

weakness here is that a thrill-seeking player can, in theory, zero in

on roughly half of the tickets as having no chance to generate the

big-money prizes ($250 and up) and avoid those. They can't, however,

game these tickets to alter substantially their expected loss of $2

per ticket bought.



The above has three discernible groups of grids: four with 25 in the

center, two with mostly smaller numbers, and two more. This indicates

that it's a species likely to produce 0, 1, or 2 winning grids and

very unlikely to produce more; the bottom left corner is one of the

odd ones and the top right isn't so it probably belongs to the

varied-prizes subspecies capable of generating the big $50,000 maximum

prize. The odds of a ticket like this one winning $50,000 are

minuscule, however.



Virtually "scratching" this ticket and matching up the numbers with

the hit-grids function reveals:







([[true true false] ; XX .

[true false true ] ; X . X

[false true true ]] ; . XX



[[false true true ] ; . XX

[true false true ] ; X . X

[true true false]] ; XX .



[[false true true ] ; . XX

[true true false] ; XX .

[false false true ]] ; . . X



[[true true false] ; XX .

[true false true ] ; X . X

[false true true ]] ; . XX



[[true true false] ; XX .

[true false false] ; X . .

[false true true ]] ; . XX



[[true true false] ; XX .

[true false true ] ; X . X

[false true true ]] ; . XX



[[true true false] ; XX .

[false true true ] ; . XX

[true false false]] ; X . .



[[true true false] ; XX .

[true false false] ; X . .

[false true true ]]); . XX







Like about 8 out of 9 of the tickets this code generates, ours is not a winner.







Below is the code that generated this, plus a small suite of simple

tests, and a detailed discussion of it all. To understand the code and

its discussion, it is recommended that you visit the link above and

familiarize yourself with the appearance of the tic-tac-toe tickets,

particularly the geometry of the potential winning lines and

corresponding prize amounts.



This section is fairly technical so you may want to skip to the

concluding remarks at the bottom of this post with control-end and

page-up now. You can also control-F for



sample test result



to get there quickly. That phrase occurs at the start of the

conclusion and nowhere between here and there. In Firefox and

Thunderbird, triple-clicking the isolated line above will select it

for quick copy/paste.







The first part of the code is a set of utility functions centered

around randomizing various things: making shuffled vectors from seqs,

uniformly picking a random element from a coll, picking randomly from

a set of alternatives with specified probability weightings, and

generating a boolean that has a 1 in N chance of being true. That last

is mainly used in ifs: (if (one-in N) (do-rare-thing)

(do-common-thing)); (one-in 2) generates a coin flip boolean and

(one-in nil) is constantly false, so nil can be used as "infinity"

here, eg



(if (one-in

(cond

(some-test) 1 ; always

(another-test) 2 ; half the time

(test-no-3) 100 ; rarely

:otherwise)) nil ; never

(do-something))



All of these use the rand-int function and none of the rest of the

code directly uses the RNG. These functions are thus the one area that

needs modifying to drop in another RNG implementation, which could be

done with a replacement for rand-int that calls a custom

implementation that, say, stores its state in an atom held in a global

var. This allows replacement of the default RNG with something like,

say, a Mersenne Twister or an entropy pool or even, hardware

permitting, a quantum source of genuine randomness.







(defn shuffle [ss]

(let [s (count ss)]

(loop [out (transient (vec ss)) n (dec s)]

(if (zero? n)

(persistent! out)

(let [i (rand-int (inc n))

t (out n)]

(recur

(assoc!

(assoc! out n (out i))

it)

(dec n)))))))



(defn rand-elt [s]

(nth s (rand-int (count s))))



(defn rand-elt-fmap [fmap]

(let [ftot (reduce + (vals fmap))

n (rand-int ftot)]

(loop [i 0 k nil fmap (seq fmap)]

(if (
k

(let [f (first fmap)]

(recur (+ i (val f)) (key f) (next fmap)))))))



(defn one-in [n]

(and n (= (rand-int n) 0)))







The next part of the code generates "templates", which are

three-element vectors whose final element is the visible eight

tic-tac-toe grids. That's a list of eight lists of three lists of

three integers. The other two parts are used by the hidden number

generator. Importantly, the latter can make a winner or a loser out of

any ticket. The only thing different across the different ticket

"species" is the number of possibly-winning grids: one species will

fairly often have one possibly-winning grid and rarely have 7, another

sometimes 2 and rarely 6, another occasionally 3 and rarely 5, another

infrequently 4, and another rarely 8. These are successively rarer, as

well as having increasing probabilities of losing. Each has three

subspecies which are subtler to spot but produce more distinct prize

distribution differences -- one produces rare $100 wins, one common $3

wins, and one wins of various amounts except $3 at various

frequencies. All species and subspecies have about the same expected

total prize amount, though -- roughly $1.







(def t1

[[:za :ca :xc]

[:cb :xx :ys]

[:yc :zb :cc]])



(def t2

[[:xc :ca :za]

[:ys :xx :cb]

[:cc :zb :yc]])



(def rarities [t1 t2 t1 t1 t1 t1 t2 t1])



(defn create-template []

(let [nums (shuffle (range 1 40))

cs (subvec nums 0 15)

xs (subvec nums 15 23)

ys (subvec nums 23 31)

zs (subvec nums 31)

xx1s (subvec xs 0 2)

xx2s (subvec xs 2 4)

xc1s (subvec xs 4 6)

xc2s (subvec xs 6)

yc1s (subvec ys 0 2)

yc2s (subvec ys 2 4)

ys1s (subvec ys 4 6)

ys2s (subvec ys 6)

za1s (subvec zs 0 2)

zb1s (subvec zs 2 4)

za2s (subvec zs 4 6)

zb2s (subvec zs 6)

grid-nums (map #(+ (* 2 %1) %2)

(shuffle

(rand-elt-fmap

{[0 0 0 0 0 0 0 0] 1

[0 0 0 0 0 0 0 1] 100

[0 0 0 0 0 0 1 1] 50

[0 0 0 0 0 1 1 1] 20

[0 0 0 0 1 1 1 1] 5

[0 0 0 1 1 1 1 1] 20

[0 0 1 1 1 1 1 1] 50

[0 1 1 1 1 1 1 1] 100

[1 1 1 1 1 1 1 1] 1}))

(repeatedly 8 #(rand-int 2)))

card (map

(fn [grid num]

(let [twos (> num 1)

m (mod num 2)

xxs (if twos xx2s xx1s)

xcs (if twos xc2s xc1s)

ycs (if twos yc2s yc1s)

yss (if twos ys2s ys1s)

zas (if twos za2s za1s)

zbs (if twos zb2s zb1s)

cs (shuffle cs)

ca (cs 0)

cb (cs 1)

cc (cs 2)]

(map

(fn [row]

(map

(fn [cell]

(condp = cell

:ca ca

:cb cb

:cc cc

:za (zas m)

:zb (zbs m)

:xx (xxs m)

:xc (xcs m)

:yc (ycs m)

:ys (yss m)))

row))

grid)))

rarities grid-nums)]

[nums grid-nums card]))







The template generator takes a random permutation of 1 to 39 and

partitions it into 15 common numbers, which will be among the hidden

numbers on all tickets generated from that template, and 24 controlled

numbers, only 9 of which will appear on each ticket and which are

subdivided into three sets of 8, called xs, ys, and zs, which are each

further categorized in three ways: "ones" versus "twos" and "parity",

about which see below, and two possible positions within grids: for

the xs, center or corner, for the ys, side or corner, and for the zs,

a or b.



The t1 and t2 templates are grid templates with the numbers replaced

by categories: za and zb for the zs, xx (center) and xc (corner) for

the xs, and yc (corner) and ys (side) for the ys, as well as three

cells marked ca, cb, and cc which can get arbitrary common numbers.



These templates both have the property that every winning line must

cross either an x and ay, an x and az, or ay and a z. Further, one

of the diagonals (which generate the most valuable prizes) requires

both xs and a y. This feature is used to control the prize

distribution at the coarsest level and to make it possible to make

both losing and winning tickets based on any template this function

can emit.



The rarities template is mostly t1s with two t2s. The t2 template is

simply a mirror image of the t1 template and the rarities template

arranges things so the four biggest prizes, the two $50,000s and the

two $1000s, are xxy diagonals. These diagonals will be made very

rare.



The template generator first shuffles 1 to 39 and partitions the

result; these numbers, in this sequence, will be passed to the

hidden-number generator as the first element of the template vector.

Then it generates the second part: a seq of 8 numbers between 0 and 3.

Each grid is assigned one of these numbers. It's actually a bitfield

of sorts: the high bit divides the grids into "ones" and "twos" and

the low bit gives some of them "even" parity and some "odd". Note that

the various types of xs, ys, and zs come in pairs; the parity is used

sometimes to prevent certain winning lines from occurring in

hidden-number generation. Depending on a grid's parity, only the

zeroth or only the first elements of those vectors make it into that

grid. Similarly, there are xs that only occur in "ones" and xs that

only occur in "twos". So, the 0 1 2 3 number of a grid determines

which of four sets of controlled numbers occur on that grid.



Further, the distribution of "ones" and "twos" is skewed so most cards

will have one of one type and seven of the other, fewer will have two

of the rarer type, fewer still will have three, fewer still will have

four of each, and only rarely will a card have all of the same type.

(These produce the five distinguishable species). As you might have

guessed, the hidden-number generator, by choosing from among the xs

and ys and zs appropriately, will make only the "ones" or only the

"twos" eligible to win, and will almost always choose the rarer of the

two. This means most winning cards have a single win, fewer have two,

and so on. The user might try to game that by picking a card with a

less skewed number distribution (or, if he's lucky enough, one with a

lot of non-occurring numbers) hoping for four or even eight winning

grids, but the hidden-number generator compensates by making more of

those cards pure losers than of cards that have the more skewed

distributions.



The card itself just uses random common numbers along with the

grid-number (0 to 3) determined xs, ys, and zs to fill in each of the

t1 or t2 templates.



The output vector is the shuffling of the range 1 to 39, the

grid-nums, and the exposed part of the card.







(defn ifilt [coll idx]

(if idx [(nth coll idx)] coll))







This is a utility function used when generating the hidden numbers. It

takes a coll and an index and if index is nil, returns the coll,

otherwise returns a one-element vector of the coll's element at that

index. So, it can be used to selectively narrow a coll down to one

element or not. In practice, it gets used on two-element collections

(specific pairs of xs, ys, or zs) with either 0, 1 or nil (so, a

parity or nil) as idx to force parity 0, force parity 1, or allow both

parities. It plays a role in dealing with a complex set of possible

winning cards.







(defn pick-player-nums [[nums grid-nums _]]

(let [cs (subvec nums 0 15)

xs (subvec nums 15 23)

ys (subvec nums 23 31)

zs (subvec nums 31)

xx1s (subvec xs 0 2)

xx2s (subvec x

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to