4

Last night I saw the episode 7 of the Squid game tv series. The episode has a game with binomial distribution in the bridge.

enter image description here

Specifically there are 16 players and a bridge with 18 pair of glasses (one pure glass and one safe glass).If one player happened to choose the pure glass then the glass couldn't stand the weight of the player and the glass broke. The next player had the advantage that he/she was starting from the position that the last player had and continues the binomial search.At the end 3 players happened to cross the bridge.

So i was wondering: It is like, I have 16 euros in my pocket and I play head or tails with p = 1/2. Every time I bet on heads. If the coin flip is head then I earn 0 and if is tails I lose 1 euro. What is the probability of hitting 18 times (consecutive or not) heads and to be left 3 euros in my pocket.

I tried to simulate this problem in R:

squid_bridge = function(a,n,p) {
  players = a
  while (position > 0 & position < n) {
    jump  =  sample(c(0,1),1,prob=c(1-p,p))
    position = position + jump
  }
  if (position == 0) 
    return(1) 
  else 
    return(0)
}   

n = 18
trials = 100000
a = 16
p = 1/2
set.seed(1)
simlist = replicate(trials, squid_bridge(a, n, p))

It does not seem to work. Any help?

user438383
  • 5,716
  • 8
  • 28
  • 43
Homer Jay Simpson
  • 1,043
  • 6
  • 19
  • It won't know what position is, you need to give it an initial value in your function near where you define players – cgvoller Oct 15 '21 at 12:54
  • @cgvoller inside the function i must set `position = 0` ? – Homer Jay Simpson Oct 15 '21 at 12:58
  • so i have to make it like `squid_bridge = function(a,n,p) { players = a position = 0 while (position < n) { jump = sample(c(0,1),1,prob=c(1-p,p)) position = position + jump } if (position == 0) return(1) else return(0) }` – Homer Jay Simpson Oct 15 '21 at 13:07

5 Answers5

3

Here is a Monte Carlo experiment in R returning the distribution of the number of failures.

apply(apply(matrix(rgeom(16*1e6,.5)+1,nc=16),1,cumsum)>18,1,mean)
#with details:
#rgeom(16*1e6,.5)+1 for 16 x 10⁶ geometric simulations when
#the outcome is the number of attempts till "success",
# "success" included
#,1,cumsum) for the number of steps till 16th "success"
#)>18 for counting the cases when a player manages to X the bridge
#1,mean) for finding the probability of each of the players to X

This is not a Binomial but a truncated Negative Binomial experiment in that the number of new steps made by each player is a Geometric Geom(1/2) variate unless the 18 steps have been made. The average number of survivors is thus

sum(1-pnbinom(size=1:16,q=17:2,prob=.5))
#Explanation:
#pnbinom is the Negative Binomial cdf
#with size the number of "successes"
#q the integer at which the cdf is computed
#prob is the Negative Binomial probability parameter
#Because nbinom() is calibrated as the number of attempts
#before "success", rather than until "success", the value of
#q decreases by one for each player in the game

whose value is 7.000076, rather than 16-18/2=7!

  • 1
    Could you please say more? I'm not understanding what `q` represents in the `pnbinom` calculation and how the truncation is taken into account, though re-arranging a little bit to `16 - sum(pnbinom(size = 1:16, q = 17:2, prob = 0.5)` I'm pretty sure I'm simulating the same thing in my answer. – Gregor Thomas Nov 03 '21 at 18:47
2

Here is how I think you can model the game in R. The first version is similar to what you have: there's a 50% chance of guessing correctly and if the guess is correct, the players advance a tile. Otherwise they do not, and the number of players decrements by 1. If the number of players reaches 0, or they advance to the end, the game ends. This is shown in squid_bridge1().

squid_bridge1 <- function(players, n, prob) {
  if (players == 0 | n == 18) {
    # All players have died or we have reached the end
    return(players)
  }
  
  jump <- rbinom(1, 1, prob)
  
  if (jump == 0) {
    # Player died
    return(squid_bridge1(players - 1, n, prob))
  }

  if (jump == 1 & n < 18) {
    # Player lives and advances 1 space
    return(squid_bridge1(players, n + 1, prob))
  } 
}

However, this does not accurately depict the game since a wrong guess gives the remaining players additional information. If a player chooses wrong, the probability of the next guess being correct is not 50%, it's 100%. However, after that point the probability of a correct guess decreases to 50%. This can be accounted for with another argument to keep track of the correctness of the previous guess.

