Probability space, conditional probability, dan Bayes' theorem P(A|B) = P(B|A)·P(A) / P(B) adalah fondasi dari randomized algorithm, A/B testing, spam filter, dan classification probabilistic — dari konsep dasar hingga implementasi Naive Bayes di Python.

Setelah di episode 12 kita mempelajari Pigeonhole Principle dan Inclusion-Exclusion — prinsip counting lanjutan — pada episode ini kita mempelajari probability: cabang matematika yang mengukur kemungkinan suatu kejadian. Probability adalah fondasi dari A/B testing, spam filter, randomized algorithm, dan machine learning probabilistic.
Mengapa probability penting untuk programmer? Karena dunia nyata penuh ketidakpastian: user bisa mengklik atau tidak, server bisa down atau tidak, model bisa prediksi benar atau salah. Probability memberikan kerangka kerja formal untuk mengukur ketidakpastian dan mengambil keputusan optimal di bawah ketidakpastian. Bayes' theorem — yang akan kita pelajari — adalah alat paling powerful untuk memperbarui keyakinan dengan bukti baru.
Probability space terdiri dari tiga komponen:
import random
# Contoh: lempar dadu 6 sisi
sample_space = {1, 2, 3, 4, 5, 6}
# Event: angka genap
event_genap = {2, 4, 6}
# Probability P(genap) = |event_genap| / |sample_space|
p_genap = len(event_genap) / len(sample_space)
print(f"P(genap) = {len(event_genap)}/{len(sample_space)} = {p_genap}")
# Event: angka > 4
event_besar = {5, 6}
p_besar = len(event_besar) / len(sample_space)
print(f"P(>4) = {len(event_besar)}/{len(sample_space)} = {p_besar}")Tiga aturan dasar yang harus dipenuhi:
import random
# 1. P(A) ≥ 0 — probability tidak pernah negatif
assert 0 <= p_genap <= 1
# 2. P(S) = 1 — probability semua outcome = 1
p_all = len(sample_space) / len(sample_space)
assert p_all == 1.0
# 3. P(A ∪ B) = P(A) + P(B) − P(A ∩ B) — Inclusion-Exclusion!
event_ganjil = {1, 3, 5}
p_union = len(event_genap | event_ganjil) / len(sample_space)
p_separate = len(event_genap) / len(sample_space) + len(event_ganjil) / len(sample_space)
print(f"P(genap ∪ ganjil) = {p_union}")
print(f"P(genap) + P(ganjil) = {p_separate}")P(A|B) adalah probability A diberikan B sudah terjadi:
P(A|B) = P(A ∩ B) / P(B)
Dalam dadu: P(genap | >3) = P(genap ∩ >3) / P(>3) = P(6) / P(6) = 2/3.
# Dadu
sample_space = {1, 2, 3, 4, 5, 6}
# P(genap | >3)
A = {2, 4, 6} # genap
B = {4, 5, 6} # >3
p_a_given_b = len(A & B) / len(B)
print(f"P(genap | >3) = {len(A & B)}/{len(B)} = {p_a_given_b:.4f}")
# 2/3 = 0.6667Dua event A dan B independen jika P(A ∩ B) = P(A) × P(B):
# Lempang dadu dua kali — event independen
# P(6 pada dadu pertama) = 1/6
# P(6 pada dadu kedua) = 1/6
# P(keduanya 6) = 1/6 × 1/6 = 1/36
p_dadu1_6 = 1/6
p_dadu2_6 = 1/6
p_both_6 = p_dadu1_6 * p_dadu2_6
# Verifikasi Monte Carlo
trials = 1_000_000
count = sum(
1 for _ in range(trials)
if random.randint(1, 6) == 6 and random.randint(1, 6) == 6
)
print(f"P(6,6) teori: {p_both_6:.6f}")
print(f"P(6,6) simulasi: {count/trials:.6f}")P(A|B) = P(B|A) × P(A) / P(B)
Bayes' theorem memungkinkan kita membalik conditional probability: dari P(B|A) ke P(A|B).
Jika tes penyakit akurat 99% (P(positif|sakit) = 0.99), dan penyakit langka (P(sakit) = 0.001), maka P(sakit|positif) jauh lebih kecil dari yang kita kira:
# P(sakit) = 0.1% — penyakit langka
# P(positif|sakit) = 99% — tes akurat
# P(positif|sehat) = 1% — false positive rate
p_sakit = 0.001
p_positif_given_sakit = 0.99
p_positif_given_sehat = 0.01
# Total probability P(positif)
p_positif = (p_positif_given_sakit * p_sakit +
p_positif_given_sehat * (1 - p_sakit))
# P(sakit|positif) — Bayes
p_sakit_given_positif = (p_positif_given_sakit * p_sakit) / p_positif
print(f"P(sakit) = {p_sakit:.3%}")
print(f"P(positif|sakit) = {p_positif_given_sakit:.1%}")
print(f"P(positif|sehat) = {p_positif_given_sehat:.1%}")
print(f"P(sakit|positif) = {p_sakit_given_positif:.2%}")
print(f"\nHanya {p_sakit_given_positif:.2%} dari yang positif benar-benar sakit!")# Dataset dummy: email dengan kata-kata dan label
emails = [
{"words": ["win", "free", "money", "click"], "spam": True},
{"words": ["win", "prize", "claim", "free"], "spam": True},
{"words": ["meeting", "tomorrow", "project"], "spam": False},
{"words": ["lunch", "team", "today"], "spam": False},
{"words": ["free", "offer", "limited", "win"], "spam": True},
{"words": ["report", "deadline", "review"], "spam": False},
]
# Hitung P(spam) dan P(word|spam)
total = len(emails)
p_spam = sum(1 for e in emails if e["spam"]) / total
p_not_spam = 1 - p_spam
# Hitung kemunculan kata per kelas
spam_words = {}
not_spam_words = {}
for email in emails:
target = spam_words if email["spam"] else not_spam_words
for word in email["words"]:
target[word] = target.get(word, 0) + 1
def classify_spam(words):
"""Classify email menggunakan Naive Bayes."""
log_p_spam = 0
log_p_not_spam = 0
for word in words:
# Laplace smoothing: tambah 1 untuk kata yang belum terlihat
p_word_given_spam = (spam_words.get(word, 0) + 1) / (sum(spam_words.values()) + len(set(sum((e["words"] for e in emails), []))))
p_word_given_not_spam = (not_spam_words.get(word, 0) + 1) / (sum(not_spam_words.values()) + len(set(sum((e["words"] for e in emails), []))))
import math
log_p_spam += math.log(p_word_given_spam)
log_p_not_spam += math.log(p_word_given_not_spam)
log_p_spam += math.log(p_spam)
log_p_not_spam += math.log(p_not_spam)
return "SPAM" if log_p_spam > log_p_not_spam else "NOT SPAM"
# Test
test_emails = [
["win", "free", "money"],
["meeting", "project", "deadline"],
]
for words in test_emails:
result = classify_spam(words)
print(f" {words} → {result}")Tip
Bayes' theorem mengajarkan bahwa posterior probability bergantung pada prior (P(A)) dan likelihood (P(B|A)). Ketika prior sangat kecil (penyakit langka), bahkan tes akurat pun menghasilkan banyak false positive. Selalu pertimbangkan base rate!
Inti yang harus dibawa pulang:
Di episode 14 selanjutnya kita akan mempelajari graph theory: terminologi dan representasi — node, edge, directed/undirected, weighted, adjacency matrix vs adjacency list. Probability yang baru kalian pelajari akan muncul kembali dalam probabilistic graphs dan random walks!