Bottom-Heavy Random Number
java
Solution
Use `Random.nextDouble()` to generate a number in [0,1), then you can use `Math.pow()` to give it a non-linear bias and scale it up to cover your range, e.g.:
int randomBiased (int max, float bias) {
float v = Math.pow(random.nextDouble(), bias);
return (int)(v * max);
}
Or if you prefer a one-liner:
int value = (int)(100 * Math.pow(random.nextDouble(), bias));
This is useful because you can adjust `bias` to tweak the "rareness" of rarer items. A bias > 1 will favor lower numbers, < 1 will favor higher numbers, 1 will be uniform.
For example, `randomBiased(100, 2.0)` will give the same result distribution as Tim B's answer.
Note also that you can use any function that maps [0,1) to [0,1) to modify the bias; for example, you could use a cubic to bias all results away from the center (see the graph):
int randomFavorEdges (int max) {
float v = random.nextDouble();
v = 3*v*v - 2*v*v*v;
return (int)(v * max);
}
Another example is that you can get one half of a Gaussian distribution using `Math.abs(random.nextGaussian())` instead (see Christian's answer). The caveat is you have to keep trying that until you get a number less than 1 (it can go outside the range [-1,1]). However, you can take advantage of the large range by adding a scaling parameter that can be tweaked to taste:
int randomGaussian (int max, float scale) {
double v;
do {
v = Math.abs(random.nextGaussian() / scale);
} while (v >= 1.0);
return (int)(v * max);
}
Truth be told, after looking at the test results below, personally I like the Gaussian distributions with a higher scale value -- rare items become much rarer but without as intense a bias towards common items as the exponential distribution.
Update: I've created a project on ideone that demonstrates the methods listed above. Here is an example for 10000 samples of 30 values:
Name : Uniform Pow(0.5) Pow(2.0) Pow(10.0) G(1.0) G(3.5) Cubic
0 : 337 11 1872 7118 365 946 1126
1 : 359 33 744 511 406 939 517
2 : 327 60 589 328 360 876 407
3 : 330 101 458 224 370 846 307
4 : 347 103 445 170 395 817 310
5 : 344 131 366 141 374 727 257
6 : 326 148 358 147 416 691 275
7 : 326 180 309 106 373 645 254
8 : 314 195 295 129 335 575 282
9 : 331 227 311 79 356 506 219
10 : 329 245 325 84 376 458 258
11 : 340 241 230 75 370 366 228
12 : 367 290 251 75 366 320 215
13 : 343 294 264 70 345 243 237
14 : 313 317 256 60 344 211 224
15 : 346 331 240 66 358 186 210
16 : 353 363 204 48 338 131 226
17 : 329 373 213 55 344 157 193
18 : 323 417 200 57 327 86 230
19 : 323 446 219 56 354 75 208
20 : 321 466 211 43 296 56 209
21 : 339 496 205 40 297 34 267
22 : 338 484 192 48 278 37 237
23 : 335 523 168 46 290 19 306
24 : 327 571 162 31 251 18 279
25 : 322 573 216 49 263 7 302
26 : 323 580 152 35 277 11 311
27 : 333 596 209 41 268 9 354
28 : 316 590 158 38 250 6 495
29 : 339 615 178 30 258 2 1057
Note that you can really make rare objects rare with high bias value for the `Math.pow()` method or a high scale value for the Gaussian method. This also shows the bias of the cubic S-curve away from the center.
Problem
Right now, I'm trying to make an Item drop table for a minigame. In this game, I would like for you to receive certain items more commonly than others, with some items having a very low chance of being picked. I have tried using something along the lines of: ``` Random rand = new Random(); int chance = rand.nextInt(100) + 1; if(chance > 2){ //give common item; } else if(chance == 1){ //give rare item } ``` However, when you do this over a scale of 50+ items, it becomes very tedious to create, modify, as well as the code takes a long time to execute. So, is there some kind of bottom-heavy random that will create a large amount of low numbers (1's, 2's, etc.) and very few high numbers (50's, 60's, etc.)?