Genere 3 números aleatorios que sumen 1 en R

Espero crear 3 números cuasialeatorios (no negativos) que sumen uno y se repitan una y otra vez.

Básicamente, estoy tratando de dividir algo en tres partes aleatorias durante muchas pruebas.

Mientras soy consciente de

a = runif(3,0,1)

Estaba pensando que podría usar 1-a como el máximo en el próximo runif, pero parece desordenado.

Pero estos, por supuesto, no suman uno. ¿Alguna idea, oh sabios stackoverflow-ers?

preguntado el 12 de junio de 12 a las 21:06

¿Es una opción volver a normalizar los números aleatorios después de la generación? -

¿Qué tal generar 2 números aleatorios a y b? Entonces a+b+c = 1 => c = 1 - (a+b) -

y si a y b suman más de 1? -

¿Qué distribución quieres en los intervalos? Por ejemplo, ¿debería elegirse uniformemente la longitud del primer intervalo en (0,1)? -

El problema que tienes parece llamarse "división aleatoria de un intervalo". Es un problema clásico en estadística y buscar en Google produce imágenes sorprendentemente pequeñas y bonitas... -

6 Respuestas

Esta pregunta implica cuestiones más sutiles de lo que podría parecer a primera vista. Después de ver lo siguiente, es posible que desee pensar detenidamente en el proceso que está utilizando para representar estos números:

## My initial idea (and commenter Anders Gustafsson's):
## Sample 3 random numbers from [0,1], sum them, and normalize
jobFun <- function(n) {
    m <- matrix(runif(3*n,0,1), ncol=3)
    m<- sweep(m, 1, rowSums(m), FUN="/")
    m
}

## Andrie's solution. Sample 1 number from [0,1], then break upper 
## interval in two. (aka "Broken stick" distribution).
andFun <- function(n){
  x1 <- runif(n)
  x2 <- runif(n)*(1-x1)
  matrix(c(x1, x2, 1-(x1+x2)), ncol=3)
}

## ddzialak's solution (vectorized by me)
ddzFun <- function(n) {
    a <- runif(n, 0, 1)
    b <- runif(n, 0, 1)
    rand1 = pmin(a, b)
    rand2 = abs(a - b)
    rand3 = 1 - pmax(a, b)
    cbind(rand1, rand2, rand3)
}

## Simulate 10k triplets using each of the functions above
JOB <- jobFun(10000)
AND <- andFun(10000)
DDZ <- ddzFun(10000)

## Plot the distributions of values
par(mfcol=c(2,2))
hist(JOB, main="JOB")
hist(AND, main="AND")
hist(DDZ, main="DDZ")

enter image description here

Respondido el 12 de junio de 12 a las 21:06

Genial, estaba pensando en graficar los resultados pero ya lo hiciste. Es interesante ver que aparentemente ninguna de las soluciones realmente hace lo que a uno le hubiera gustado intuitivamente. También es interesante que en estas gráficas realmente no se puede ver que DDZ hace lo correcto según los medios, mientras que AND ni siquiera hace eso. - cristiano

solo al azar 2 dígitos de (0, 1) y si se asume que es a y b entonces tienes:

rand1 = min(a, b)
rand2 = abs(a - b)
rand3 = 1 - max(a, b)

Respondido el 12 de junio de 12 a las 21:06

Además, debe repetir la generación del segundo número si a == b ... (debería ser un caso MUY raro) - ddzialak

@usuario entonces a=0.85, b=0.99 entonces obtuviste números: 0.85, 0.14, 0.01 (en cuanto a mí estos son muy buenos 3 números aleatorios de 0..1) - ddzialak

La distribución resultante parece no ser exactamente trivial: jstor.org/discover/10.2307/… y un documento posterior al que se puede acceder libremente doc.utwente.nl/70657/1/Sleutel67random.pdf - cristiano

