Muestra una secuencia aleatoria no decreciente

20

Entrada: dos enteros n y k dados en cualquier forma que sea conveniente para su código

Salida Una secuencia aleatoria no decreciente de k enteros, cada uno en el rango de 1 a n. La muestra debe elegirse uniformemente entre todas las secuencias no decrecientes de k enteros con enteros en el rango de 1 a n.

La salida puede estar en cualquier formato razonable que considere conveniente.

Puede usar cualquier generador pseudoaleatorio que proporcione su biblioteca / idioma favorito.

Podemos suponer que los enteros n, k> 0.

Ejemplo

Digamos n, k = 2. Las secuencias no decrecientes son

1,1
1,2
2,2

Cada secuencia debe tener una probabilidad de 1/3 de salida.

Restricción

Su código debe ejecutarse en no más de unos segundos para k = 20 yn = 100.

Lo que no funciona

Si solo muestra cada número entero al azar del rango 1 a ny luego ordena la lista, no obtendrá una distribución uniforme.

DJMcMayhem
fuente
La salida del número de secuencias no decrecientes para n, k podría ser un desafío interesante por sí solo, si aún no se ha hecho ...
ETHproductions
1
@ETHproductions No realmente, es solo un binomio (relacionado con esto )
Sp3000
@ Sp3000 Ah, está bien. Sin embargo, fue un desafío divertido para mí descubrir cómo calcularlo de manera eficiente.
ETHproductions
Su requisito de que cada secuencia tenga la misma probabilidad de salida no es posible para satisfacer la mayoría de los PRNG de variedades de jardín que pueden tener solo 32 o 48 bits. Según Wolfram, hay 535 quintillones de subsecuencias de 20 elementos de 1, ..., 100 (no comprobé cuántos de ellos no disminuyen). 2 ^ 64 es solo 18 quintillones.
Sinan Ünür

Respuestas:

1

En realidad , 14 12 bytes

Esta respuesta se basa en respuesta 05AB1E de Emigna y las respuestas en esta pregunta Math.SE . Sugerencias de golf bienvenidas! Pruébalo en línea!

;;ra+DR╚HS♀-

Ungolfing

      Implicit input n, then k.
;;    Duplicate k twice.
r     Push range [0...k] for later.
a     Invert the stack. Stack: n, k, k, [0...k]
+DR   Push the range [1..n+k-1].
╚     Shuffle the range. Stack: shuffled_range, k, [0...k]
H     Push the first k elements of shuffled_range. Call this increasing.
S     Sort increasing so the elements are actually increasing.
♀-    Subtract each element of [0...k] from each element of increasing.
      This gives us our non-decreasing sequence.
      Implicit return.
Sherlock9
fuente
13

Python, 89 bytes

from random import*
lambda n,k:[x-i for i,x in enumerate(sorted(sample(range(1,n+k),k)))]

Generar una secuencia creciente en lugar de una no decreciente sería sencillo: esto es solo un subconjunto aleatorio de knúmeros entre 1y n, ordenados.

Pero, podemos convertir una secuencia creciente en una no decreciente reduciendo cada espacio entre números consecutivos en 1. Entonces, un espacio de 1 se convierte en un espacio de 0, haciendo números iguales. Para hacerlo, disminuya el i'valor más grande eni

r[0], r[1], ..., r[n-1]  =>  r[0]-0, r[1]-1, ..., r[n-1]-(n-1)

Para que el resultado sea de 1a n, la entrada debe ser de 1a n+k-1. Esto proporciona una biyección entre secuencias no decrecientes de números entre 1y n, a secuencias crecientes entre 1y n+k-1. La misma biyección se usa en el argumento de barras y estrellas para contar tales secuencias.

El código usa la función python random.sample, que toma kmuestras sin reemplazo de la lista de entrada. Ordenarlo da la secuencia creciente.

xnor
fuente
Esto es impresionante ¿Podría agregar una explicación del método por favor?
Sí, ocupado ahora, explicaré más tarde.
xnor
Conté 90 bytes ... (y también puedes import*ahorrar 1 byte)
Rod
@ Rod Gracias, me olvidé de eso.
xnor
7

05AB1E , 13 bytes

