Experimente uma sequência aleatória não decrescente


20

Entrada: Dois números inteiros n e k, fornecidos de qualquer forma que seja conveniente para o seu código

Saída Uma sequência aleatória não decrescente de k números inteiros, cada um no intervalo de 1 a n. A amostra deve ser escolhida uniformemente entre todas as seqüências não decrescentes de k números inteiros com números inteiros no intervalo de 1 a n.

A saída pode estar em qualquer formato razoável que você achar conveniente.

Você pode usar qualquer gerador pseudo-aleatório que sua biblioteca / idioma favorito fornecer.

Podemos assumir que os números inteiros n, k> 0.

Exemplo

Diga n, k = 2. As seqüências não decrescentes são

1,1
1,2
2,2

Cada sequência deve ter probabilidade de 1/3 de ser emitida.

Restrição

Seu código deve ser executado em apenas alguns segundos para k = 20 en = 100.

O que não funciona

Se você apenas amostrar cada número inteiro aleatoriamente do intervalo de 1 a n e classificar a lista, não obterá uma distribuição uniforme.


Produzir o número de sequências para n não-decrescente, k pode fazer um desafio interessante em seu próprio, se ele já não foi feito ...
ETHproductions

1
@ETHproductions Não, na verdade, é apenas um binômio (relacionado a isso ) #
SP3000 Sp3000

@ Sp3000 Ah, tudo bem. Foi um desafio divertido para mim descobrir como calculá-lo com eficiência.
ETHproductions

Seu requisito de que cada sequência tenha a mesma probabilidade de saída não é possível satisfazer com a maioria dos PRNGs de variedades de jardins que podem ter apenas estados de 32 ou 48 bits. De acordo com Wolfram, existem 535 quintilhões de 20 subsequências de elementos de 1, ..., 100 (não verificou quantas delas não estão diminuindo). 2 ^ 64 é apenas 18 quintilhões.
Sinan Ünür 18/11/16

Respostas:


1

Na realidade , 14 12 bytes

Esta resposta é baseada na resposta 05AB1E de Emigna e as respostas sobre esta questão Math.SE . Sugestões de golfe são bem-vindas! Experimente online!

;;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.

13

Python, 89 bytes

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

Gerar uma sequência crescente em vez de uma não decrescente seria simples: este é apenas um subconjunto aleatório de knúmeros entre1 e n, classificado.

Porém, podemos converter uma sequência crescente em uma não decrescente, reduzindo cada intervalo entre números consecutivos em 1. Portanto, um intervalo de 1 se torna um intervalo de 0, formando números iguais. Para fazer isso, diminua o idécimo maior valor emi

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

Para que o resultado seja de 1para n, a entrada deve ser de 1para n+k-1. Isso fornece uma bijeção entre seqüências não decrescentes de números entre 1e n, para aumentar sequências entre 1e n+k-1. A mesma bijeção é usada no argumento de estrelas e barras para contar essas seqüências.

O código usa a função python random.sample, que coleta kamostras sem substituição da lista de entrada. A classificação fornece a sequência crescente.


Isso é impressionante. Você poderia adicionar uma explicação do método, por favor?

Sim, ocupado agora, irá explicar mais tarde.
Xnor

Eu contei 90 bytes ... (e você também pode import*salvar 1 byte) #
187 Rod Rod

@ Rod Obrigado, eu esqueci disso.
Xnor

7

05AB1E , 13 bytes

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

Experimente online!

Explicação

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

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)

A probabilidade de que o valor máximo possível nseja incluído é igual k/(n+k-1). Para incluí-lo, coloque-o no final da lista e diminua os números necessários restantes k. Para excluí-lo, diminua o limite superior n. Em seguida, recorra até que não sejam necessários mais valores (k==0 ).

O Python randomnão parece ter um built-in para uma variável Bernoulli: 1 com alguma probabilidade e 0 caso contrário. Portanto, isso verifica se um valor aleatório entre 0 e 1 gerado por randomcai abaixo k/(n+k-1). Python 2 seria a razão como a divisão float, por isso, em vez se multiplicam pelo denominador: k>random()*(n+k-1).


O numpy ajudaria aqui?

@Lembik Bom pensamento, mas parece que você teria que importar numpy.random, o que é muito longo.
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--]

Explicação

A excelente resposta Python do xnor contém um resumo muito bom de como / por que a técnica usada aqui funciona. O primeiro passo é criar o intervalo [1, 2, ..., n + k - 1] :

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

Em seguida, precisamos pegar k itens aleatórios desse intervalo. Para fazer isso, precisamos selecionar cada item com probabilidade s / q , onde s é o número de itens ainda necessários e q é o número de itens restantes no intervalo. Como estamos usando uma compreensão de array, isso é bastante fácil:

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

Isso nos dá uma sequência crescente de números uniformemente distribuída . Isso pode ser corrigido subtraindo o número de itens j que encontramos anteriormente:

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

Finalmente, armazenando k em j , podemos incorporar k--na expressão e salvar alguns bytes:

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

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

Siga a lógica do xnor, com uma pequena ressalva. Teoricamente, podemos barbear um byte fazendo algo assim:

K>rand(N+K-1

Mas rand (é reservado para criar uma lista de números aleatórios, portanto, não poderíamos fazer a multiplicação implícita desejada para salvar um byte.

Isso deve ser executado decentemente rápido em um 84+ por restrição, mas vou verificar se posso.


1

PHP, 77 75 73 bytes

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

Execute assim:

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_

Explicação

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.

Tweaks

  • Salva 2 bytes através da remoção de end()chamada e conjunto $argv[2]para $k, em vez de acesso encurtar a argumentos
  • Economizou 2 bytes removendo o índice do foreach, pois é simplesmente um número crescente. Apenas incremente $icada iteração

Primeiro JavaScript e agora PHP. Todas as melhores linguagens de programação científica :) Obrigado.

@Lembik, de nada. Lembre-se, ele usa um PRNG básico. Não use para coisas criptográficas . :)
aross
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.