Personal code snippets of @tmasjc

Site powered by Hugo + Blogdown

Image by Mads Schmidt Rasmussen from unsplash.com

Minimal Bootstrap Theme by Zachary Betz

Puzzle: Biased Coin Toss

Apr 21, 2019 #monte-carlo

From this paper “What’s Past Is Not Prologue” comes a quiz,

You are presented with two coins: one is fair, and the other has a 60% chance of coming up heads. Unfortunately, you don’t know which is which. How many flips would you need to perform in parallel on the two coins to give yourself a 95% chance of correctly identifying the biased one?

The following demonstrates 2 simulation methods to obtain the result.

library(tidyverse)
library(ggthemes)
library(discreteRV)
set.seed(1212)

# ggplot theme
old <- theme_set(theme_tufte() + theme(text = element_text(family = 'Menlo')))
# limit range from 50 to 200 flips
flips  = 50:200
trials = 3000

# Method One --------------------------------------------------------------


# actually simulate the action of tossing 2 coins
method_one <- function(x) {
    regular = rbinom(x, 1, .5)
    biased  = rbinom(x, 1, .6)
    # return
    sum(biased) - sum(regular)
}

# repeat action and bind into a data frame
res_method_one <-
    map_dbl(flips, ~ {
        sum(replicate(trials, method_one(.x)) > 0) / trials
    }) %>%
    bind_cols(flip = flips, prob = .)

# first occurance
min_flip_of_method_one <-
    res_method_one %>%
    filter(prob >= 0.95) %>%
    slice(1)

# visualize result
res_method_one %>%
    ggplot(aes(flip, prob)) +
    geom_line() +
    geom_vline(xintercept = min_flip_of_method_one$flip, lty = 4, col = "red") +
    geom_hline(yintercept = min_flip_of_method_one$prob, lty = 4, col = "red") +
    annotate(
        "text",
        x = min_flip_of_method_one$flip,
        y = .5,
        label = as.character(min_flip_of_method_one$flip)
    ) +
    scale_y_continuous(breaks = seq(.5, 1, .1), labels = scales::percent)

# Method Two --------------------------------------------------------------


# we will reuse this function repeatedly to test different diff flips
method_two <- function(x) {
    
    # how many tosses?
    regular = RV(1:x, dbinom(1:x, size = x, prob = .5))
    biased  = RV(1:x, dbinom(1:x, size = x, prob = .6))
    
    # joint pmf
    jpmf <- joint(regular, biased)
    
    # probability = proportion of combination of possible outcomes
    tibble(prob = probs(jpmf), comb = outcomes(jpmf)) %>%
        separate(comb, c("fair", "biased"), ",", convert = TRUE) %>%
        # if biased lands more than unbiased coin, choose correctly
        # if both lands the same, choose randomly
        mutate(correct = case_when(biased >  fair ~ 1.0,
                                   biased == fair ~ 0.5,
                                   biased <  fair ~ 0.0)) %>%
        # the likelihood that guessing correctly happens
        mutate(likelihood = prob * correct) %>%
        filter(correct > 0) %>%
        summarise(conf = sum(likelihood)) %>%
        pull(conf)

}

# loop thru flips
res_method_two <- 
    flips %>% 
    map_dbl( ~ method_two(.x) ) %>% 
    bind_cols(flip = flips, prob = .)

# minimum flip to obtain 95% confidence
min_flip_of_method_two <- 
    res_method_two %>%
    filter(prob >= 0.95) %>%
    slice(1)

res_method_two %>%
    ggplot(aes(flip, prob)) +
    geom_line() +
    geom_vline(xintercept = min_flip_of_method_two$flip, lty = 4, col = "red") +
    geom_hline(yintercept = min_flip_of_method_two$prob, lty = 4, col = "red") +
    annotate(
        "text",
        x = min_flip_of_method_two$flip,
        y = .5,
        label = as.character(min_flip_of_method_two$flip)
    ) +
    scale_y_continuous(breaks = seq(.5, 1, .1), labels = scales::percent)