Create numbers with boundaries based on a probability per number - java

I would like to create some numbers each time based on a probability.
Example:
Numbers from : 1-5
Number 1 has probability to be created by 0.55,
Number 2 has probability to be craeted by 0.25,
Number 3 has probability to be craeted by 0.10,
Number 4 has probability to be craeted by 0.05,
Number 5 has probability to be craeted by 0.05
In this case, it has to usually create number 1, and often number 2, sometimes number 3 and rarely number 4 or 5
So, the "method": create_the_numbers(int ending_number) should be like this pseado-code:
public int create_the_numbers(int ending_number){
return probability(Random.getInt(ending_number))
}
There is an question for the C# here: Probability Random Number Generator

The following method will do it:
private static int get() {
double d = Math.random();
if (d < 0.55) return 1; // 0.55
if (d < 0.80) return 2; // 0.25
if (d < 0.90) return 3; // 0.10
if (d < 0.95) return 4; // 0.05
return 5; // 0.05
}
Using Math.random() is simple, but has it limitations. Read the javadoc. Alternatively, use Random.

I believe you are asking how to weight probabilities. A simple method is as follows:
int getWeightedRandomInt(double... weights) {
final Random random = new Random();
double val = random.nextDouble();
for (int i = 0; i < weights.length; i++) {
if (val < weights[i])
return i;
}
throw new IllegalArgumentException("badly formed weights");
}
This is used by passing sorted cumulative weights. In your example:
getWeightedRandomInt(0.55, 0.8, 0.9, 0.95, 1.0);
If you are using Java 8:
Arrays.stream(weights).filter(w -> val < w)
.findFirst().orElseThrow(IllegalArgumentException::new);

Related

Java Random() rounding off

I'm using Java's Random to generate random numbers: 1.0, 1.1 - 10
Random random = new Random();
return (double) ((random.nextInt(91) + 10) / 10.0);
When I printed a lot of these numbers (2000), I noticed 1.0 and 10 are significant less printed than all others (repeated 20 times, happened every time). Most likely because 0.95-0.99 and 10.01-10.04 aren't generated.
Now I have read a lot of threads about this, but it still leaves me to the following question:
If these numbers would represent grades for example, you can't get lower than a 1 and higher than a 10 here, would it be legit to extend the range from 0.95 up to 10.04?
Random random = new Random();
return Double.valueOf((1005-95) / 100);
To generate a random value between 1.1 and 10 use the following code:
double min = 1.1d;
double max = 10d;
Random r = new Random();
double value = min + (max - min) * r.nextDouble();
Afterwarsds you can use Math.floor(value) too round your result
This premise
Most likely because 0.95-0.99 and 10.01-10.04 aren't
generated.
is wrong. You generate random ints from 10 inclusive to 100 inclusive. Lower fractions and rounding of values does not play into it. Random nextInt is random in the interval; the end cases is not discriminated against.
I think your method
Random random = new Random();
return (double) ((random.nextInt(91) + 10) / 10.0);
Looks correct. I would suggest measuring the anomaly you are experiencing, maybe it is a human bias from when you are merely looking at the output.
Here is some code that measures the actual random generation of the 91 values. It is before the conversion to double which is not ideal.(but I do not see how dividing by 10 does anything else than map values as 10 -> 1.0, 11 -> 1.1 ... 99 -> 9.9 and 100 -> 10.0. A measure of the final result would of course be more desirable)
Random random = new Random();
int[] measure = new int[101];
for (int i = 0; i < 10000; i++) {
int number = (random.nextInt(91) + 10);
measure[number]++;
}
for (int i = 0; i < 101; i++) {
System.out.println(i + " count: " + measure[i]);
}
Looking at the results from that code the 10 and 100 values seem to come up as often as any other.

JAVA : generate integer numbers randomly with probabilities

