Generate Random Numbers Summing to a Predefined Value

Generate random numbers summing to a predefined value

b = random.randint(2, 38)
a = random.randint(1, b - 1)
c = random.randint(b + 1, 39)
return [a, b - a, c - b, 40 - c]

(I assume you wanted integers since you said "1-40", but this could be easily generalized for floats.)

Here's how it works:

  • cut the total range in two randomly, that's b. The odd range is because there are going to be at least 2 below the midpoint and at least 2 above. (This comes from your 1 minimum on each value).
  • cut each of those ranges in two randomly. Again, the bounds are to account for the 1 minimum.
  • return the size of each slice. They'll add up to 40.

Generate three Random numbers with sum add to predefined value in java, where 3 numbers stored in 3 different textfields?

You could use Math.random() instead of the Random class. Math.random() returns a double between 0 and 1. So the only thing you have to do is multiply the result of Math.random() with N. The next number would be N minus your result from the subtraction of N and the previous result.

final int N = 20;
final int result0 = (int) (Math.random() * N);
final int result1 = (int) (Math.random() * (N - result0));
final int result2 = N - result0 - result1;

EDIT: You could than choose the first, second and third parameter randomly too.

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.

how to generate random numbers that can be summed to a specific value?

The core idea is as you said (about getting 3*5=15 numbers), plus reshaping it into a 2D array with the same number of rows as that id has in the dataframe. The following function does that,

def generate_random_numbers(df):
value = df['value'].iloc[0]

list_len = 5
num_rows = len(df)
num_rand = list_len*num_rows

return pd.Series(
map(list, np.random.multinomial(value, np.ones(num_rand)/num_rand).reshape(num_rows, -1)),
df.index
)

And apply it:

df['random_value'] = df.groupby(['id', 'value'], as_index=False).apply(generate_random_numbers).droplevel(0)

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]]

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])

How to generate n random numbers within a range matching a fixed sum in javascript?

Here is one way to achieve the desired result.

The original solution is this: https://stackoverflow.com/a/19278621/17175441 which I have modified to account for the numbers range limit.

Note that there are probably better ways to do this but this does the job for now:

function generate({
targetSum,
numberCount,
minNum,
maxNum
}) {
var r = []
var currsum = 0
for (var i = 0; i < numberCount; i++) {
r[i] = Math.random() * (maxNum - minNum) + minNum
currsum += r[i]
}

let clamped = 0
let currentIndex = 0
while (currsum !== targetSum && clamped < numberCount) {
let currNum = r[currentIndex]
if (currNum == minNum || currNum == maxNum) {
currentIndex++
if (currentIndex > numberCount - 1) currentIndex = 0
continue
}
currNum += (targetSum - currsum) / 3
if (currNum <= minNum) {
r[currentIndex] = minNum + Math.random()
clamped++
} else if (currNum >= maxNum) {
r[currentIndex] = maxNum - Math.random()
clamped++
} else {
r[currentIndex] = currNum
}
currsum = r.reduce((p, c) => p + c)
currentIndex++
if (currentIndex > numberCount - 1) currentIndex = 0
}

if (currsum !== targetSum) {
console.log(`\nTargetSum: ${targetSum} can't be reached with the given options`)
}
return r
}

const numbers = generate({
targetSum: 88.3,
numberCount: 12,
minNum: 6,
maxNum: 9.99,
})
console.log('number = ', numbers)
console.log(
'Sum = ',
numbers.reduce((p, c) => p + c)
)

Random numbers to a sum of constant value in excel

So set it up - I laid it out like this but you can change that:

Sample Image

You can add flexibility by bringing the constraints for the 3 and 5 onto the sheet - which keeps them visible when you keep trying different scenarios. Like this:

Sample Image

So, based on the comment:

Sample Image

Went back to the other method to state the constraints.

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



Related Topics



Leave a reply



Submit