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