How can I generate integer numbers randomly from (1 to 100) with probability for example 30% if the numbers range from (1 to 50), and with probability 70% if the numbers range from (50 to 100)?
int integer = new Random().nextInt(100) + 1;
// Probabilities
..... code here ....
How would I do that?
Here is a method getRandom() which returns a single random number meeting the criteria you specified. It actually uses a random number between 0 and 9 to determine which of the two ranges to use.
public int getRandom() {
Random random = new Random();
int val = random.nextInt(10);
if (val < 3) {
return random.nextInt(50) + 1; // random range 1 to 50
}
else {
return random.nextInt(51) + 50; // random range 50 to 100
}
}
Here's a general solution that will return one of any number of events, where you specify the relative weights of the events. The weights could be probabilities, but they don't have to; they don't have to add up to 1. For example, if you have three events, and you want the first one to have probability 20%, the second 30%, and the third 50%, you could call addEvent on each event with 2, 3, and 5 as the second parameter, or 20, 30, and 50, or 0.2, 0.3, and 0.5, or any other combination of numbers that has those ratios. For your case, you could make the generic parameter an interval and add two events with weights 3 and 7 (or 30 and 70, or whatever); then, when you call randomEvent, and it returns an interval with endpoints m and n inclusive, you then generate another random number in that interval:
value = m + random.nextInt(n - m + 1);
where random is your own instance of Random.
class RandomDistribution<T> {
private class Event {
public final T event;
public final double relativeWeight;
public Event(T event, double relativeWeight) {
this.event = event;
this.relativeWeight = relativeWeight;
}
}
private double totalWeight = 0D;
private ArrayList<Event> events = new ArrayList<>();
private Random generator = new Random();
public void addEvent(T event, double relativeWeight) {
events.add(new Event(event, relativeWeight));
totalWeight += relativeWeight;
}
public T randomEvent() {
double random = generator.nextDouble() * totalWeight;
for (Event event : events) {
random -= event.relativeWeight;
if (random < 0D) {
return event.event;
}
}
// It's possible to get here due to rounding errors
return events.get(events.size() - 1).event;
}
}
You can use MockNeat probabilities() method.
String s = mockNeat.probabilites(String.class)
.add(0.1, "A")
.add(0.2, "B")
.add(0.5, "C")
.add(0.2, "D")
.val();
The above example will generate "A" with 10% probability, B with 20% probability and so on.
Integer x = m.probabilites(Integer.class)
.add(0.2, m.ints().range(0, 100))
.add(0.5, m.ints().range(100, 200))
.add(0.3, m.ints().range(200, 300))
.val();
The above example will generate a number in the range [0, 100) with 20% probability, a number in the range [100, 200) with 50% probability and a number in the range [200, 300) with 30% probability.
Disclaimer: I am the author of the library, so I might be biased when I am recommending it.

Random number with Probabilities

