ランダムな非減少シーケンスをサンプリングする


20

入力:コードに便利な任意の形式で指定された2つの整数nおよびk

出力 1〜nの範囲にあるk個の整数のランダムで減少しないシーケンス。サンプルは、1〜nの範囲の整数を持つk個の整数のすべての非減少シーケンスから均一に選択する必要があります。

出力は、便利な任意の妥当な形式にすることができます。

お気に入りのライブラリ/言語が提供する擬似ランダムジェネレータを使用できます。

整数n、k> 0と仮定できます。

n、k = 2と言います。非減少シーケンスは

1,1
1,2
2,2

各シーケンスには、出力される確率の1/3が必要です。

制限

コードは、k = 20およびn = 100の場合、数秒以内に実行する必要があります。

動作しないもの

1からnの範囲の各整数をランダムにサンプリングし、リストを並べ替えるだけでは、均一な分布は得られません。


n、kの非減少シーケンスの数を出力することは、それがまだ行われていない場合、それ自体で興味深い挑戦をするかもしれません
...-ETHproductions

1
@ETHproductions実際には、これは(これに関連する)単なる二項式です
Sp3000

@ Sp3000ああ、OK。しかし、それを効率的に計算する方法を理解することは私にとって楽しい挑戦でした。
ETHproductions

各シーケンスが出力される確率が等しいという要件は、32ビットまたは48ビットの状態しか持たないほとんどの園芸品種のPRNGでは満たすことができません。Wolframによると、1、...、100の535の20個の要素サブシーケンスがあります(それらの数が減少していないことを確認しませんでした)。2 ^ 64はわずか18クインティオンです。
シナンÜnür16年

回答:


1

実際には14 12バイト

この回答は、エミグナの05AB1Eの回答このMath.SEの質問に対する回答に基づいています。ゴルフの提案を歓迎します!オンラインでお試しください!

;;ra+DR╚HS♀-

アンゴルフ

      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バイト

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

減少ないシーケンスではなく、増加するシーケンスを生成するのは簡単です。これは、との間のランダムな数のサブセットで、ソートされています。k1n

ただし、連続する数値間の各ギャップを1ずつ縮小することで、増加するシーケンスを非減少シーケンスに変換できます。したがって、1のギャップは0のギャップになり、等しい数になります。そのためには、i'番目に大きい値をi

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

以下からのものであるとの結果のため1n、入力からでなければならない1n+k-1。これは、非減少との間の数値の配列の間の全単射与える1nの間の配列を増加する、1およびn+k-1。このようなシーケンスをカウントするために、スターとバーの引数で同じ全単射が使用されます。

コードはpython関数を使用しますrandom.sample。この関数はk、入力リストから置換せずにサンプルを取得します。それをソートすると、増加するシーケンスが得られます。


これは印象的です。メソッドの説明を追加してください。

うん、今忙しい、後で説明します。
xnor

90バイトをカウントしました...(import*1バイトを節約することもできます)
ロッド

@Rodありがとう、私はそれを忘れていました。
-xnor


7

Python、87バイト

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)

最大可能値nが含まれる確率はに等しくなりk/(n+k-1)ます。それを含めるには、それをリストの最後に置き、必要な数を減らしますk。除外するには、上限をデクリメントしますn。次に、値が不要になるまで再帰します(k==0)。

Python randomにはベルヌーイ変数の組み込み機能がないようです。1は何らかの確率で、それ以外は0です。そのため、によって生成された0〜1のランダムな値randomがを下回るかどうかをチェックしますk/(n+k-1)。Python 2は比率を浮動小数点除算として計算するため、代わりに分母を乗算しますk>random()*(n+k-1)


numpyはここで役立ちますか?

@Lembik良い考えですが、インポートする必要があるように見えますがnumpy.random、これは長すぎます。
-xnor

5

JavaScript(Firefox 30 +)、74バイト

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

説明

xnorの優れたPythonの回答には、ここで使用されている手法がどのように/なぜ機能するかの非常に良い要約が含まれています。最初のステップは、範囲[1、2、...、n + k-1]を作成することです。

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

次に、この範囲からk個のランダムアイテムを取得する必要があります。これを行うには、確率s / qで各アイテムを選択する必要があります。ここで、sはまだ必要なアイテムの数で、qは範囲内に残っているアイテムの数です。配列内包表記を使用しているため、これはかなり簡単です。

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

これにより、一様に分布する増加する数列が得られます。これは、以前に見つけたアイテムjの数を引くことで修正できます。

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

最後に、jkを格納することにより、式に組み込み、数バイトを節約できます。k--

(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バイト

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

次のxnorのロジックを少し注意してください。理論的には、次のようなことを行うことでバイトを削ることができます。

K>rand(N+K-1

ただし、rand(は乱数のリストを作成するために予約されているため、バイトを節約するために必要な暗黙の乗算を行うことはできません。

これは、制限ごとに84+でかなり高速に実行されるはずですが、可能な場合は確認します。


1

PHP、77 75 73バイト

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

次のように実行します:

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_

説明

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.

微調整

  • end()呼び出しを削除$argv[2]し、$k代わりにに設定して2バイトを保存し、引数へのアクセスを短縮しました
  • foreachからインデックスを削除することで2バイトを節約しました。これは単純にインクリメントする数値だからです。$i代わりに各反復をインクリメントするだけです

最初のJavaScript、そして今はPHP。すべての最高の科学プログラミング言語:)ありがとう。

@Lembik、どういたしまして。気をつけて、それは基本的なPRNGを使用しています。暗号化に使用しないでください。:)
アロス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.