So here is the deal: I want to (for example) generate 4 pseudo-random numbers, that when added together would equal 40. How could this be dome in python? I could generate a random number 1-40, then generate another number between 1 and the remainder,etc, but then the first number would have a greater chance of "grabbing" more.
The basic idea goes like this: start from a random point x and take a random step xnew = x + delta. evaluate the desired probability distribution in the starting point p(x) and in the new one p(xnew) if the new point is more probable p(xnew)/p(x) >= 1 accept the move.
You could use the RAND() function to generate N numbers (8 in your case) in column A. Then, in column B you could use the following formula B1=A1/SUM(A:A)*320 , B2=A2/SUM(A:A)*320 and so on (where 320 is the sum that you are interested into). So you can just enter =RAND() in A1, then drag it down to A8.
Here's the standard solution. It's similar to Laurence Gonsalves' answer, but has two advantages over that answer.
and
import random def constrained_sum_sample_pos(n, total): """Return a randomly chosen list of n positive integers summing to total. Each such list is equally likely to occur.""" dividers = sorted(random.sample(range(1, total), n - 1)) return [a - b for a, b in zip(dividers + [total], [0] + dividers)]
Sample outputs:
>>> constrained_sum_sample_pos(4, 40) [4, 4, 25, 7] >>> constrained_sum_sample_pos(4, 40) [9, 6, 5, 20] >>> constrained_sum_sample_pos(4, 40) [11, 2, 15, 12] >>> constrained_sum_sample_pos(4, 40) [24, 8, 3, 5]
Explanation: there's a one-to-one correspondence between (1) 4-tuples (a, b, c, d)
of positive integers such that a + b + c + d == 40
, and (2) triples of integers (e, f, g)
with 0 < e < f < g < 40
, and it's easy to produce the latter using random.sample
. The correspondence is given by (e, f, g) = (a, a + b, a + b + c)
in one direction, and (a, b, c, d) = (e, f - e, g - f, 40 - g)
in the reverse direction.
If you want nonnegative integers (i.e., allowing 0
) instead of positive ones, then there's an easy transformation: if (a, b, c, d)
are nonnegative integers summing to 40
then (a+1, b+1, c+1, d+1)
are positive integers summing to 44
, and vice versa. Using this idea, we have:
def constrained_sum_sample_nonneg(n, total): """Return a randomly chosen list of n nonnegative integers summing to total. Each such list is equally likely to occur.""" return [x - 1 for x in constrained_sum_sample_pos(n, total + n)]
Graphical illustration of constrained_sum_sample_pos(4, 10)
, thanks to @FM. (Edited slightly.)
0 1 2 3 4 5 6 7 8 9 10 # The universe. | | # Place fixed dividers at 0, 10. | | | | | # Add 4 - 1 randomly chosen dividers in [1, 9] a b c d # Compute the 4 differences: 2 3 4 1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With