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:
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:
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: -2Mari kita bedah:
- Baris 4 (
rng.choice([-1, 1], size=20)): generator mengambil dua puluh nilai acak dari pilihan-1(mundur) dan1(maju), masing-masing berpeluang setengah. - Baris 5 (
np.cumsum(langkah)): cumulative sum mengubah perpindahan menjadi posisi — elemen ke-i adalah total langkah sampai waktu ke-i. - Baris 6-8: jalur ini sempat merosot sampai
-2sebelum merangkak naik ke5pada langkah ke-17, lalu kembali ke4;maxdanminmeringkas 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?
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:
P menyentuh +6 : 0.414
P berakhir > 0 : 0.456Bedah baris demi baris:
- Baris 4 (
size=(1000, 50)): satu pemanggilan menghasilkan seribu jalur sekaligus; baris adalah jalur, kolom adalah waktu. - Baris 5 (
np.cumsum(..., axis=1)):axis=1menjumlahkan sepanjang waktu, sehingga setiap baris menjadi posisi satu jalur — pemahamanaxisdari Chapter Operasi Berdasarkan Axis menjadi krusial di sini. - Baris 6 (
posisi.max(axis=1) >= 6): nilai tertinggi tiap jalur dibandingkan dengan ambang, menghasilkan array boolean 1000 elemen;np.meanatas boolean menghitung proporsiTrue, yaitu peluang yang dicari. - 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:
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:
P(total >= 10) simulasi : 0.1679
P(total >= 10) teori : 0.1667
ekspektasi simulasi : -1,641.2 rupiah
ekspektasi teori : -1,666.7 rupiah- Baris 4-5:
rng.integers(1, 7, size=(50_000, 2))melempar dua dadu untuk 50.000 permainan sekaligus, dansum(axis=1)menjumlahkan keduanya per baris. - 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. - Baris 8-9: peluang simulasi 0.1679 mendekati nilai teoretis 0.1667 — selisihnya wajar untuk 50.000 percobaan.
- 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 simulasi | Perangkat NumPy | Hasil pada latihan kita |
|---|---|---|
| Jalur ini berakhir di mana? | rng.choice + np.cumsum | deret posisi 20 langkah |
| Seberapa sering ambang tersentuh? | cumsum(axis=1) + np.mean boolean | 0.414 |
| Layakkah permainan dimainkan? | integers + np.where + np.mean | -1.666,7 rupiah per permainan |
Pengalaman ketiganya juga merangkum kebiasaan yang layak diikuti:
- Selalu beri seed pada generator (
default_rng(42), misalnya) supaya hasil bisa direproduksi dan diperiksa ulang; bab dengan output teruji pun menuntutnya. - Vectorize, jangan loop. Seribu jalur atau 50.000 permainan dibangun dalam satu pemanggilan fungsi; loop Python untuk jumlah yang sama jauh lebih lambat.
- 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.
- Tambah jumlah percobaan bertahap. Semakin banyak jalur, estimasi makin stabil — mulai dari ratusan untuk menguji logika, naik ke puluhan ribu hanya setelah skripnya benar.