Semua jawaban sejauh ini salah secara matematis. Kembali rand() % N
tidak secara seragam memberikan angka dalam kisaran [0, N)
kecuali N
membagi panjang interval ke mana rand()
kembali (yaitu pangkat 2). Selain itu, orang tidak tahu apakah modulusnya rand()
independen: mungkin saja mereka pergi 0, 1, 2, ...
, yang seragam tetapi tidak terlalu acak. Satu-satunya asumsi yang tampaknya masuk akal untuk dibuat adalah yang rand()
mengeluarkan distribusi Poisson: dua subinterval yang tidak tumpang tindih dengan ukuran yang sama kemungkinannya sama dan independen. Untuk sekumpulan nilai yang terbatas, ini menyiratkan distribusi seragam dan juga memastikan bahwa nilairand()
tersebar dengan baik.
Ini berarti bahwa satu-satunya cara yang benar untuk mengubah kisaran rand()
adalah dengan membaginya ke dalam kotak; misalnya, jika RAND_MAX == 11
dan Anda menginginkan rentang 1..6
, Anda harus menetapkan {0,1}
ke 1,{2,3}
ke 2, dan seterusnya. Ini adalah interval yang terputus-putus, berukuran sama dan dengan demikian didistribusikan secara seragam dan independen.
Saran untuk menggunakan pembagian floating-point secara matematis masuk akal tetapi pada prinsipnya menderita masalah pembulatan. Mungkindouble
presisi cukup tinggi untuk membuatnya bekerja; mungkin tidak. Saya tidak tahu dan saya tidak ingin memikirkannya; bagaimanapun, jawabannya tergantung pada sistem.
Cara yang benar adalah dengan menggunakan aritmatika integer. Artinya, Anda menginginkan sesuatu seperti berikut:
#include <stdlib.h> // For random(), RAND_MAX
// Assumes 0 <= max <= RAND_MAX
// Returns in the closed interval [0, max]
long random_at_most(long max) {
unsigned long
// max <= RAND_MAX < ULONG_MAX, so this is okay.
num_bins = (unsigned long) max + 1,
num_rand = (unsigned long) RAND_MAX + 1,
bin_size = num_rand / num_bins,
defect = num_rand % num_bins;
long x;
do {
x = random();
}
// This is carefully written not to overflow
while (num_rand - defect <= (unsigned long)x);
// Truncated division is intentional
return x/bin_size;
}
Loop diperlukan untuk mendapatkan distribusi seragam yang sempurna. Misalnya, jika Anda diberi nomor acak dari 0 sampai 2 dan Anda hanya menginginkan satu dari 0 sampai 1, Anda terus menarik sampai tidak mendapatkan 2; tidak sulit untuk memastikan bahwa ini memberikan 0 atau 1 dengan probabilitas yang sama. Metode ini juga dijelaskan dalam tautan yang diberikan no pada jawaban mereka, meskipun kode berbeda. Saya menggunakan random()
daripada rand()
karena memiliki distribusi yang lebih baik (seperti dicatat oleh halaman manual untuk rand()
).
Jika Anda ingin mendapatkan nilai acak di luar rentang default [0, RAND_MAX]
, Anda harus melakukan sesuatu yang rumit. Mungkin yang paling bijaksana adalah untuk mendefinisikan sebuah fungsi random_extended()
yang menarik n
bit (menggunakan random_at_most()
) dan kembali di [0, 2**n)
, dan kemudian menerapkan random_at_most()
dengan random_extended()
di tempat random()
(dan 2**n - 1
di tempat RAND_MAX
) untuk menarik nilai acak kurang dari 2**n
, dengan asumsi Anda memiliki tipe numerik yang dapat menahan seperti sebuah nilai. Akhirnya, tentu saja, Anda bisa mendapatkan nilai dalam [min, max]
menggunakan min + random_at_most(max - min)
, termasuk nilai negatif.