On the Lighter Side: A coin toss tester for multiples in a row

-- I read that if you flipped a coin 200 times, you were virtually assured of at least one run of 6 heads or 6 tails in a row. This little script lets you explore that.

-- Trails is the number of times to try the whole test
-- Tosses is how many coin tosses there are in one sample
-- numInRun is how many sequential Heads or Tails to test for
-- The answer appears in the Results Pane of the Script Editor

-- A script to hold the results of the tosses of the coin
script T
	property SideUp : {}
end script
-- Set up conditons
set Trials to 20
set Tosses to 200
set numInRun to 6
set Avg to 0
-- Outer loop to repeat the test
repeat Trials times
	set T's SideUp to {}
	-- Fill up the list of tosses
	repeat Tosses times
		set end of T's SideUp to (random number from 1 to 2) - 1 -- 0 or 1
	end repeat
	-- Multiples() tests to see if there are 6 or more similar tosses in a row
	set Avg to Avg + Multiples(T's SideUp, Tosses, numInRun)
end repeat
-- The results
set Expt to Avg div Trials
if Expt = 0 then
	"Missed - there were no runs of " & numInRun
else
	"There were " & Expt & " runs of heads or tails on average"
end if
-- The handler
to Multiples(Lst, sampSize, runSize)
	set Instances to 0
	repeat with k from 1 to sampSize - runSize
		set Total to 0
		-- Total six tosses in a row, a sum of 6 or 0 indicates a string
		repeat with j from 0 to runSize - 1
			set Total to Total + (Lst's item (k + j))
		end repeat
		if Total = 0 or Total = runSize then set Instances to Instances + 1
	end repeat
	return Instances
end Multiples

Funny script, Adam, and very interesting piece of code to optimize in speed (if you like such things :smiley: )

Here is a different version. It includes regular speed-tricks, and specially the substitution of “random number” with “some item of”:

script o
	property a : {}
end script

set tailsNum to 0
set headsNum to 0
set Trials to 20
set Tosses to 200
set numInRun to 6

set ones to text 1 thru numInRun of "11111111111111111111"
set twos to text 1 thru numInRun of "22222222222222222222"

repeat Trials times
	set o's a to {}
	repeat Tosses times
		set o's a's end to some item of {1, 2}
	end repeat
	set a to o's a as text
	set AppleScript's text item delimiters to ones
	set tailsNum to tailsNum + (count (a's text items)) - 1
	set AppleScript's text item delimiters to twos
	set headsNum to headsNum + (count (a's text items)) - 1
	set AppleScript's text item delimiters to {""}
end repeat

--> calc averages
display dialog "Averages for " & Trials & " trials:" & return & return & ¬
	(tailsNum / Trials) & " tails" & return & ¬
	(headsNum / Trials) & " heads" with icon note

I love such things, and knew posting it here might get a rise out of someone good at optimizing. I didn’t think of text item delimiters as a means of checking for runs of 1’s and 2’s, but it’s obvious after a few trials that “some item” is MUCH faster. It’s not doing exactly what my form did, but I can easily get there from here. I actually have a reason for doing this.

Thanks, ACB

Here’s a different approach to the exploration. Averaging the results over several “trials” doesn’t tell you whether or not any particular sequence of 200 tosses contains a run of at least six heads or tails ” which is the claim being explored. The following performs just one trial of 200 “tosses”.

-- "Toss a coin" 200 times. Return a record containing:the head/tail results,
-- the lengths of the runs of heads or tails, and whether or not there was
-- at least one run of six or more equal results.

set results to {results:{}, runs:{heads:{}, tails:{}}, at_least_one_run_of_six_or_more:false}
set heads_or_tails to {"t", "h"}

-- The first toss (starting the first run).
set thisResult to (random number 1 with seed (time of (current date))) -- Heads = 1, tails = 0.
set end of my results's results to item (thisResult + 1) of my heads_or_tails
set thisRun to 1
set thisRunOf to thisResult
-- The remaining 199 tosses, keeping track of runs of equal results,
-- and watching out for any such run of at least six.
repeat 199 times
	set thisResult to (random number 1)
	set end of my results's results to item (thisResult + 1) of my heads_or_tails
	if (thisResult = thisRunOf) then
		set thisRun to thisRun + 1
		if (thisRun is 6) then set results's at_least_one_run_of_six_or_more to true
	else if (thisRunOf is 1) then
		set end of my results's runs's heads to thisRun
		set thisRun to 1
		set thisRunOf to 0
	else
		set end of my results's runs's tails to thisRun
		set thisRun to 1
		set thisRunOf to 1
	end if
end repeat
-- Insert the remaining run figures.
if (thisRunOf is 1) then
	set end of my results's runs's heads to thisRun
else
	set end of my results's runs's tails to thisRun
end if

return results

Certainly is true by this standard - after many trials, I haven’t seen a single false.

The story that goes with this is of a professor of statistics who asked his class to hand in a list of two hundred coin tosses that could be composed by experiment or calculation, call it “real”, or by faking it and just filling in two hundred picks out of the blue.

When they were handed in to a later session, he separated the answers into two piles, real and faked, by inspection right in front of the class in only a few moments. His test was simply to count runs. If no runs of 6 were included, it was fake. He was right every time.