Generating a List of Random Numbers, Summing to 1

Generating a list of random numbers, summing to 1

The simplest solution is indeed to take N random values and divide by the sum.

A more generic solution is to use the Dirichlet distribution
which is available in numpy.

By changing the parameters of the distribution you can change the "randomness" of individual numbers

>>> import numpy as np, numpy.random
>>> print np.random.dirichlet(np.ones(10),size=1)
[[ 0.01779975 0.14165316 0.01029262 0.168136 0.03061161 0.09046587
0.19987289 0.13398581 0.03119906 0.17598322]]

>>> print np.random.dirichlet(np.ones(10)/1000.,size=1)
[[ 2.63435230e-115 4.31961290e-209 1.41369771e-212 1.42417285e-188
0.00000000e+000 5.79841280e-143 0.00000000e+000 9.85329725e-005
9.99901467e-001 8.37460207e-246]]

>>> print np.random.dirichlet(np.ones(10)*1000.,size=1)
[[ 0.09967689 0.10151585 0.10077575 0.09875282 0.09935606 0.10093678
0.09517132 0.09891358 0.10206595 0.10283501]]

Depending on the main parameter the Dirichlet distribution will either give vectors where all the values are close to 1./N where N is the length of the vector, or give vectors where most of the values of the vectors will be ~0 , and there will be a single 1, or give something in between those possibilities.

EDIT (5 years after the original answer): Another useful fact about the Dirichlet distribution is that you naturally get it, if you generate a Gamma-distributed set of random variables and then divide them by their sum.

Python - Generate a list of 40 random numbers summing to 1, with min and max weight

Perhaps using np.random.normal would give better results. You could scale down the distribution to the 0.005-0.045 range using the proportion of 80% that is variable (above 0.005). Because normal distributions can still have outliers, it will be necessary to "retry" the calculation if the values go out of bounds (but that shouldn't happen too frequently unless you give a large standard deviation):

import numpy as np

def randStock(count=40,minR=0.005,maxR=0.045,sd=3):
iterations = 0
while True:
iterations += 1
r = np.random.normal(1,sd,count) #normal distribution
r -= min(r) # offset to zero
r /= max(r) # scale to 0..1
r = minR + r/sum(r)*(maxR-minR)/(maxR+minR) # scale to range
if min(r)>=minR and max(r)<=maxR: return r, iterations

Output:

for _ in range(10):
s,i = randStock()
print(*map("{:6.4f}".format,(sum(s),min(s),max(s))),i,"iterations")

[sum] [min] [max] [mean]
1.0000 0.0050 0.0404 0.0250 1 iterations
1.0000 0.0050 0.0409 0.0250 2 iterations
1.0000 0.0050 0.0395 0.0250 1 iterations
1.0000 0.0050 0.0411 0.0250 4 iterations
1.0000 0.0050 0.0410 0.0250 2 iterations
1.0000 0.0050 0.0428 0.0250 1 iterations
1.0000 0.0050 0.0433 0.0250 1 iterations
1.0000 0.0050 0.0424 0.0250 1 iterations
1.0000 0.0050 0.0371 0.0250 1 iterations
1.0000 0.0050 0.0446 0.0250 1 iterations

Note that this could be improved to randomize the lower bound a bit more and you can chose a different standard deviations

Generating random numbers to obtain a fixed sum(python)

This might not be the most efficient way but it will work

totals = [54, 1536, 36, 14]

nums = []
x = np.random.randint(0, i, size=(6,))
for i in totals:
while sum(x) != i: x = np.random.randint(0, i, size=(6,))
nums.append(x)
print(nums)

[array([ 3, 19, 21, 11, 0, 0]), array([111, 155, 224, 511, 457,
78]), array([ 8, 5, 4, 12, 2, 5]), array([3, 1, 3, 2, 1, 4])]


This is a way more efficient way to do this

totals = [54,1536,36,14,9,360, 0]

nums = []
for i in totals:
if i == 0:
nums.append([0 for i in range(6)])
continue
total = i
temp = []
for i in range(5):
val = np.random.randint(0, total)
temp.append(val)
total -= val
temp.append(total)
nums.append(temp)

print(nums)

[[22, 4, 16, 0, 2, 10], [775, 49, 255, 112, 185, 160], [2, 10, 18, 2,
0, 4], [10, 2, 1, 0, 0, 1], [8, 0, 0, 0, 0, 1], [330, 26, 1, 0, 2, 1],
[0, 0, 0, 0, 0, 0]]

How to generate three random numbers, whose sum is 1?

Just get 3 random numbers and then calculate a factor which is 1 / [sum of your numbers]. Finally multiply each of the random numbers with that factor. The sum will be 1.

Generating random floats, summing to 1, with minimum value

IIUC, you want to generate an array of k values, with minimum value of low=0.055.

It is easier to generate numbers from 0 that sum up to 1-low*k, and then to add low so that the final array sums to 1. Thus, this guarantees both the lower bound and the sum.

Regarding the high, I am pretty sure it is mathematically impossible to add this constraint as once you fix the lower bound and the sum, there is not enough degrees of freedom to chose an upper bound. The upper bound will be 1-low*(k-1) (here 0.505).

Also, be aware that, with a minimum value, you necessarily enforce a maximum k of 1//low (here 18 values). If you set k higher, the low bound won't be correct.

# parameters
low = 0.055
k = 10

a = np.random.rand(k)
a = (a/a.sum()*(1-low*k))
weights = a+low

# checking that the sum is 1
assert np.isclose(weights.sum(), 1)

Example output:

array([0.13608635, 0.06796974, 0.07444545, 0.1361171 , 0.07217206,
0.09223554, 0.12713463, 0.11012871, 0.1107402 , 0.07297022])

Generating a list of random numbers, summing to a fixed amount using SQL

While the @Squirrel answer is interesting but numbers here is more random
here is the code:

DECLARE @s INT=1,
@k FLOAT=0,
@final FLOAT=9.917,
@sum FLOAT =0,
@min FLOAT=1,
@max FLOAT=9.917

BEGIN
WHILE (@sum <> @final)
BEGIN
WHILE (@s <= 10)
BEGIN

SET @k =
(
SELECT ROUND(RAND(CHECKSUM(NEWID())) * (@max - @min) + @min,3)
);
PRINT (CONCAT('random: ',@k));

IF(@sum+@k <=@final)
SET @sum+=@k;
SET @max=@final-@sum;

PRINT (CONCAT('computed sum: ',@k));
IF(@max>1) SET @min=1 ELSE SET @min=0;

IF(@sum=@final)
BREAK;

SET @s = @s + 1;
SET @k = @k + 0;
END;

PRINT (CONCAT('final', @final))
PRINT (CONCAT('sum', @sum))

IF(@sum<>@final)--force stop if after 10 try the sum not match with final
BEGIN
PRINT(CONCAT('final random number:',@final-@sum))
SET @sum=@final;
END;

SET @s=0;

IF(@sum=@final)
BEGIN
PRINT('****************************DONE****************************')
BREAK;
END
END;
PRINT ('end');
END;

Generate random list of numbers that add up to 1

One option would be to use generate to fill the vector with random numbers, then using accumulate to sum up the values, and finally dividing all the values in the vector by the sum to normalize the sum to one. This is shown here:

std::vector<double> vec(23);
std::generate(vec.begin(), vec.end(), /* some random source */);
const double total = std::accumulate(vec.begin(), vec.end(), 0.0);
for (double& value: vec) value /= total;

Hope this helps!



Related Topics



Leave a reply



Submit