I am wondering what would be the best way (e.g. in Java) to generate random numbers within a particular range where each number has a certain probability to occur or not?
e.g.
Generate random integers from within [1;3] with the following probabilities:
P(1) = 0.2
P(2) = 0.3
P(3) = 0.5
Right now I am considering the approach to generate a random integer within [0;100] and do the following:
If it is within [0;20] --> I got my random number 1.
If it is within [21;50] --> I got my random number 2.
If it is within [51;100] --> I got my random number 3.
What would you say?
Yours is a pretty good way already and works well with any range.
Just thinking: another possibility is to get rid of the fractions by multiplying with a constant multiplier, and then build an array with the size of this multiplier. Multiplying by 10 you get
P(1) = 2
P(2) = 3
P(3) = 5
Then you create an array with the inverse values -- '1' goes into elements 1 and 2, '2' into 3 to 6, and so on:
P = (1,1, 2,2,2, 3,3,3,3,3);
and then you can pick a random element from this array instead.
(Add.) Using the probabilities from the example in kiruwka's comment:
int[] numsToGenerate = new int[] { 1, 2, 3, 4, 5 };
double[] discreteProbabilities = new double[] { 0.1, 0.25, 0.3, 0.25, 0.1 };
the smallest multiplier that leads to all-integers is 20, which gives you
2, 5, 6, 5, 2
and so the length of numsToGenerate would be 20, with the following values:
1 1
2 2 2 2 2
3 3 3 3 3 3
4 4 4 4 4
5 5
The distribution is exactly the same: the chance of '1', for example, is now 2 out of 20 -- still 0.1.
This is based on your original probabilities all adding up to 1. If they do not, multiply the total by this same factor (which is then going to be your array length as well).
Some time ago I wrote a helper class to solve this issue. The source code should show the concept clear enough:
public class DistributedRandomNumberGenerator {
private Map<Integer, Double> distribution;
private double distSum;
public DistributedRandomNumberGenerator() {
distribution = new HashMap<>();
}
public void addNumber(int value, double distribution) {
if (this.distribution.get(value) != null) {
distSum -= this.distribution.get(value);
}
this.distribution.put(value, distribution);
distSum += distribution;
}
public int getDistributedRandomNumber() {
double rand = Math.random();
double ratio = 1.0f / distSum;
double tempDist = 0;
for (Integer i : distribution.keySet()) {
tempDist += distribution.get(i);
if (rand / ratio <= tempDist) {
return i;
}
}
return 0;
}
}
The usage of the class is as follows:
DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
drng.addNumber(1, 0.3d); // Adds the numerical value 1 with a probability of 0.3 (30%)
// [...] Add more values
int random = drng.getDistributedRandomNumber(); // Generate a random number
Test driver to verify functionality:
public static void main(String[] args) {
DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
drng.addNumber(1, 0.2d);
drng.addNumber(2, 0.3d);
drng.addNumber(3, 0.5d);
int testCount = 1000000;
HashMap<Integer, Double> test = new HashMap<>();
for (int i = 0; i < testCount; i++) {
int random = drng.getDistributedRandomNumber();
test.put(random, (test.get(random) == null) ? (1d / testCount) : test.get(random) + 1d / testCount);
}
System.out.println(test.toString());
}
Sample output for this test driver:
{1=0.20019100000017953, 2=0.2999349999988933, 3=0.4998739999935438}
You already wrote the implementation in your question. ;)
final int ran = myRandom.nextInt(100);
if (ran > 50) { return 3; }
else if (ran > 20) { return 2; }
else { return 1; }
You can speed this up for more complex implementations by per-calculating the result on a switch table like this:
t[0] = 1; t[1] = 1; // ... one for each possible result
return t[ran];
But this should only be used if this is a performance bottleneck and called several hundred times per second.
If you have performance issue instead of searching all the n values O(n)
you could perform binary search which costs O(log n)
Random r=new Random();
double[] weights=new double[]{0.1,0.1+0.2,0.1+0.2+0.5};
// end of init
double random=r.nextDouble();
// next perform the binary search in weights array
you only need to access log2(weights.length) in average if you have a lot of weights elements.
Your approach is fine for the specific numbers you picked, although you could reduce storage by using an array of 10 instead of an array of 100. However, this approach doesn't generalize well to large numbers of outcomes or outcomes with probabilities such as 1/e or 1/PI.
A potentially better solution is to use an alias table. The alias method takes O(n) work to set up the table for n outcomes, but then is constant time to generate regardless of how many outcomes there are.
Try this:
In this example i use an array of chars, but you can substitute it with your integer array.
Weight list contains for each char the associated probability.
It represent the probability distribution of my charset.
In weightsum list for each char i stored his actual probability plus the sum of any antecedent probability.
For example in weightsum the third element corresponding to 'C', is 65:
P('A') + P('B) + P('C') = P(X=>c)
10 + 20 + 25 = 65
So weightsum represent the cumulative distribution of my charset.
weightsum contains the following values:
It's easy to see that the 8th element correspondig to H, have a larger gap (80 of course like his probability) then is more like to happen!
List<Character> charset = Arrays.asList('A','B','C','D','E','F','G','H','I','J');
List<Integer> weight = Arrays.asList(10,30,25,60,20,70,10,80,20,30);
List<Integer> weightsum = new ArrayList<>();
int i=0,j=0,k=0;
Random Rnd = new Random();
weightsum.add(weight.get(0));
for (i = 1; i < 10; i++)
weightsum.add(weightsum.get(i-1) + weight.get(i));
Then i use a cycle to get 30 random char extractions from charset,each one drawned accordingly to the cumulative probability.
In k i stored a random number from 0 to the max value allocated in weightsum.
Then i look up in weightsum for a number grather than k, the position of the number in weightsum correspond to the same position of the char in charset.
for (j = 0; j < 30; j++)
{
Random r = new Random();
k = r.nextInt(weightsum.get(weightsum.size()-1));
for (i = 0; k > weightsum.get(i); i++) ;
System.out.print(charset.get(i));
}
The code give out that sequence of char:
HHFAIIDFBDDDHFICJHACCDFJBGBHHB
Let's do the math!
A = 2
B = 4
C = 3
D = 5
E = 0
F = 4
G = 1
H = 6
I = 3
J = 2
Total.:30
As we wish D and H are have more occurances (70% and 80% prob.)
Otherwinse E didn't come out at all. (10% prob.)
there is one more effective way rather than getting into fractions or creating big arrays or hard coding range to 100
in your case array becomes int[]{2,3,5} sum = 10
just take sum of all the probablity run random number generator on it
result = New Random().nextInt(10)
iterate over array elements from index 0 and calculate sum and return when sum is greater than return element of that index as a output
i.e if result is 6 then it will return index 2 which is no 5
this solution will scale irrespective of having big numbers or size of the range
If you are not against adding a new library in your code, this feature is already implemented in MockNeat, check the probabilities() method.
Some examples directly from the wiki:
String s = mockNeat.probabilites(String.class)
.add(0.1, "A") // 10% chance
.add(0.2, "B") // 20% chance
.add(0.5, "C") // 50% chance
.add(0.2, "D") // 20% chance
.val();
Or if you want to generate numbers within given ranges with a given probability you can do something like:
Integer x = m.probabilites(Integer.class)
.add(0.2, m.ints().range(0, 100))
.add(0.5, m.ints().range(100, 200))
.add(0.3, m.ints().range(200, 300))
.val();
Disclaimer: I am the author of the library, so I might be biased when I am recommending it.
Here is the python code even though you ask for java, but it's very similar.
# weighted probability
theta = np.array([0.1,0.25,0.6,0.05])
print(theta)
sample_axis = np.hstack((np.zeros(1), np.cumsum(theta)))
print(sample_axis)
[0. 0.1 0.35 0.95 1. ]. This represent the cumulative distribution.
you can use a uniform distribution to draw an index in this unit range.
def binary_search(axis, q, s, e):
if e-s <= 1:
print(s)
return s
else:
m = int( np.around( (s+e)/2 ) )
if q < axis[m]:
binary_search(axis, q, s, m)
else:
binary_search(axis, q, m, e)
range_index = np.random.rand(1)
print(range_index)
q = range_index
s = 0
e = sample_axis.shape[0]-1
binary_search(sample_axis, q, 0, e)
Also responded here: find random country but probability of picking higher population country should be higher. Using TreeMap:
TreeMap<Integer, Integer> map = new TreeMap<>();
map.put(percent1, 1);
map.put(percent1 + percent2, 2);
// ...
int random = (new Random()).nextInt(100);
int result = map.ceilingEntry(random).getValue();
This may be useful to someone, a simple one I did in python. you just have to change the way p and r are written. This one, for instance, projects random values between 0 and 0.1 to 1e-20 to 1e-12.
import random
def generate_distributed_random():
p = [1e-20, 1e-12, 1e-10, 1e-08, 1e-04, 1e-02, 1]
r = [0, 0.1, 0.3, 0.5, 0.7, 0.9, 1]
val = random.random()
for i in range(1, len(r)):
if val <= r[i] and val >= r[i - 1]:
slope = (p[i] - p[i - 1])/(r[i] - r[i - 1])
return p[i - 1] + (val - r[i - 1])*slope
print(generate_distributed_random())
referencing the paper pointed by pjs in another post , the population of base64 table can be further optimized. The result is amazingly fast, initialization is slightly expensive, but if the probabilities are not changing often, this is a good approach.
*For duplicate key, the last probability is taken instead of being combined (slightly different from EnumeratedIntegerDistribution behaviour)
public class RandomGen5 extends BaseRandomGen {
private int[] t_array = new int[4];
private int sumOfNumerator;
private final static int DENOM = (int) Math.pow(2, 24);
private static final int[] bitCount = new int[] {18, 12, 6, 0};
private static final int[] cumPow64 = new int[] {
(int) ( Math.pow( 64, 3 ) + Math.pow( 64, 2 ) + Math.pow( 64, 1 ) + Math.pow( 64, 0 ) ),
(int) ( Math.pow( 64, 2 ) + Math.pow( 64, 1 ) + Math.pow( 64, 0 ) ),
(int) ( Math.pow( 64, 1 ) + Math.pow( 64, 0 ) ),
(int) ( Math.pow( 64, 0 ) )
};
ArrayList[] base64Table = {new ArrayList<Integer>()
, new ArrayList<Integer>()
, new ArrayList<Integer>()
, new ArrayList<Integer>()};
public int nextNum() {
int rand = (int) (randGen.nextFloat() * sumOfNumerator);
for ( int x = 0 ; x < 4 ; x ++ ) {
if (rand < t_array[x])
return x == 0 ? (int) base64Table[x].get(rand >> bitCount[x])
: (int) base64Table[x].get( ( rand - t_array[x-1] ) >> bitCount[x]) ;
}
return 0;
}
public void setIntProbList( int[] intList, float[] probList ) {
Map<Integer, Float> map = normalizeMap( intList, probList );
populateBase64Table( map );
}
private void clearBase64Table() {
for ( int x = 0 ; x < 4 ; x++ ) {
base64Table[x].clear();
}
}
private void populateBase64Table( Map<Integer, Float> intProbMap ) {
int startPow, decodedFreq, table_index;
float rem;
clearBase64Table();
for ( Map.Entry<Integer, Float> numObj : intProbMap.entrySet() ) {
rem = numObj.getValue();
table_index = 3;
for ( int x = 0 ; x < 4 ; x++ ) {
decodedFreq = (int) (rem % 64);
rem /= 64;
for ( int y = 0 ; y < decodedFreq ; y ++ ) {
base64Table[table_index].add( numObj.getKey() );
}
table_index--;
}
}
startPow = 3;
for ( int x = 0 ; x < 4 ; x++ ) {
t_array[x] = x == 0 ? (int) ( Math.pow( 64, startPow-- ) * base64Table[x].size() )
: ( (int) ( ( Math.pow( 64, startPow-- ) * base64Table[x].size() ) + t_array[x-1] ) );
}
}
private Map<Integer, Float> normalizeMap( int[] intList, float[] probList ) {
Map<Integer, Float> tmpMap = new HashMap<>();
Float mappedFloat;
int numerator;
float normalizedProb, distSum = 0;
//Remove duplicates, and calculate the sum of non-repeated keys
for ( int x = 0 ; x < probList.length ; x++ ) {
mappedFloat = tmpMap.get( intList[x] );
if ( mappedFloat != null ) {
distSum -= mappedFloat;
} else {
distSum += probList[x];
}
tmpMap.put( intList[x], probList[x] );
}
//Normalise the map to key -> corresponding numerator by multiplying with 2^24
sumOfNumerator = 0;
for ( Map.Entry<Integer, Float> intProb : tmpMap.entrySet() ) {
normalizedProb = intProb.getValue() / distSum;
numerator = (int) ( normalizedProb * DENOM );
intProb.setValue( (float) numerator );
sumOfNumerator += numerator;
}
return tmpMap;
}
}

Implementing probability distribution function in Java

I'm trying to implement a probability distribution function in java where it returns the ith entry in the array with probability:
Fi = 6i(n-i) / (n3 - n)
where n is the array length i.e. for an array length 4:
P1 = 3/10, P2 = 4/10, P3 = 3/10, P4 = 0
Note that this function assumes numbering from 1 to n rather 0 to n-1 as in Java.
At the moment I'm just using the uniform distribution i.e.
int i = (int)(Math.random()*((arraySize)-1));
with the -1 so it doesn't choose the last element (i.e. Pn = 0 as in the above formula).
Anyone with any ideas or tips on implementing this?
double rand = Math.random(); // generate a random number in [0,1]
F=0;
// you test if rand is in [F(1)+..+F(i):F(1)+..+F(i)+F(i+1)] it is in this rnge with proba P(i) and therefore if it is in this range you return i
for (int i=1,i<array.size();i++ ){
F+=F(i);
if rand < F
return i;
}
return array.size(); // you went through all the array then rand==1 (this probability is null) and you return n
This is essentially what thomson_matt says, but a little more formally: You should perform discrete inverse transform sampling. Pseudocode for your example:
p = [0.3, 0.4, 0.3. 0.0]
c = [0.3, 0.7, 1.0, 1.0] // cumulative sum
generate x uniformly in continuous range [0,1]
find max i such that c[i] < x.
To do this, you want to divide the range [0, 1] into regions that have the required size. So in this case:
0 -> 0.0 - 0.3
1 -> 0.3 - 0.7
2 -> 0.7 - 1.0
3 -> 1.0 - 1.0
Then generate a random number with Math.random(), and see which interval it falls into.
In general, you want to do something like the following pseudocode:
double r = Math.random();
int i = -1;
while (r >= 0)
{
i++;
r -= F(i);
}
// i is now the value you want.
You generate a value on [0, 1], then subtract the size of each interval until you go below 0, at which point you've found your random value.
You could try using a navigable map with the probability distribution. Unlike normal Maps the NaviableMap defines an absolute ordering over its keys. And if the key isn't present in the map it can tell you which is the closest key, or which is the smallest key that is greater than the argument. I've used ceilingEntry which returns the map entry with the smallest key that is greater than or equal to the given key.
If you use a TreeMap as your implementation of NavigableMap then look ups on distributions with many classes will be faster as it performs a binary search rather than starting with the first key and then testing each key in turn.
The other advantage of NaviableMap is that you get the class of data your directly interested in rather than an index to another array or list, which can make code cleaner.
In my example I've used BigDecimals as I'm not particularly fond of using floating point numbers as you can't specify the precision you need. But you could use floats or doubles or whatever.
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.NavigableMap;
import java.util.TreeMap;
public class Main {
public static void main(String[] args) {
String[] classes = {"A", "B", "C", "D"};
BigDecimal[] probabilities = createProbabilities(classes.length);
BigDecimal[] distribution = createDistribution(probabilities);
System.out.println("probabilities: "+Arrays.toString(probabilities));
System.out.println("distribution: "+Arrays.toString(distribution)+"\n");
NavigableMap<BigDecimal, String> map = new TreeMap<BigDecimal, String>();
for (int i = 0; i < distribution.length; i++) {
map.put(distribution[i], classes[i]);
}
BigDecimal d = new BigDecimal(Math.random());
System.out.println("probability: "+d);
System.out.println("result: "+map.ceilingEntry(d).getValue());
}
private static BigDecimal[] createDistribution(BigDecimal[] probabilities) {
BigDecimal[] distribution = new BigDecimal[probabilities.length];
distribution[0] = probabilities[0];
for (int i = 1; i < distribution.length; i++) {
distribution[i] = distribution[i-1].add(probabilities[i]);
}
return distribution;
}
private static BigDecimal[] createProbabilities(int n) {
BigDecimal[] probabilities = new BigDecimal[n];
for (int i = 0; i < probabilities.length; i++) {
probabilities[i] = F(i+1, n);
}
return probabilities;
}
private static BigDecimal F(int i, int n) {
// 6i(n-i) / (n3 - n)
BigDecimal j = new BigDecimal(i);
BigDecimal m = new BigDecimal(n);
BigDecimal six = new BigDecimal(6);
BigDecimal dividend = m.subtract(j).multiply(j).multiply(six);
BigDecimal divisor = m.pow(3).subtract(m);
return dividend.divide(divisor, 64, RoundingMode.HALF_UP);
}
}

Generating N numbers that sum to 1

Given an array of size n I want to generate random probabilities for each index such that Sigma(a[0]..a[n-1])=1
One possible result might be:
0 1 2 3 4
0.15 0.2 0.18 0.22 0.25
Another perfectly legal result can be:
0 1 2 3 4
0.01 0.01 0.96 0.01 0.01
How can I generate these easily and quickly? Answers in any language are fine, Java preferred.
Get n random numbers, calculate their sum and normalize the sum to 1 by dividing each number with the sum.
The task you are trying to accomplish is tantamount to drawing a random point from the N-dimensional unit simplex.
http://en.wikipedia.org/wiki/Simplex#Random_sampling might help you.
A naive solution might go as following:
public static double[] getArray(int n)
{
double a[] = new double[n];
double s = 0.0d;
Random random = new Random();
for (int i = 0; i < n; i++)
{
a [i] = 1.0d - random.nextDouble();
a [i] = -1 * Math.log(a[i]);
s += a[i];
}
for (int i = 0; i < n; i++)
{
a [i] /= s;
}
return a;
}
To draw a point uniformly from the N-dimensional unit simplex, we must take a vector of exponentially distributed random variables, then normalize it by the sum of those variables. To get an exponentially distributed value, we take a negative log of uniformly distributed value.
This is relatively late, but to show the ammendment to #Kobi's simple and straightforward answer given in this paper pointed to by #dreeves which makes the sampling uniform. The method (if I understand it clearly) is to
Generate n-1 distinct values from the range [1, 2, ... , M-1].
Sort the resulting vector
Add 0 and M as the first and last elements of the resulting vector.
Generate a new vector by computing xi - xi-1 where i = 1,2, ... n. That is, the new vector is made up of the differences between consecutive elements of the old vector.
Divide each element of the new vector by M. You have your uniform distribution!
I am curious to know if generating distinct random values and normalizing them to 1 by dividing by their sum will also produce a uniform distribution.
Get n random numbers, calculate their sum and normalize the sum to 1
by dividing each number with the sum.
Expanding on Kobi's answer, here's a Java function that does exactly that.
public static double[] getRandDistArray(int n) {
double randArray[] = new double[n];
double sum = 0;
// Generate n random numbers
for (int i = 0; i < randArray.length; i++) {
randArray[i] = Math.random();
sum += randArray[i];
}
// Normalize sum to 1
for (int i = 0; i < randArray.length; i++) {
randArray[i] /= sum;
}
return randArray;
}
In a test run, getRandDistArray(5) returned the following
[0.1796505603694718, 0.31518724882558813, 0.15226147256596428, 0.30954417535503603, 0.043356542883939767]
If you want to generate values from a normal distribution efficiently, try the Box Muller Transformation.
public static double[] array(int n){
double[] a = new double[n];
double flag = 0;
for(int i=0;i<n;i++){
a[i] = Math.random();
flag += a[i];
}
for(int i=0;i<n;i++) a[i] /= flag;
return a;
}
Here, at first a stores random numbers. And the flag will keep the sum all the numbers generated so that at the next for loop the numbers generated will be divided by the flag, which at the end the array will have random numbers in probability distribution.

Categories