squid_bridge2 <- function(players, n, prob, previous) {
  if (players == 0 | n == 18) {
    # The game ends if there are no players or they have reached the end
    return(players)
  }
  
  if (previous == 0) {
    # The previous guess was wrong, but now the players know where to go next
    return(squid_bridge2(players, n + 1, prob, previous = 1))
  }
  
  jump <- rbinom(1, 1, prob)
  
  if (jump == 0) {
    # Player died
    return(squid_bridge2(players - 1, n, prob, previous = 0))
  }
  
  if (jump == 1 & n < 18) {
    # Move is correct. Advance 1 space
    return(squid_bridge2(players, n + 1, prob, previous = 1))
  } 
}

However, there's a catch. It wasn't quite that simple in the show, and players fell for reasons other than an incorrect guess (being pushed, jumping on purpose, etc.). I don't know what a reasonable probability of doing something like this is, but it is likely low, let's say 10%.

not_stupid <- function() {
  x <- runif(1, 0, 1)
  if (x <= 0.1) {
    return(FALSE)
  } else {
    return(TRUE)
  }
}

Since emotions spike just before each move, we will test this prior to each move.

squid_bridge3 <- function(players, n, prob, previous) {
  if (players == 0 | n == 18) {
    # The game is over because there are no players left or they reached the end
    return(players)
  }
        
  if (previous == 0) {
    # The previous guess was wrong, but now the players know where to go next
    return(squid_bridge3(players, n + 1, prob, previous = 1))
  }
  
  if (!not_stupid()) {
    return(squid_bridge3(players - 1, n, prob, previous = 1))
  }
  
  jump <- rbinom(1, 1, prob)
  
  if (jump == 0) {
    # Player died because of either choosing wrong or a self-inflicted loss
    return(squid_bridge3(players - 1, n, prob, previous = 0))
  }
  
  if (jump == 1 & n < 18) {
    # Move is correct. Advance 1 space
    return(squid_bridge3(players, n + 1, prob, previous = 1))
  } 
}

Then running some simulations:

set.seed(123)
trials <- 10000
players <- 16
squid1 <- replicate(trials, squid_bridge1(players, 0, 0.5))
squid2 <- replicate(trials, squid_bridge2(players, 0, 0.5, 1))
squid3 <- replicate(trials, squid_bridge3(16, 0, 0.5, 1))

df <- tibble(squid1 = squid1,
             squid2 = squid2,
             squid3 = squid3) %>%
  pivot_longer(cols = c(squid1, squid2, squid3))

ggplot(data = df,
       aes(x = value)) +
  geom_histogram(bins = 10,
                 binwidth = 1,
                 fill = "cornflowerblue",
                 color = "black") +
  facet_wrap(~name,
             nrow = 3) +
  xlab("# of players to make it to the end") +
  scale_x_continuous(breaks = seq(0, 16, by = 1),
                     labels = seq(0, 16, by = 1))

As you can see below, the first situation is heavily skewed to the left. Since the players are essentially "blindly guessing" at each tile, it is unlikely that any will make it to the end. However, after accounting for the information gained from a wrong guess, it averages somewhere around 7 players making it. By adding in a random chance of falling for another reason, the distribution skews to the left some.

  • Average for first situation: 1.45
  • Average for second situation: 7.01
  • Average for third situation: 4.99

enter image description here

To answer the question of the probability of only 3 players making it, I get ~ 10.8% for the last case

Edit: As requested, here is the code to generate the plots. I also fixed the various functions that had some naming issues (went through a few different names when I made them). It looks like it resulted in a slight bug for the 3rd function, but I have fixed it throughout.

