Simulasi menjawab pertanyaan yang tidak bisa diselesaikan satu kali perhitungan: berapa peluang suku bunga menyentuh ambang tertentu, atau berapa rata-rata keuntungan sebuah permainan. Pendekatan Monte Carlo menjawabnya dengan mengulang skenario acak ribuan kali lalu merangkum hasilnya — dan justru karena pengulangan inilah NumPy unggul: menghasilkan ribuan skenario sekaligus lewat vectorization jauh lebih cepat daripada loop Python.

Kita akan menyusun tiga latihan yang bergerak dari yang kecil ke besar: satu jalur random walk, seribu jalur untuk mengestimasi peluang, lalu permainan dadu untuk menghitung ekspektasi. Semuanya menggunakan generator default_rng ber-seed agar hasil dapat direproduksi (dibahas pada Chapter default_rng dan Generator).

Random Walk Satu Dimensi #

Random walk memodelkan posisi yang berpindah acak setiap langkah — misalnya pemain maju atau mundur satu kotak, atau harga komoditas naik-turun satu poin. Perpindahan dibuat dengan rng.choice, lalu np.cumsum menjumlahkannya menjadi posisi tiap waktu:

python
import numpy as np

rng = np.random.default_rng(42)
langkah = rng.choice([-1, 1], size=20)
posisi = np.cumsum(langkah)
print("langkah:", langkah)
print("posisi :", posisi)
print("terjauh:", posisi.max(), "terendah:", posisi.min())

Output yang Dihasilkan:

text
langkah: [-1  1  1 -1 -1  1 -1  1 -1 -1  1  1  1  1  1  1  1 -1  1 -1]
posisi : [-1  0  1  0 -1  0 -1  0 -1 -2 -1  0  1  2  3  4  5  4  5  4]
terjauh: 5 terendah: -2

Mari kita bedah:

  1. Baris 4 (rng.choice([-1, 1], size=20)): generator mengambil dua puluh nilai acak dari pilihan -1 (mundur) dan 1 (maju), masing-masing berpeluang setengah.
  2. Baris 5 (np.cumsum(langkah)): cumulative sum mengubah perpindahan menjadi posisi — elemen ke-i adalah total langkah sampai waktu ke-i.
  3. Baris 6-8: jalur ini sempat merosot sampai -2 sebelum merangkak naik ke 5 pada langkah ke-17, lalu kembali ke 4; max dan min meringkas rentang perjalanan tanpa perlu memeriksa satu per satu.

Mengestimasi Peluang dengan Ribuan Jalur #

Satu jalur tidak cukup untuk menilai peluang. Kita naik kelas: seribu jalur masing-masing 50 langkah, ditata sebagai array dua dimensi (1000, 50). Pertanyaan kita: seberapa sering jalur menyentuh ambang +6 kapan pun sepanjang perjalanannya?

python
import numpy as np

rng = np.random.default_rng(7)
langkah = rng.choice([-1, 1], size=(1000, 50))
posisi = np.cumsum(langkah, axis=1)
p_sentuh = np.mean(posisi.max(axis=1) >= 6)
p_akhir = np.mean(posisi[:, -1] > 0)
print(f"P menyentuh +6 : {p_sentuh:.3f}")
print(f"P berakhir > 0 : {p_akhir:.3f}")

Output yang Dihasilkan:

text
P menyentuh +6 : 0.414
P berakhir > 0 : 0.456

Bedah baris demi baris:

  1. Baris 4 (size=(1000, 50)): satu pemanggilan menghasilkan seribu jalur sekaligus; baris adalah jalur, kolom adalah waktu.
  2. Baris 5 (np.cumsum(..., axis=1)): axis=1 menjumlahkan sepanjang waktu, sehingga setiap baris menjadi posisi satu jalur — pemahaman axis dari Chapter Operasi Berdasarkan Axis menjadi krusial di sini.
  3. Baris 6 (posisi.max(axis=1) >= 6): nilai tertinggi tiap jalur dibandingkan dengan ambang, menghasilkan array boolean 1000 elemen; np.mean atas boolean menghitung proporsi True, yaitu peluang yang dicari.
  4. Baris 7: sebagai pembanding, hanya 45,6% jalur berakhir positif — lebih kecil dari peluang menyentuh +6, karena banyak jalur sempat naik tinggi lalu turun kembali sebelum langkah habis.

