Campionare una sequenza casuale non decrescente


20

Input: due numeri interi n e k indicati in qualsiasi forma che sia conveniente per il tuo codice

Output Una sequenza casuale non decrescente di k numeri interi, ciascuno compreso tra 1 e n. Il campione deve essere scelto in modo uniforme tra tutte le sequenze non decrescenti di k numeri interi con numeri interi compresi tra 1 e n.

L'output può essere in qualsiasi formato ragionevole che ritieni conveniente.

Puoi usare qualsiasi generatore pseudo-casuale fornito dalla tua libreria / lingua preferita.

Possiamo supporre che gli interi n, k> 0.

Esempio

Di 'n, k = 2. Le sequenze non decrescenti sono

1,1
1,2
2,2

Ogni sequenza dovrebbe avere probabilità 1/3 di essere emessa.

Restrizione

Il codice dovrebbe essere eseguito in non più di qualche secondo per k = 20 e n = 100.

Cosa non funziona

Se si campiona casualmente ogni numero intero nell'intervallo da 1 a n e quindi si ordina l'elenco non si otterrà una distribuzione uniforme.


Presentare il numero di sequenze non decrescenti per n, k potrebbe rappresentare una sfida interessante da solo, se non è già stato fatto ...
ETHproductions

1
@ETHproductions Non proprio, è solo un binomio (correlato a questo )
Sp3000,

@ Sp3000 Ah, OK. Per me è stata una sfida divertente capire come calcolarla in modo efficiente.
ETHproductions

Il tuo requisito che ogni sequenza abbia la stessa probabilità di essere emesso non è possibile soddisfare con la maggior parte dei PRNG di varietà da giardino che possono avere solo 32 o 48 bit. Secondo Wolfram, ci sono 535 quintilioni di 20 elementi secondari di 1, ..., 100 (non ho verificato quanti di questi non diminuiscono). 2 ^ 64 sono solo 18 quintilioni.
Sinan Ünür

Risposte:


1

In realtà , 14 12 byte

Questa risposta si basa sulla risposta 05AB1E di Emigna e le risposte su questo argomento Math.SE . Suggerimenti di golf benvenuti! Provalo 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 byte

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

Generare una sequenza crescente piuttosto che una non decrescente sarebbe semplice: questo è solo un sottoinsieme casuale di knumeri tra 1e n, ordinati.

Tuttavia, possiamo convertire una sequenza crescente in una non decrescente riducendo ogni spazio tra i numeri consecutivi di 1. Quindi, uno spazio di 1 diventa uno spazio di 0, creando numeri uguali. Per fare ciò, diminuisci il ivalore più grande dii

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

Affinché il risultato sia da 1a n, l'input deve essere da 1a n+k-1. Questo dà una biiezione tra sequenze non decrescenti di numeri tra 1e n, a sequenze crescenti tra 1e n+k-1. La stessa biiezione viene utilizzata nell'argomento stelle e barre per contare tali sequenze.

Il codice utilizza la funzione python random.sample, che preleva kcampioni senza sostituzione dall'elenco di input. L'ordinamento dà la sequenza crescente.


Questo è impressionante. Potresti aggiungere una spiegazione del metodo per favore?

Sì, occupato ora, spiegherò più tardi.
xnor

Ho contato 90 byte ... (e puoi anche import*salvare 1 byte)
Rod

@Rod Grazie, me ne sono dimenticato.
xnor

7

05AB1E , 13 byte

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

Provalo online!

Spiegazione

+<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 byte

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 probabilità che nsia incluso il valore massimo possibile è uguale k/(n+k-1). Per includerlo, inseriscilo alla fine dell'elenco e decrementa i numeri necessari rimanenti k. Per escluderlo, decrementa il limite superiore n. Quindi, ricorrere fino a quando non sono necessari più valori ( k==0).

Python's randomnon sembra avere un built-in per una variabile di Bernoulli: 1 con qualche probabilità e 0 altrimenti. Quindi, questo controlla se un valore casuale compreso tra 0 e 1 generato da randomscende al di sotto k/(n+k-1). Pitone 2 sarebbe il rapporto di divisione come float, quindi abbiamo invece moltiplicare per il denominatore: k>random()*(n+k-1).


Intorpidimento sarebbe d'aiuto qui?

@Lembik Buona idea, ma sembra che dovresti importare numpy.random, che è troppo lungo.
xnor

5

JavaScript (Firefox 30+), 74 byte

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

Spiegazione

L'eccellente risposta di Python di xnor contiene un ottimo riassunto di come / perché la tecnica usata qui funziona. Il primo passo è creare l'intervallo [1, 2, ..., n + k - 1] :

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

Quindi dobbiamo prendere k oggetti casuali da questo intervallo. Per fare ciò, dobbiamo selezionare ogni articolo con probabilità s / q , dove s è il numero di elementi ancora necessari e q è il numero di elementi rimasti nell'intervallo. Dal momento che stiamo usando una comprensione dell'array, questo è abbastanza facile:

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

Questo ci dà una sequenza crescente di numeri uniformemente distribuiti . Questo può essere risolto sottraendo il numero di elementi j che abbiamo trovato in precedenza:

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

Infine, memorizzando k in j , possiamo incorporare k--nell'espressione e salvare alcuni byte:

(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 byte

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

Segui la logica di xnor, con un piccolo avvertimento. Potremmo teoricamente radere un byte facendo qualcosa del genere:

K>rand(N+K-1

Ma Rand (è riservato per creare un elenco di numeri casuali, quindi non saremmo in grado di fare la moltiplicazione implicita desiderata per salvare un byte.

Questo dovrebbe funzionare abbastanza velocemente su un 84+ per la restrizione, ma controllerò per assicurarmi quando posso.


1

PHP, 77 75 73 byte

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

Esegui in questo modo:

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_

Spiegazione

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.

Ritocchi

  • Salvato 2 byte rimuovendo la end()chiamata e impostandolo $argv[2]su $kinvece per abbreviare l'accesso agli argomenti
  • Salvato 2 byte rimuovendo l'indice dal foreach, poiché è semplicemente un numero crescente. Basta incrementare $iogni iterazione invece

Prima JavaScript e ora PHP. Tutti i migliori linguaggi di programmazione scientifica :) Grazie.

@Lembik, prego. Intendiamoci, utilizza un PRNG di base. Non utilizzare per materiale crittografico . :)
circa il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.