Gradient Dissents

Coin Flip Probability with Go

Jun 25, 2018 | 4 minutes read
Share this:

I decided to return to this problem of average probability of getting two heads flipped in a row versus a heads then a tails. I know, beat a dead horse much (💀🐴)? Hear me out…

Sadly, our 100 year old oak in the front yard is slowly dying, which means that for the majority of the day today, I didn’t have electricity…and electricity causes internet, which means that I also didn’t have internet. I know, I could have gone to a coffee shop, campus, hacked into my neighbors’ “toolshed” network, but here’s the thing, taking down an oak tree makes a lot of noise. I have 2 dogs and one we suspect had quite an abusive past and her and loud noises…well…let’s just say she can’t deal.

In an effort to keep them comfortable, I stayed home. It was a good opportunity for me to take the offline tutorials to learn go. I’ve been meaning to do so because it’s fast and the logo is really cute.

Go Logo

So I thought this would be a good learning experience. The tutorials are great but this problem is a good way to start thinking about the language. If you need a refresher of what I am talking about, here’s my post about it in python.

Keep in mind I am still learning here, so there’s most certainly a more elegant solution but here we go…step by step.

Step 1: Load in packages

package main

import (
        "fmt"
        "math/rand"
        "time"
        "reflect"
)

I’m going to say one thing here; I really like how it throws an error when I don’t use a package that I load in.

Step 2: Write a function to simulate a coin flip:

// simulate flipping a fair coin
func FlipCoin() string {
        rand.Seed(time.Now().UnixNano())

        if flipint := rand.Intn(2); flipint == 0 {
               return "tails"
        }
        return "heads"
}

The rand package provides some pretty convenient methods, however, they are deterministic by default and therefore setting the seed is important. I know, seed setting makes it deterministic too, which is why I followed the convention of passing the current nanosecond as the seed. rand.Intn(2) also returns either 0 or 1. Perfect!

Step 3: Flip until condition is met.

// flip until condition is met
func FlipCondition() int {
        flips    := [2]string{FlipCoin(), FlipCoin()}
        previous := 0
        current  := 1
        s        := flips[previous: current+1]
        cond     := []string{"heads","tails"}
        numflips := 2

        for !reflect.DeepEqual(s, cond) {
                s[previous] = s[current]
                s[current] = FlipCoin()
                numflips +=1
        }
        return numflips
}

In this function, it is currently set to stop whenever it finds heads and then tails.

Step 4: Run many trials and return a slice.

// run multiple trials
func FlippityFlip(numtrials int) []int {
        vals := make([]int, numtrials)
        for i := 0; i < numtrials; i++ {
                vals[i] = FlipCondition()
        }
        return vals
}

This simply runs FlipCondition() numtrials times and saves it in a slice.

Step 5: Implement a function to find the mean.

// mean function
func mean(vals[]int) float64 {
        tot := 0.0
        for _, v := range vals{
                tot += float64(v)
        }
        return tot/float64(len(vals))
}

Step 6: Wrap it all together and find the average in the main() function.

func main() {
        trials := FlippityFlip(10000)
        fmt.Println(mean(trials))
}

This runs it 10,000 times and then calculates the average. The final step is to make my way over to a terminal and run the go run command on this file to assess the output.

For heads then tails:

go run coinflip.go
4.0054

For heads then heads:

go run coinflip.go
5.9916

It still works! You know, the great thing about this exercises is that I knew what to expect. This served as a sort of check on my code since, you know, I’m new to all of this…

Oh, if you want to take a look at the code it right here.

comments powered by Disqus