Menghitung Ekspektasi Permainan Dadu #

Simulasi juga menguji apakah sebuah permainan menguntungkan. Aturan main: lempar dua dadu, pemain menang 20.000 rupiah bila jumlahnya 10 atau lebih, dan membayar 5.000 rupiah tiap kali bermain. Ekspektasi teoretisnya bisa dihitung (peluang menang 6/36), tetapi kita buktikan lewat 50.000 permainan:

python
import numpy as np

rng = np.random.default_rng(42)
dadu = rng.integers(1, 7, size=(50_000, 2))
total = dadu.sum(axis=1)
menang = total >= 10
untung = np.where(menang, 20000, 0) - 5000
print(f"P(total >= 10) simulasi : {menang.mean():.4f}")
print(f"P(total >= 10) teori    : {6 / 36:.4f}")
print(f"ekspektasi simulasi     : {untung.mean():+,.1f} rupiah")
print(f"ekspektasi teori        : {6 / 36 * 20000 - 5000:+,.1f} rupiah")

Output yang Dihasilkan:

text
P(total >= 10) simulasi : 0.1679
P(total >= 10) teori    : 0.1667
ekspektasi simulasi     : -1,641.2 rupiah
ekspektasi teori        : -1,666.7 rupiah
  1. Baris 4-5: rng.integers(1, 7, size=(50_000, 2)) melempar dua dadu untuk 50.000 permainan sekaligus, dan sum(axis=1) menjumlahkan keduanya per baris.
  2. Baris 7 (np.where(menang, 20000, 0)): hadiah 20.000 diberikan pada permainan yang menang dan 0 untuk sisanya, lalu biaya bermain 5.000 dikurangkan dari semua baris.
  3. Baris 8-9: peluang simulasi 0.1679 mendekati nilai teoretis 0.1667 — selisihnya wajar untuk 50.000 percobaan.
  4. Baris 10-11: ekspektasi keduanya negatif sekitar -1.600 rupiah per permainan. Simulasi mengonfirmasi hitungan teori: jangka panjangnya pemain kalah rata-rata itu, dan peluang menang 1/6 tidak cukup menutup biaya.

Praktik Baik Simulasi NumPy #

Tiga latihan tadi dapat dirangkum sebagai peta pertanyaan → perangkat:

Pertanyaan simulasiPerangkat NumPyHasil pada latihan kita
Jalur ini berakhir di mana?rng.choice + np.cumsumderet posisi 20 langkah
Seberapa sering ambang tersentuh?cumsum(axis=1) + np.mean boolean0.414
Layakkah permainan dimainkan?integers + np.where + np.mean-1.666,7 rupiah per permainan

Pengalaman ketiganya juga merangkum kebiasaan yang layak diikuti:

  1. Selalu beri seed pada generator (default_rng(42), misalnya) supaya hasil bisa direproduksi dan diperiksa ulang; bab dengan output teruji pun menuntutnya.
  2. Vectorize, jangan loop. Seribu jalur atau 50.000 permainan dibangun dalam satu pemanggilan fungsi; loop Python untuk jumlah yang sama jauh lebih lambat.
  3. Bandingkan dengan nilai teoretis bila ada. Angka simulasi 0.1679 jadi meyakinkan justru karena ditempel pada teori 0.1667; tanpa pembanding, kesalahan model simulasi sulit terlihat.
  4. Tambah jumlah percobaan bertahap. Semakin banyak jalur, estimasi makin stabil — mulai dari ratusan untuk menguji logika, naik ke puluhan ribu hanya setelah skripnya benar.