Cuando desee generar aleatoriamente números que sumen 1 (o algún otro valor), debe mirar el Distribución de Dirichlet.

Hay un rdirichlet en función de la gtools paquete y funcionando RSiteSearch('Dirichlet') presenta bastantes resultados que podrían conducirlo fácilmente a herramientas para hacer esto (y no es difícil codificar a mano tampoco para distribuciones simples de Dirichlet).

Respondido el 12 de junio de 12 a las 22:06

Supongo que depende de la distribución que desee en los números, pero aquí hay una forma:

diff(c(0, sort(runif(2)), 1))

Utiliza replicate para obtener tantos juegos como quieras:

> x <- replicate(5, diff(c(0, sort(runif(2)), 1)))
> x
           [,1]       [,2]      [,3]      [,4]       [,5]
[1,] 0.66855903 0.01338052 0.3722026 0.4299087 0.67537181
[2,] 0.32130979 0.69666871 0.2670380 0.3359640 0.25860581
[3,] 0.01013117 0.28995078 0.3607594 0.2341273 0.06602238
> colSums(x)
[1] 1 1 1 1 1

Respondido el 12 de junio de 12 a las 21:06

Simplemente seleccionaría al azar 3 números de una distribución uniforme y luego los dividiría por su suma. Codifique como se muestra a continuación.

n <- 3
x <- runif(3, 0, 1)
y <- x/sum(x)
sum(y)== 1

n podría ser cualquier número que desee.

Respondido el 21 de Septiembre de 18 a las 16:09

Este problema y las diferentes soluciones propuestas me intrigaron. Hice una pequeña prueba de los tres algoritmos básicos sugeridos y qué valores promedio arrojarían para los números generados.

choose_one_and_divide_rest
means:                [ 0.49999212  0.24982403  0.25018384]
standard deviations:  [ 0.28849948  0.22032758  0.22049302]
time needed to fill array of size 1000000 was 26.874945879 seconds

choose_two_points_and_use_intervals
means:                [ 0.33301421  0.33392816  0.33305763]
standard deviations:  [ 0.23565652  0.23579615  0.23554689]
time needed to fill array of size 1000000 was 28.8600130081 seconds

choose_three_and_normalize
means:                [ 0.33334531  0.33336692  0.33328777]
standard deviations:  [ 0.17964206  0.17974085  0.17968462]
time needed to fill array of size 1000000 was 27.4301018715 seconds

Las medidas de tiempo deben tomarse con cautela, ya que pueden estar más influenciadas por la gestión de la memoria de Python que por el propio algoritmo. Soy demasiado perezoso para hacerlo correctamente con timeit. Hice esto en Atom de 1 GHz, eso explica por qué tomó tanto tiempo.

De todos modos, choose_one_and_divide_rest es el algoritmo sugerido por Andrie y el propio cartel de la pregunta (Y DE): eliges un valor a en [0,1], luego uno en [a,1] y luego miras lo que te queda. Suma uno pero eso es todo, la primera división es el doble de grande que las otras dos. Uno podría haber adivinado tanto ...

elegir_dos_puntos_y_usar_intervalos es la respuesta aceptada por ddzialak (DDZ). Toma dos puntos en el intervalo [0,1] y usa el tamaño de los tres subintervalos creados por estos puntos como los tres números. Funciona de maravilla y los medios son todos 1/3.

elegir_tres_y_normalizar es la solución de Anders Gustafsson y Josh O'Brien (TRABAJO). Simplemente genera tres números en [0,1] y los normaliza a una suma de 1. Funciona igual de bien y sorprendentemente un poco más rápido en mi implementación de Python. La varianza es un poco menor que para la segunda solución.

Ahí tienes. No tengo idea de a qué distribución beta corresponden estas soluciones o qué conjunto de parámetros en el documento correspondiente al que me referí en un comentario, pero tal vez alguien más pueda resolverlo.

contestado el 23 de mayo de 17 a las 13:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.