Probability in R Exercises: 15 Problems from Basic to Bayesian — Solved Step-by-Step
Probability in R powers everything from A/B testing to Bayesian inference, but it clicks only when you solve problems with code. This exercise set walks you through 15 problems — from basic coin tosses to Bayesian belief updates — each with a starter, a click-to-reveal solution, and a clear explanation.
How do I simulate basic probability events in R?
Estimating a probability with R takes two steps: simulate the random event many times, then count how often the outcome you care about happens. The function sample() generates the random draws, and mean() (applied to a logical vector) gives you the proportion of TRUEs — your estimated probability. Below, we toss a fair coin 1,000 times. The empirical proportion lands close to the true 0.5; this same simulate-and-count pattern powers every problem in this set.
We sampled 1,000 outcomes from {H, T} with replacement, then asked for the proportion that equalled "H". The result, 0.516, is just slightly off the theoretical 0.5 — exactly the kind of sampling wobble you'd expect with 1,000 trials. Push the trial count higher and the estimate tightens. This is the workhorse pattern: mean(condition) over many simulated outcomes estimates any probability.
mean(condition) is your estimated probability.Problem 1: Estimate P(heads) from a fair coin
Try it: Toss a fair coin 5,000 times and estimate the probability of heads. Use set.seed(101) for reproducibility.
Click to reveal solution
Explanation: With 5,000 tosses, the empirical estimate (0.5034) is much closer to the true 0.5 than the 1,000-toss demo. The Law of Large Numbers in action.
Problem 2: Estimate P(rolling a 6) on a fair die
Try it: Roll a fair six-sided die 10,000 times and estimate the probability of rolling a 6. Use set.seed(102).
Click to reveal solution
Explanation: sample(1:6, ...) draws integers 1 through 6 uniformly. The mean of p2_rolls == 6 (a logical vector with TRUE wherever the roll was a 6) gives the empirical probability — 0.1656 against the true 1/6 ≈ 0.1667.
Problem 3: Probability of drawing an Ace from a 52-card deck
Try it: Build a 52-card deck (4 aces and 48 non-aces is enough for this), then simulate drawing one card 10,000 times. Estimate P(Ace). Use set.seed(103).
Click to reveal solution
Explanation: We collapsed the deck to two categories because we only care about Ace vs not. replace = TRUE is appropriate here because each "draw" is independent — we're simulating 10,000 separate single-card draws, not dealing a hand.
Problem 4: Probability of getting at least 5 heads in a row in 100 tosses
Try it: Simulate 5,000 sequences of 100 fair coin tosses. For each, check whether at least one run of 5 or more heads occurred. Estimate the probability. Use set.seed(104) and the helper rle().
Click to reveal solution
Explanation: rle() returns the lengths and values of consecutive runs in a vector. We ask whether any run is 5+ long AND consists of heads. Most people guess this probability is much lower than 81% — long streaks are far more common than intuition suggests.
rle() makes streak detection a one-liner. It returns the lengths of consecutive runs and the value of each run, so testing for any streak of length k becomes any(rle(x)$lengths >= k & rle(x)$values == target).Problem 5: Probability that the sum of two dice is at least 10
Try it: Simulate 20,000 rolls of two fair dice, sum each pair, and estimate P(sum ≥ 10). Use set.seed(105).
Click to reveal solution
Explanation: Out of the 36 possible (d1, d2) pairs, six produce a sum of 10 or more — (4,6), (5,5), (5,6), (6,4), (6,5), (6,6). The exact probability is 6/36 ≈ 0.1667, and our simulation lands at 0.1664 with 20,000 trials.
Which R probability distribution functions should I use?
R ships with built-in functions for every common distribution, organised by a four-letter prefix family. Once you internalise the prefixes, you can switch from "what's the probability of exactly X?" to "what value sits at the 95th percentile?" without lookup. Each prefix answers a specific question, applied to whichever distribution name follows.
| Prefix | What it returns | Example |
|---|---|---|
d |
density / probability mass at a value | dbinom(6, 10, 0.5) |
p |
cumulative probability — P(X ≤ x) | pbinom(4, 20, 0.3) |
q |
quantile — inverse cumulative | qnorm(0.95, 70, 10) |
r |
random samples | rnorm(1000, 0, 1) |
The same four prefixes apply to every distribution: *binom, *norm, *pois, *exp, *beta, *gamma, and so on. The next five problems give you practice with the most common ones.
Problem 6: P(exactly 6 heads in 10 fair coin tosses)
Try it: Use dbinom() to compute the exact probability of getting exactly 6 heads in 10 tosses of a fair coin.
Click to reveal solution
Explanation: dbinom(x, size, prob) returns the probability mass at exactly x successes for a Binomial distribution with size trials and success probability prob. About 20.5% of the time, a fair 10-flip sequence lands on exactly 6 heads.
Problem 7: P(at most 4 successes in 20 trials with p = 0.3)
Try it: Use pbinom() to compute the cumulative probability of at most 4 successes in 20 Bernoulli trials with success probability 0.3.
Click to reveal solution
Explanation: pbinom(q, size, prob) returns P(X ≤ q). With 20 trials and p = 0.3, the expected count is 6, so seeing 4 or fewer is below average — about 24% of the time.
pbinom(k, ...) returns P(X ≤ k), not P(X < k). Off-by-one errors here are the most common probability bug in R. For "strictly less than k", use pbinom(k - 1, ...). For the upper tail, prefer lower.tail = FALSE over 1 - pbinom(...) for numerical accuracy.Problem 8: P(weight > 75 kg) given Normal(mean = 70, sd = 5)
Try it: Use pnorm() with lower.tail = FALSE to compute the probability of an adult weighing more than 75 kg, assuming weights follow Normal(70, 5).
Click to reveal solution
Explanation: 75 kg is exactly one standard deviation above the mean. The familiar 68-95-99.7 rule tells us about 16% of the distribution lies beyond +1 sd — and that's precisely what pnorm() returns.
lower.tail = FALSE for upper-tail probabilities. It's more numerically accurate than 1 - pnorm(x, ...) for extreme values where 1 - p loses precision near 1.Problem 9: 95th percentile of test scores ~ Normal(70, 10)
Try it: Use qnorm() to find the test score that exactly 95% of students score at or below, assuming scores are Normal(70, 10).
Click to reveal solution
Explanation: qnorm() is the inverse of pnorm() — give it a probability, get back the value at that percentile. A score of ~86.4 means 95% of students score 86 or below, and the top 5% score above it.
Problem 10: P(receiving 3 or more emails per hour) given λ = 2
Try it: Customer support receives an average of 2 emails per hour, modelled as Poisson(λ = 2). Use ppois() with lower.tail = FALSE to find the probability of receiving 3 or more in any given hour.
Click to reveal solution
Explanation: ppois(2, 2, lower.tail = FALSE) returns P(X > 2), which equals P(X ≥ 3) for integer-valued distributions. About a third of all hours will see 3+ emails — useful for staffing decisions.
How do I solve conditional probability problems in R?
Conditional probability — P(A given B) — narrows the sample space to the cases where B happened, then asks how often A occurs within that narrower world. In R you can compute it two ways: by counting (filter the simulated outcomes where B is true, then take the mean of A among them), or by applying Bayes' theorem directly.
The formula:
$$P(A \mid B) = \frac{P(B \mid A) \cdot P(A)}{P(B)}$$
Where:
- $P(A \mid B)$ = probability of A given that B happened (what you want)
- $P(B \mid A)$ = probability of B given A (often easier to know)
- $P(A)$ = base rate of A (the prior)
- $P(B)$ = total probability of B across all causes
Plain-language gloss: probability of A given B equals how often B follows A, weighted by how common A is, divided by how common B is overall.
Problem 11: Given a card is red, what is P(it is an Ace)?
Try it: Build a 52-card data frame with colour (red/black) and is_ace columns. Filter to red cards, then compute P(Ace) within that subset. Use set.seed(111) if you simulate; otherwise compute directly.
Click to reveal solution
Explanation: Conditioning on "red" narrows the deck from 52 cards to 26. Of those, 2 are aces (the heart and diamond aces). 2/26 = 1/13 ≈ 0.077. Notice this is the same as the unconditional P(Ace) = 4/52 = 1/13 — colour and ace-ness are independent.
Problem 12: Medical test — P(disease | positive test)?
A disease has a prevalence of 1% in the population. A test is 99% sensitive (correctly flags 99% of true cases) and 95% specific (correctly clears 95% of healthy people). Someone tests positive — what's the probability they actually have the disease?
Try it: Apply Bayes' theorem directly. P(B) needs the law of total probability: P(positive) = P(pos | disease)·P(disease) + P(pos | healthy)·P(healthy).
Click to reveal solution
Explanation: Despite the test being 99% sensitive, only ~16.7% of positive results correspond to true cases. The rare disease (1% prevalence) means most positives are false positives drawn from the much larger healthy population. This is the base-rate fallacy in action.
Practice Exercises
The next three problems are capstones — each combines simulation, distributions, and conditional reasoning into a single workflow. They use a mp_ variable prefix to keep them isolated from the per-problem variables above.
Exercise 13: The birthday problem
In a room of 23 people, what's the probability that at least two share a birthday? Solve it two ways: (a) by simulation with replicate() and duplicated(), and (b) analytically using prod() over the sequence 365, 364, …, 343.
Click to reveal solution
Explanation: Both methods land at ~0.507 — a coin-flip chance of a shared birthday in just 23 people, which surprises almost everyone. The analytical formula computes P(all distinct) by multiplying the available "free" days for each new person, then subtracts from 1.
Exercise 14: Monty Hall — should you switch?
Three doors hide one car and two goats. You pick a door. The host (who knows where the car is) opens a different door revealing a goat, then offers you the chance to switch. Simulate 10,000 games for both "stay" and "switch" strategies. Report the empirical win rates.
Click to reveal solution
Explanation: Switching wins ~2/3 of the time — twice as often as staying. The intuition: your initial pick is right 1/3 of the time, so switching loses 1/3 of the time and wins the other 2/3. The host's reveal doesn't change the original 1/3 odds on your first pick — it just concentrates the remaining 2/3 onto the single unopened door.
Exercise 15: Bayesian update — coin bias from data
You're handed a coin and want to estimate its bias (probability of heads). Start with a uniform prior, Beta(1, 1), reflecting "I have no idea — any bias from 0 to 1 is equally plausible." After observing 7 heads in 10 tosses, compute the posterior, plot it, and report the posterior mean and 95% credible interval. Use the conjugate update: Beta(α + heads, β + tails).
Click to reveal solution
Explanation: The Beta-Binomial conjugate update is a one-liner: add observed heads to prior alpha, observed tails to prior beta. The posterior mean of 0.667 matches the empirical proportion (7/10), but the wide 95% credible interval (0.39, 0.89) honestly reflects how little 10 tosses actually tells us. With more data, the interval would tighten dramatically around the true bias.
Complete Example: Simulate-then-verify workflow on the birthday paradox
The birthday problem is the perfect prototype for the workflow you'll reuse on every probability question: frame it, simulate it, derive the analytical answer, and compare. Below we put all five steps in one place.
The simulation and the closed-form answer agree to within 0.001 — a sanity check that both your code and your math are correct. Now we extend the question: how does the probability scale with room size?
The dashed lines mark the famous tipping point: 23 people is the smallest n where the probability crosses 50%. By 50 people, you're at 97% — almost guaranteed. The same simulate-then-verify-then-explore workflow scales to any probability question you'll encounter.
Summary
The 15 problems above span the full toolkit you need for everyday probability work in R:
- Simulate-and-count —
mean(condition)over manyreplicate()s estimates any probability empirically d/p/q/rdistribution prefix family — density, cumulative, quantile, random samples for*binom,*norm,*pois,*exp,*betapbinom(k, ...)returns P(X ≤ k) — uselower.tail = FALSEfor upper tails and to avoid off-by-one bugs- Conditional probability narrows the sample space — filter to where the condition holds, then compute the inner probability
- Bayes' theorem flips the direction — prior × likelihood / evidence converts P(B|A) into P(A|B)
- Beta-Binomial conjugate update — Bayesian inference in a single line: posterior = Beta(α + heads, β + tails)
- Always verify simulation against analytical — when an exact formula exists, compute both and check they agree
| Problem type | Function | Typical pattern |
|---|---|---|
| Empirical probability | sample(), replicate(), mean() |
mean(replicate(N, condition)) |
| Exact discrete probability | dbinom, dpois |
dbinom(k, n, p) |
| Cumulative probability | pbinom, pnorm, ppois |
pbinom(k, n, p, lower.tail = FALSE) |
| Percentile / quantile | qnorm, qbeta |
qnorm(0.95, mean, sd) |
| Bayesian posterior (Beta-Binomial) | dbeta, qbeta |
qbeta(c(.025, .975), α + h, β + t) |
References
- R Core Team — An Introduction to R. CRAN documentation. Link
- R
statspackage — distribution function reference (?Distributions). Link - Jim Albert & Jingchen Hu — Probability and Bayesian Modeling. CRC Press. Link
- Alicia A. Johnson, Miles Q. Ott, Mine Dogucu — Bayes Rules! An Introduction to Applied Bayesian Modeling. CRC Press. Link
- Paul Teetor — R Cookbook (2nd ed.), Chapter 8: Probability. O'Reilly. Link
- Wikipedia — Birthday problem. Link
- Wikipedia — Monty Hall problem. Link
Continue Learning
- Probability Simulation in R — a deeper tutorial on the simulate-and-count workflow, including
replicate(), Monte Carlo estimation, and convergence diagnostics. - Conditional Probability in R — extends Problems 11 and 12 with more conditional setups, the law of total probability, and event independence.
- Binomial and Poisson Distributions in R — a focused walk-through of the discrete distributions used in Problems 6, 7, and 10, with all four
d/p/q/rfunctions explored in depth.