cazman
  • 1,452
  • 1
  • 4
  • 11
  • 1
    Vey clear and robust answer.What are the codes for the plots?Can you edit your answer adding the plot codes? – Homer Jay Simpson Oct 16 '21 at 06:38
  • @cazman your answer is awesome but I cannot run the functions though – Homer Jay Simpson Oct 16 '21 at 06:41
  • @TheRed See edits. I couldn't decide on names and changed them a few times. I guess I overlooked it when posting to SO. You should be able to copy/paste now. – cazman Oct 16 '21 at 13:03
  • @cazman thanks for sharing.Very good approach – Homer Jay Simpson Oct 16 '21 at 14:40
  • I haven't seen the episode, but I feel like the 10% stupidity percentage is much too high since you are checking it **every move**. To make 18 successful moves with a 90% success probability each move would lead to a `.9^18 ~= 15%` survival rate without making any wrong choices. That would lead to an expected value of less than 3 of the 16 players surviving assuming the only mortality is due to stupidity. – Gregor Thomas Nov 03 '21 at 17:46
  • 1
    I also feel compelled to offer a little style feedback - negatives that lead to double negatives become hard to read. I'd suggest replacing `not_stupid` with `is_stupid <- function(p_stupid = 0.01) {runif(1) < p_stupid}` - getting rid of the negative function name and parameterizing the stupid probability (and simplifying the construction). Then, in the main function the double negative `if(!not_stupid())` can be replaced with the simpler `if(is_stupid())` – Gregor Thomas Nov 04 '21 at 12:41
  • @GregorThomas Good catch, I completely agree with the double negatives causing readability issues. I'll change this when I get a chance. – cazman Nov 04 '21 at 15:05
2

○ △ □

##########
# Game ○ △ □ 
##########
squidd7<-function(N_Fields,N_Players,p_new_field){
  Players<-data.frame(id = 1:N_Players, alive=rep(1,N_Players),Field=0)
  
    for(i in 1:N_Players){
      while (Players[i,"alive"]==TRUE && max(Players$Field)< N_Fields) { 
        Players[i,"Field"]=Players[i,"Field"]+1  # Jump onto the next Field
        Players[i,"alive"]=rbinom(1,1,p_new_field)# Fall or repeat
      }
      Players[i+1,"Field"]=Players[i,"Field"] # next player starts where prior player died
    }
  Players<-Players[1:N_Players,] # cosmetic because i do i+1 in the prior line
  # Print me some messages
  if(!any(Players$alive>0)){
      cat("Players loose!")
    } else {
    cat(" \n After", max(Players$Field),"goal was reached! ")
    cat("Players",Players[Players$alive==1,"id"], "survive")
    }
  
  return(Players)
}


squidd7(18,16,0.5)

###########
# simulation ○ △ □
###########
results<-data.frame(matrix(0, nrow = 100, ncol = 20))
for(x in 1:ncol(results)){
     for (i in 1:nrow(results)) {
    Players<-squidd7(x+7,16,0.5)
    results[i,x]<-sum(Players$alive)
  }
}
###########
## Results ○○□□○ △ □
sdt<-apply(results,2,sd) # standart devation 
mn<-apply(results,2,mean) # ○ △ □

boxplot(results,xlab ="n Steps ",names = 8:27,ylab="N Survivors of 16 ")
points(mn,type="l")
points(sdt,type="l")

colors<-colorRampPalette(c(rgb(0,1,0,0.4),
                           rgb(1,1,0,0.4),
                           rgb(1,0,0,0.4)), alpha = TRUE)(21)


plot(density(results$X1),type="n",xlim=c(-1,17),ylim=c(0,0.30),
     main="○ △ □ ",
     sub="○ △ □ ○ △ □ ○ △ □",
     xlab="number of survivors")
for( i in 1:21){
polygon(density(results[,i]),col= colors[i])
}
legend(15,0.31,title="Steps",legend=8:28,fill=colors,border = NA,
       y.intersp = 0.5,
       cex = 0.8, text.font = 0.3)

user12256545
  • 2,755
  • 4
  • 14
  • 28
0

If I'm understanding correctly, I believe the other answers are complicating the simulation.

We can simulate draws from a binomial distribution of size 18. Every 1 kills someone (assuming there is anyone left to kill). Thus we can calculate the number of survivors by subtracting the number of 1s drawn from the number of players, truncated at 0 (any negative results are counted as 0, via pmax()).

set.seed(47)
n_sim = 1e4
survivors = pmax(0, 16 - rbinom(n_sim, size = 18, prob = 0.5))
mean(survivors)
# [1] 7.009

The mean seems to approach Xi'an's answer of 7.000076 as n_sim increases. At 500M simulations (which still runs rather quickly with this method!) we get a mean of 7.000073.

Plotting these results, they appear basically identical to cazman's squid2 scenario.

ggplot(data.frame(survivors), aes(x = survivors)) + geom_bar() +
  scale_y_continuous(limits = c(0, 6000))

enter image description here

Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294
0

well to simulate this game. you will need 50%/50% chance 16 times. meaning all you have to code is 50% chance of not losing and run it 16 times than if you lose you it will do a -1 form a varible of 18. that will create a perfect digital recreation of squidgame bridge

  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 02 '22 at 07:38