Bab sebelumnya memakai np.random.default_rng() sebagai pintu masuk API modern tanpa menjelaskan apa yang sebenarnya dikembalikan fungsi tersebut. Bab ini membedahnya: sebuah objek Generator yang menyimpan keadaan sendiri dan ditopang BitGenerator PCG64. Memahami struktur ini membuat rng berhenti menjadi kotak hitam: kelas apa yang kita pegang, keadaan apa yang berubah setiap kali angka ditarik, dan bagaimana objek ini seharusnya diteruskan ke fungsi-fungsi program.
Membuat Generator dengan default_rng #
np.random.default_rng() dapat dipanggil tanpa argumen maupun dengan seed. Tanpa argumen, NumPy mengambil titik awal dari sumber entropi sistem operasi sehingga setiap eksekusi program menghasilkan urutan yang berbeda. Dengan seed, titik awal dikunci dan urutannya dapat direproduksi. Angka 42 pada contoh bukan angka khusus — bilangan bulat non-negatif apa pun sah dipakai.
import numpy as np
rng = np.random.default_rng(42)
print(type(rng))
print(rng)
print(type(rng.bit_generator))Output yang Dihasilkan:
<class 'numpy.random._generator.Generator'>
Generator(PCG64)
<class 'numpy.random._pcg64.PCG64'>Bedah baris demi baris:
- Baris 3 (
rng = np.random.default_rng(42)):default_rngmembuat objekGeneratorber-seed42. - Baris 4 (
print(type(rng))): kelasnya adalahGenerator, yang tinggal di submodul internalnumpy.random._generator. Awalan garis bawah pada nama modul menandai modul internal; kita tidak perlu mengimpornya langsung, cukup memakai objek hasildefault_rng(). - Baris 5 (
print(rng)): mencetak objeknya menampilkanGenerator(PCG64)— repr yang memuat nama mesin pembangkit di baliknya. - Baris 6 (
print(type(rng.bit_generator))): atributbit_generatormengekspos mesin tersebut: kelasPCG64dari modulnumpy.random._pcg64.
Keadaan yang Berubah Setiap Tarikan #
Generator adalah objek stateful: ia membawa keadaan internal yang berubah setiap kali angka ditarik. Keadaan BitGenerator dapat dilihat melalui atribut bit_generator.state, sebuah dict yang isinya bisa kita periksa sebelum dan sesudah tarikan:
import numpy as np
rng = np.random.default_rng(42)
print(rng.bit_generator.state)
print(rng.integers(0, 10, size=3))
print(rng.bit_generator.state)Output yang Dihasilkan:
{'bit_generator': 'PCG64', 'state': {'state': 274674114334540486603088602300644985544, 'inc': 332724090758049132448979897138935081983}, 'has_uint32': 0, 'uinteger': 0}
[0 7 6]
{'bit_generator': 'PCG64', 'state': {'state': 180456145198944327624639367796045148994, 'inc': 332724090758049132448979897138935081983}, 'has_uint32': 1, 'uinteger': 1884968545}Bedah baris demi baris:
- Baris 4: dict keadaan memuat nama mesin (
'bit_generator': 'PCG64'), pasangan bilangan besar'state'dan'inc'yang menjadi inti algoritme PCG64, serta sisa cache bilangan 32-bit pada'has_uint32'dan'uinteger'. - Baris 5: tarikan tiga angka memakai sekaligus memperbarui keadaan generator.
- Baris 7: nilai
'state'berubah dari27467...menjadi18045..., sedangkan'inc'tetap; cache 32-bit kini berisi sisa nilai1884968545yang menunggu tarikan berikutnya. Inilah bukti nyata sifat stateful: posisi generator di dalam urutannya telah bergeser.
| Kunci state | Arti |
|---|---|
'bit_generator' | nama mesin pembangkit yang menopang Generator |
'state' | pasangan bilangan inti algoritme PCG64 ('state' dan 'inc') |
'has_uint32', 'uinteger' | sisa cache nilai 32-bit yang belum habis dipakai |
Keadaan inilah yang membuat dua generator terpisah dengan seed sama dapat menghasilkan urutan identik, sekaligus membuat dua generator yang tak berkaitan tidak saling memengaruhi — dua sifat yang dibuktikan pada Chapter Reproducibility dan Random Seed.
Satu Generator per Bagian Program #
Praktik yang disarankan: buat satu Generator di awal bagian program — misalnya di atas sebuah modul atau di dalam fungsi utama — lalu teruskan sebagai parameter ke fungsi yang membutuhkan angka acak. Dengan cara ini aliran keacakan mudah dilacak, dan fungsi tetap mudah diuji karena kita bisa memberinya generator ber-seed khusus untuk pengujian.
import numpy as np
rng = np.random.default_rng(2024)
def undi_nomor_undian(rng, n):
return rng.integers(1, 1000, size=n)
print(undi_nomor_undian(rng, 4))
print(undi_nomor_undian(rng, 4))Output yang Dihasilkan:
[242 676 93 215]
[318 310 908 799]Bedah baris demi baris:
- Baris 3: satu generator dibuat sekali di bagian atas program.
- Baris 5-6 (
def undi_nomor_undian(...)): fungsi menerimarngsebagai parameter biasa; fungsi tidak membuat generator sendiri dan tidak menyentuh keadaan global apa pun. - Baris 8-9: dua pemanggilan menghasilkan deret berbeda karena keduanya melanjutkan urutan generator yang sama.
Dengan memahami apa itu Generator, seluruh method pada section ini — integers, random, uniform, choice, dan seterusnya — dapat dibaca sebagai method milik satu objek stateful yang kita kendalikan penuh.