+<L.r¹£{¹L<-Ä

Pruébalo en línea!

Explicación

+<L            # range [1 ... n+k-1]
   .r          # scramble order
     ¹£        # take k numbers
       {       # sort
        ¹L<-   # subtract from their 0-based index
            Ä  # absolute value
Emigna
fuente
7

Python, 87 bytes

from random import*
f=lambda n,k:k>random()*(n+k-1)and f(n,k-1)+[n]or k*[7]and f(n-1,k)

La probabilidad de que nse incluya el valor máximo posible es igual k/(n+k-1). Para incluirlo, colóquelo al final de la lista y disminuya los números necesarios restantes k. Para excluirlo, disminuya el límite superior n. Luego, recurse hasta que no se necesiten más valores ( k==0).

Python randomno parece tener incorporado una variable de Bernoulli: 1 con alguna probabilidad, y 0 en caso contrario. Entonces, esto verifica si un valor aleatorio entre 0 y 1 generado por randomcae por debajo k/(n+k-1). Python 2 sería la relación como la división de flotación, por lo que en su lugar se multiplican por el denominador: k>random()*(n+k-1).

xnor
fuente
¿Numpy ayudaría aquí?
@Lembik Buen pensamiento, pero parece que tendría que importar numpy.random, que es demasiado largo.
xnor
5

JavaScript (Firefox 30+), 74 bytes

(n,k,i=0,j=k)=>[for(_ of Array(q=k+n-1))if(Math.random(++i)<k/q--)i-j+k--]

Explicación

La excelente respuesta de Python de xnor contiene un muy buen resumen de cómo / por qué funciona la técnica utilizada aquí. El primer paso es crear el rango [1, 2, ..., n + k - 1] :

(n,k,i=0)=>[for(_ of Array(q=k+n-1))++i]

A continuación, debemos tomar k elementos aleatorios de este rango. Para hacer esto, debemos seleccionar cada elemento con probabilidad s / q , donde s es la cantidad de elementos que aún se necesitan y q es la cantidad de elementos que quedan en el rango. Como estamos usando una comprensión de matriz, esto es bastante fácil:

(n,k,i=0)=>[for(_ of Array(q=k+n-1))if(Math.random(++i)<k/q--)k--&&i]

Esto nos da una secuencia creciente de números distribuidos uniformemente . Esto se puede solucionar restando el número de elementos j que hemos encontrado previamente:

(n,k,i=0,j=0)=>[for(_ of Array(q=k+n-1))if(Math.random(++i)<k/q--)k--&&i-j++]

Finalmente, almacenando k en j , podemos incorporar k--a la expresión y guardar algunos bytes:

(n,k,i=0,j=k)=>[for(_ of Array(q=k+n-1))if(Math.random(++i)<k/q--)i-j+k--]
ETHproducciones
fuente
2

TI-BASIC, 54 bytes

Prompt N,K
K→dim(L1
While K
If rand<K/(N+K-1
Then
N→L1(K
K-1→K
Else
N-1→N
End
End
Disp L1

Sigue la lógica de xnor, con una pequeña advertencia. Teóricamente podríamos afeitar un byte haciendo algo como esto:

K>rand(N+K-1

Pero rand (está reservado para crear una lista de números aleatorios, por lo que no podríamos hacer la multiplicación implícita deseada para guardar un byte.

Esto debería ejecutarse decentemente rápido en un 84+ por restricción, pero lo comprobaré para asegurarme cuando pueda.

TiKevin83
fuente
1

PHP, 77 75 73 bytes

foreach(array_rand(range(2,$argv[1]+$k=$argv[2]),$k)as$v)echo$v+1-$i++,_;

Corre así:

php -r 'foreach(array_rand(range(2,$argv[1]+$k=$argv[2]),$k)as$v)echo$v+1-$i++,_;' -- 10 5 2>/dev/null;echo
> 1_4_6_9_9_

Explicación

foreach(                    # Iterate over...
  array_rand(               #   a (sorted) random number of items from...
    range(                  #     an array with items...
      2,                    #       from 2
      $argv[1]+$k=$argv[2]  #       to n + k (set arg 2 to $k)
    ),
    $k                      #     Take k number of items (their keys)
  )
  as $v
)
  echo $v +1 - $i++,"_";    # Print the value subtracted by the index.
                            # Need to add 1, because keys are 0-indexed.

Ajustes

  • Ahorró 2 bytes al eliminar la end()llamada y establecer $argv[2]en$k lugar para acortar el acceso a los argumentos
  • Ahorró 2 bytes al eliminar el índice del foreach, ya que es simplemente un número incremental. Simplemente incremente $icada iteración
aross
fuente
Primero JavaScript y ahora PHP. Todos los mejores lenguajes de programación científica :) Gracias.
@Lembik, de nada. Eso sí, utiliza un PRNG básico. No lo use para cosas criptográficas . :)
Aross