# /// script # requires-python = ">=3.10" # dependencies = [ # "marimo", # "matplotlib", # "matplotlib-venn" # ] # /// import marimo __generated_with = "0.11.4" app = marimo.App(width="medium") @app.cell def _(): import marimo as mo return (mo,) @app.cell def _(): import matplotlib.pyplot as plt from matplotlib_venn import venn2 return plt, venn2 @app.cell(hide_code=True) def _(mo): mo.md( r""" # Probability of And _This notebook is a computational companion to the book ["Probability for Computer Scientists"](https://chrispiech.github.io/probabilityForComputerScientists/en/part1/prob_and/), by Stanford professor Chris Piech._ When calculating the probability of both events occurring together, we need to consider whether the events are independent or dependent. Let's explore how to calculate $P(E \cap F)$, i.e. $P(E \text{ and } F)$, in different scenarios. """ ) return @app.cell(hide_code=True) def _(mo): mo.md( r""" ## And with Independent Events Two events $E$ and $F$ are **independent** if knowing one event occurred doesn't affect the probability of the other. For independent events: $P(E \text{ and } F) = P(E) \cdot P(F)$ For example: - Rolling a 6 on one die and getting heads on a coin flip - Drawing a heart from a deck, replacing it, and drawing another heart - Getting a computer error on Monday vs. Tuesday Here's a Python function to calculate probability for independent events: """ ) return @app.cell def _(): def calc_independent_prob(p_e, p_f): return p_e * p_f # Example 1: Rolling a die and flipping a coin p_six = 1/6 # P(rolling a 6) p_heads = 1/2 # P(getting heads) p_both = calc_independent_prob(p_six, p_heads) print(f"Example 1: P(rolling 6 AND getting heads) = {p_six:.3f} × {p_heads:.3f} = {p_both:.3f}") return calc_independent_prob, p_both, p_heads, p_six @app.cell def _(calc_independent_prob): # Example 2: Two independent system components failing p_cpu_fail = 0.05 # P(CPU failure) p_disk_fail = 0.03 # P(disk failure) p_both_fail = calc_independent_prob(p_cpu_fail, p_disk_fail) print(f"Example 2: P(both CPU and disk failing) = {p_cpu_fail:.3f} × {p_disk_fail:.3f} = {p_both_fail:.3f}") return p_both_fail, p_cpu_fail, p_disk_fail @app.cell(hide_code=True) def _(mo): mo.md( r""" ## And with Dependent Events For dependent events, we use the **chain rule**: $P(E \text{ and } F) = P(E) \cdot P(F|E)$ where $P(F|E)$ is the probability of $F$ occurring given that $E$ has occurred. For example: - Drawing two hearts without replacement - Getting two consecutive heads in poker - System failures in connected components Let's implement this calculation: """ ) return @app.cell def _(): def calc_dependent_prob(p_e, p_f_given_e): return p_e * p_f_given_e # Example 1: Drawing two hearts without replacement p_first_heart = 13/52 # P(first heart) p_second_heart = 12/51 # P(second heart | first heart) p_both_hearts = calc_dependent_prob(p_first_heart, p_second_heart) print(f"Example 1: P(two hearts) = {p_first_heart:.3f} × {p_second_heart:.3f} = {p_both_hearts:.3f}") return calc_dependent_prob, p_both_hearts, p_first_heart, p_second_heart @app.cell def _(calc_dependent_prob): # Example 2: Drawing two aces without replacement p_first_ace = 4/52 # P(first ace) p_second_ace = 3/51 # P(second ace | first ace) p_both_aces = calc_dependent_prob(p_first_ace, p_second_ace) print(f"Example 2: P(two aces) = {p_first_ace:.3f} × {p_second_ace:.3f} = {p_both_aces:.3f}") return p_both_aces, p_first_ace, p_second_ace @app.cell(hide_code=True) def _(mo): mo.md( r""" ## Multiple Events For multiple independent events: $P(E_1 \text{ and } E_2 \text{ and } \cdots \text{ and } E_n) = \prod_{i=1}^n P(E_i)$ For dependent events: $P(E_1 \text{ and } E_2 \text{ and } \cdots \text{ and } E_n) = P(E_1) \cdot P(E_2|E_1) \cdot P(E_3|E_1,E_2) \cdots P(E_n|E_1,\ldots,E_{n-1})$ Let's visualize these probabilities: """ ) return @app.cell(hide_code=True) def _(mo): mo.md(r"""### Interactive example""") return @app.cell def _(event_type): event_type return @app.cell(hide_code=True) def _(mo): event_type = mo.ui.dropdown( options=[ "Independent AND (Die and Coin)", "Dependent AND (Sequential Cards)", "Multiple AND (System Components)" ], value="Independent AND (Die and Coin)", label="Select AND Probability Scenario" ) return (event_type,) @app.cell(hide_code=True) def _(event_type, mo, plt, venn2): # Define the events and their probabilities events_data = { "Independent AND (Die and Coin)": { "sets": (0.33, 0.17, 0.08), # (die, coin, intersection) "labels": ("Die\nP(6)=1/6", "Coin\nP(H)=1/2"), "title": "Independent Events: Rolling a 6 AND Getting Heads", "explanation": r""" ### Independent Events: Die Roll and Coin Flip $P(\text{Rolling 6}) = \frac{1}{6} \approx 0.17$ $P(\text{Getting Heads}) = \frac{1}{2} = 0.5$ $P(\text{6 and Heads}) = \frac{1}{6} \times \frac{1}{2} = \frac{1}{12} \approx 0.08$ These events are independent because the outcome of the die roll doesn't affect the coin flip, and vice versa. """, }, "Dependent AND (Sequential Cards)": { "sets": ( 0.25, 0.24, 0.06, ), # (first heart, second heart, intersection) "labels": ("First\nP(H₁)=13/52", "Second\nP(H₂|H₁)=12/51"), "title": "Dependent Events: Drawing Two Hearts", "explanation": r""" ### Dependent Events: Drawing Hearts $P(\text{First Heart}) = \frac{13}{52} = 0.25$ $P(\text{Second Heart}|\text{First Heart}) = \frac{12}{51} \approx 0.24$ $P(\text{Both Hearts}) = \frac{13}{52} \times \frac{12}{51} \approx 0.06$ These events are dependent because drawing the first heart changes the probability of drawing the second heart. """, }, "Multiple AND (System Components)": { "sets": (0.05, 0.03, 0.0015), # (CPU fail, disk fail, intersection) "labels": ("CPU\nP(C)=0.05", "Disk\nP(D)=0.03"), "title": "Independent System Failures", "explanation": r""" ### System Component Failures $P(\text{CPU Failure}) = 0.05$ $P(\text{Disk Failure}) = 0.03$ $P(\text{Both Fail}) = 0.05 \times 0.03 = 0.0015$ Component failures are typically independent in **well-designed systems**, meaning one component's failure doesn't affect the other's probability of failing. """, }, } # Get data for selected event type data = events_data[event_type.value] # Create visualization plt.figure(figsize=(10, 5)) v = venn2(subsets=data["sets"], set_labels=data["labels"]) plt.title(data["title"]) # Display explanation alongside visualization mo.hstack([plt.gcf(), mo.md(data["explanation"])]) return data, events_data, v @app.cell(hide_code=True) def _(mo): mo.md( r""" ## 🤔 Test Your Understanding Which of these statements about AND probability are true?
1. The probability of getting two sixes in a row with a fair die is 1/36 ✅ True! Since die rolls are independent events: P(two sixes) = P(first six) × P(second six) = 1/6 × 1/6 = 1/36
2. When drawing cards without replacement, P(two kings) = 4/52 × 4/52 ❌ False! This is a dependent event. The correct calculation is: P(two kings) = P(first king) × P(second king | first king) = 4/52 × 3/51
3. If P(A) = 0.3 and P(B) = 0.4, then P(A and B) must be 0.12 ❌ False! P(A and B) = 0.12 only if A and B are independent events. If they're dependent, we need P(B|A) to calculate P(A and B).
4. The probability of rolling a six AND getting tails is (1/6 × 1/2) ✅ True! These are independent events, so we multiply their individual probabilities: P(six and tails) = P(six) × P(tails) = 1/6 × 1/2 = 1/12
""" ) return @app.cell(hide_code=True) def _(mo): mo.md( """ ## Summary You've learned: - How to identify independent vs dependent events - The multiplication rule for independent events - The chain rule for dependent events - How to extend these concepts to multiple events In the next lesson, we'll explore **law of total probability** in more detail, building on our understanding of various topics. """ ) return if __name__ == "__main__": app.run()