repetir - import random python
Genera múltiples números aleatorios para igualar un valor en python (6)
Aquí está la solución estándar. Es similar a la respuesta de Laurence Gonsalves, pero tiene dos ventajas sobre esa respuesta. (1) Es uniforme: cada combinación de 4 enteros positivos sumando hasta 40 es igualmente probable que presente este esquema, y (2) es fácil de adaptar a otros totales (7 números que suman 100, etc.):
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(xrange(1, total), n - 1))
return [a - b for a, b in zip(dividers + [total], [0] + dividers)]
Resultados de muestra:
>>> 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]
Explicación: hay una correspondencia uno a uno entre (1) 4-tuplas (a, b, c, d)
de enteros positivos tales que a + b + c + d == 40
y (2) triples de enteros (e, f, g)
con 0 < e < f < g < 40
, y es fácil producir el último usando random.sample
. La correspondencia viene dada por (e, f, g) = (a, a + b, a + b + c)
en una dirección, y (a, b, c, d) = (e, f - e, g - f, 40 - g)
en la dirección inversa.
Si desea enteros no negativos (es decir, permitir 0
) en lugar de positivos, entonces hay una transformación fácil: si (a, b, c, d)
son enteros no negativos que suman a 40
entonces (a+1, b+1, c+1, d+1)
son números enteros positivos que suman 44
y viceversa. Usando esta idea, tenemos:
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)]
Ilustración gráfica de constrained_sum_sample_pos(4, 10)
, gracias a @FM. (Editado levemente)
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
Así que aquí está el trato: quiero (por ejemplo) generar 4 números pseudoaleatorios, que cuando se agregan juntos equivalen a 40. ¿Cómo podría ser esto una cúpula en Python? Podría generar un número aleatorio 1-40, luego generar otro número entre 1 y el resto, etc., pero luego el primer número tendría una mayor posibilidad de "agarrar" más.
Genera 4 números aleatorios, calcula su suma, divide cada uno por la suma y multiplica por 40.
Si quieres enteros, entonces esto requerirá un poco de no aleatoriedad.
Solo hay 37 ^ 4 = 1,874,161 arreglos de cuatro enteros en el rango [1,37] (con repeticiones permitidas). Enumérelos, guardando y contando las permutaciones que suman hasta 40. (Este será un número mucho más pequeño, N).
Dibuja enteros aleatorios distribuidos uniformemente K en el intervalo [0, N-1] y devuelve la permutación K-ésima. Esto se puede ver fácilmente para garantizar una distribución uniforme en el espacio de posibles resultados, con cada posición de secuencia distribuida de forma idéntica. (¡Muchas de las respuestas que estoy viendo tendrán la elección final sesgada más baja que las tres primeras!)
Suponiendo que desea que se distribuyan de manera uniforme, y suponiendo que no desea repeticiones
addends = []
picks = range(1, 34)
while sum(addends) != 40:
addends = random.sample(picks, 3)
if sum(addends) > 39:
continue
addends.append(40 - sum(addends))
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]
(Supongo que querías enteros ya que dijiste "1-40", pero esto podría generalizarse fácilmente para las carrozas).
Así es como funciona:
- cortar el rango total en dos aleatoriamente, eso es b. El rango impar es porque habrá al menos 2 debajo del punto medio y al menos 2 arriba. (Esto viene de su mínimo 1 en cada valor).
- cortar cada uno de esos rangos en dos aleatoriamente. De nuevo, los límites son para dar cuenta del 1 mínimo.
- devuelve el tamaño de cada rebanada. Ellos sumarán hasta 40.
from numpy.random import multinomial
multinomial(40, [1/4.] * 4)