Menjana nombor rawak dengan Math.random()
Pustaka bahasa pengaturcaraan Java menyediakan fungsi/metodMath.random()
untuk menjana nombor rawak dalam atur cara. Metod ini akan menjana nilai dalam julat 0 hingga 1, dan nilai ini boleh digunakan untuk menjana nombor tertentu dalam julat tertentu.
Misalnya kod atur cara berikut menjana 5 nombor rawak dalam julat 0 hingga 9 :
Manakala dengan menambah 1, seperti berikut, boleh mendapatkan 5 nombor dalam julat 1 hingga 10 :
for(int i = 0; i < 5; i++) {
System.out.println((int)(Math.random() * 10));
}
Manakala dengan menambah 1, seperti berikut, boleh mendapatkan 5 nombor dalam julat 1 hingga 10 :
for(int i = 0; i < 5; i++) {
System.out.println((int)(Math.random() * 10 + 1));
}
Kita juga boleh menjana nombor rawak dalam julat 'min' dan 'max' yang kita sendiri tetapkan. Misalnya contoh berikut menjana nombor rawak dalam lingkungan 5 hingga 9:
int min = 5;
int max = 9;
for(int i = 0; i < 5; i++) {
System.out.println((int)(Math.random() * (max-min+1)) + min));
}
Menjana nombor rawak dengan taburan custom
Kebarangkalian bagi nombor dalam julat di atas untuk muncul/dipilih, secara amnya ialah sama rata, iaitu 1/N bagi setiap nombor (Misalnya julat 1 hingga 10, N=10, maka 1/10 = 0.1 bagi 1, 1/10 = 0.1 bagi 2, dan seterusnya sehingga 10).
Tapi bagaimana kita boleh menjana nombor rawak dalam julat 1 hingga 10 dengan pemberat tertentu, misalnya 50% percubaan (kebarangkalian 0.5, atau 1/2) mendapat nombor 4? Atau menetapkan 20% balingan dadu akan kena pada nombor 6? Bagaimana kita hendak menggunakan Math.random() untuk menjana nombor rawak berdasarkan pemberat/kebarangkalian yang ditetapkan? Pustaka Java yang asas ini tidak menyediakannya.
Saya cuba mencari implimentasi sedia ada, dan terjumpa perbincangan mengenainya di sini -> https://stackoverflow.com/questions/16435639/generating-random-integers-within-range-with-a-probability-distribution
Daripada perbincangan tersebut, saya mendapat idea untuk mengimplimentasi suatu kelas pustaka sendiri (dinamakan CustomProbabilityRandom), yang membolehkan seseorang pengatur cara menjana nombor rawak mengikut kebarangkalian tertentu. (Lihat senarai kod atur cara dalam fail CustomProbabilityRandom.java di bawah. Turut disertakan contoh aplikasi untuk cara menggunakan pustaka, iaitu fail ExampleCustomDistribution.java).
Dengan menggunakan kelas ini, agak mudah untuk menetapkan taburan kebarangkalian bagi suatu senarai item-item (nombor rawak yang dijana mewakili index bagi item dalam senarai).
Dengan menggunakan kelas ini, agak mudah untuk menetapkan taburan kebarangkalian bagi suatu senarai item-item (nombor rawak yang dijana mewakili index bagi item dalam senarai).
Sebenarnya implimentasi ini tidaklah benar-benar menghasilkan nombor rawak mengikut taburan yang ditetapkan, tetapi hanya boleh menghampiri taburan tersebut. Namun diharap dengan implimentasi ini akan memudahkan anda membina aplikasi yang melibatkan nombor rawak dengan lebih fleksibel.
Untuk mengkompil kod atur cara aplikasi dalam ExampleCustomDistribution.java, anda perlu kompil bersama-sama dengan fail pustaka CustomProbabilityRandom.java.
Selamat mencuba.
Selamat mencuba.
Fail 'CustomProbabilityRandom.java' (pustaka)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* ----------------------------------------------------------------------------- | |
* About: | |
* ----- | |
* A simple library to allow generation of random number (called 'index') based | |
* on custom distributions / probabilities. The use of index/integer as the random | |
* number will allow general usage (to refer an array of labels/strings, numbers, | |
* etc). | |
* ----------------------------------------------------------------------------- | |
* | |
* Dependency : none | |
* | |
* Author : Mohd Yunus Sharum (m_yunus@upm.edu.my) | |
* Created : 31 Oct 2019 | |
* Version : 0.1.0 | |
* Modified : - | |
* TODO : - | |
*------------------------------------------------------------------------------ | |
*/ | |
public class CustomProbabilityRandom | |
{ | |
protected int[] indexes; // a list of indexes for a list of items | |
protected double[] distribs; // a list of probabilities for each index | |
private double[] ranges; // a list of calculated ranges (from probabilities) | |
private int precision; // precision for the generated random number | |
// ... sum of distribs[] x precision = precision | |
// Size of values[] and distribs[] should be the same | |
// Total sum of values in distribs should be (approximately) 1 | |
// | |
CustomProbabilityRandom(int[] values, double[] distribs) | |
throws Exception | |
{ | |
this(values, distribs, 100); // default precision is 100 | |
} | |
// Size of values[] and distribs[] should be the same | |
// Total sum of values in distribs should be (approximately) 1 | |
// | |
CustomProbabilityRandom(int[] values, double[] distribs, int precision) | |
throws Exception | |
{ | |
if(values.length != distribs.length) { | |
throw( new Exception("CustomProbabilityRandom(): size of values[] " + | |
"not equal with distribs[]")); | |
} | |
this.indexes = values.clone(); | |
this.distribs = distribs.clone(); | |
this.precision = precision; | |
distribToRange(); | |
} | |
public int getPrecision() | |
{ | |
return precision; | |
} | |
public void setPrecision(int p) | |
throws Exception | |
{ | |
precision = p; | |
distribToRange(); | |
} | |
public int getRandomInt( ) | |
{ | |
double guess = Math.random() * precision; | |
for(int i = 0; i < distribs.length; i++) { | |
if( guess <= ranges[i] ) { | |
return indexes[i]; | |
} | |
} | |
return indexes[ distribs.length - 1 ]; | |
} | |
private void distribToRange() | |
throws Exception | |
{ | |
ranges = new double[ distribs.length ]; | |
// convert probability into ranges | |
// | |
double sum = distribs[0] * precision; | |
ranges[0] = sum; | |
for(int i = 1; i < distribs.length; i++) { | |
sum += distribs[i] * precision; | |
ranges[i] = sum; | |
} | |
if( (int)(ranges[ ranges.length - 1 ]) > precision ) { | |
throw( new Exception("CustomProbabilityRandom(): total distributions ("+ | |
(int)(ranges[ ranges.length - 1 ]) +") in distribs[] beyond 100%") | |
); | |
} | |
} | |
} |
Fail 'ExampleCustomDistribution.java' (aplikasi)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Example program | |
// | |
public class ExampleCustomDistribution { | |
public static void main(String[] args) { | |
// simulation of throwing a dice | |
int[] v = { 1, 2, 3, 4, 5, 6 }; | |
double[] d = { 0.167f, 0.167f, 0.167f, 0.167f, 0.167f, 0.165f }; // total should be 1.0 @ 100% | |
CustomProbabilityRandom custRand = null; | |
try { | |
custRand = new CustomProbabilityRandom(v, d); // precision = 100 | |
custRand.setPrecision(1000); // make it more precise (optional) | |
} catch( Exception e) { | |
e.printStackTrace(); | |
System.exit(1); | |
} | |
int attempts = 100000; | |
int[] count = { 0, 0, 0, 0, 0, 0 }; | |
for(int n = 0; n < attempts; n++) { | |
int val = custRand.getRandomInt(); | |
for(int i = 0; i < v.length; i++) { | |
if( val == v[i] ) { | |
count[i]++; | |
break; | |
} | |
} | |
} | |
System.out.println("Simulation of throwing a dice."); | |
System.out.println("Attempts 1 = " + attempts ); | |
for(int i = 0; i < v.length; i++) { | |
System.out.printf("\tNo=%2d (with prob.=%8.4f) appeared=" + count[i] + " times\n", v[i], d[i]); | |
} | |
System.out.println("Increase chances for 6, decrease for 5"); | |
d[4] = 0.1; // decrease chances for 5 | |
d[5] = 0.232; // increase chances of 6 | |
try { | |
custRand = new CustomProbabilityRandom(v, d, 1000); // precision = 100 | |
} catch( Exception e) { | |
e.printStackTrace(); | |
System.exit(1); | |
} | |
for(int i = 0; i < v.length; i++) { | |
count[i] = 0; | |
} | |
for(int n = 0; n < attempts; n++) { | |
int val = custRand.getRandomInt(); | |
for(int i = 0; i < v.length; i++) { | |
if( val == v[i] ) { | |
count[i]++; | |
break; | |
} | |
} | |
} | |
System.out.println("Attempts 2 = " + attempts ); | |
for(int i = 0; i < v.length; i++) { | |
System.out.printf("\tNo=%2d (with prob.=%8.4f) appeared=" + count[i] + " times\n", v[i], d[i]); | |
} | |
} | |
} |