交換なしのランダムサンプリング


10

範囲から引き出された一連の個別の乱数を出力する関数を作成します。セット内の要素の順序は重要ではありません(ソートすることもできます)が、関数が呼び出されるたびにセットの内容が異なる可能性がある必要があります。

この関数は、3つのパラメーターを任意の順序で受け取ります。

  1. 出力セットの数のカウント
  2. 下限(両端を含む)
  3. 上限(両端を含む)

すべての数値が0(両端を含む)から2 31(両端を含まない)の範囲の整数であると想定します。出力は任意の方法で返すことができます(コンソールに書き込む、配列としてなど)。

審査

基準には3つのRが含まれます

  1. ランタイム -クワッドコアWindows 7マシンでテストされ、自由にまたは簡単に利用できるコンパイラーを使用します(必要に応じてリンクを提供します)
  2. 堅牢性 -関数はコーナーケースを処理しますか、それとも無限ループに陥るか、無効な結果を生成しますか-無効な入力の例外またはエラーは有効です
  3. ランダム性 -ランダムな分布では容易に予測できないランダムな結果を生成するはずです。組み込みの乱数ジェネレータを使用しても問題ありません。しかし、明白なバイアスや明白な予測可能なパターンがあってはなりません。ディルバートの経理部門が使用する乱数ジェネレーターよりも優れている必要がある

堅牢でランダムな場合は、実行時になります。頑健またはランダムでないと、その立場は大きく損なわれます。


出力は、DIEHARDまたはTestU01テストのようなものに合格するはずですか、またはそのランダム性をどのように判断しますか?ああ、コードは32または64ビットモードで実行する必要がありますか?(それが最適化に大きな違いを
もたらし

TestU01はおそらく少し厳しいと思います。基準3は均一な分布を意味しますか?また、なぜ非繰り返し要件なのですか?それは特にランダムではありません。
Joey

@ジョーイ、確かにそうだ。置換なしのランダムサンプリングです。リスト内の異なる位置が独立確率変数であると誰も主張しない限り、問題はありません。
Peter Taylor

ああ、確かに。しかし、サンプリングのランダム性を測定するための確立されたライブラリとツールがあるかどうかはわかりません:-)
Joey

@IlmariKaronen:RE:ランダム性:以前に実装が途方もなく無作為であるのを見てきました。彼らは大きな偏見を持っているか、連続した実行で異なる結果を生成する能力に欠けていました。したがって、私たちは暗号レベルのランダム性について話しているのではなく、Dilbertの会計部の乱数ジェネレーターよりもランダムです。
ジムマッキース、2012年

回答:


6

パイソン

import random

def sample(n, lower, upper):
    result = []
    pool = {}
    for _ in xrange(n):
        i = random.randint(lower, upper)
        x = pool.get(i, i)
        pool[i] = pool.get(lower, lower)
        lower += 1
        result.append(x)
    return result

私はおそらくいくつかのよく知られたアルゴリズムを再考しただけですが、アイデアは(概念的に)範囲の部分的なFisher-Yatesシャッフルを実行して、均一にシャッフルされた範囲のlower..upper長さnプレフィックスを取得することです。

もちろん、範囲全体を保存するのはかなりコストがかかるので、要素が交換された場所のみを保存します。

このようにして、アルゴリズムは、狭い範囲(たとえば、1..1000の範囲の1000の数値)から数値をサンプリングする場合と、広い範囲の数値をサンプリングする場合の両方で適切に機能します。 。

Pythonの組み込みジェネレーターのランダム性の品質についてはわかりませんが、ある範囲から整数を均一に生成できるジェネレーターでスワップするのは比較的簡単です。


1
PythonはMersenne Twisterを使用しているため、比較的まともです。
ESultanik 2012年

1

Python 2.7

import random
print(lambda x,y,z:random.sample(xrange(y,z),x))(input(),input(),input())

組み込みのランダムな方法を使用してあなたの立場が何であるかはわかりませんが、ここではとにかく行きます。素敵で短い

編集:range()は大きなリストを作成したくないことに気づきました。メモリエラーが発生します。これを行う他の方法があるかどうかを確認します...

edit2:rangeは間違った関数でした、xrangeは機能します。最大整数は実際に2**31-1はpython用です

テスト:

python sample.py
10
0
2**31-1
[786475923, 2087214992, 951609341, 1894308203, 173531663, 211170399, 426989602, 1909298419, 1424337410, 2090382873]

1

C

最小と最大の間のx個の一意のランダム整数を含む配列を返します。(発信者は解放する必要があります)

#include <stdlib.h>
#include <stdint.h>
#define MAX_ALLOC ((uint32_t)0x40000000)  //max allocated bytes, fix per platform
#define MAX_SAMPLES (MAX_ALLOC/sizeof(uint32_t))

int* randsamp(uint32_t x, uint32_t min, uint32_t max)
{
   uint32_t r,i=x,*a;
   if (!x||x>MAX_SAMPLES||x>(max-min+1)) return NULL;
   a=malloc(x*sizeof(uint32_t));
   while (i--) {
      r= (max-min+1-i);
      a[i]=min+=(r ? rand()%r : 0);
      min++;
   }
   while (x>1) {
      r=a[i=rand()%x--];
      a[i]=a[x];
      a[x]=r;
   }
   return a;
}

範囲内でx個の順次ランダム整数を生成し、それらをシャッフルすることで機能します。seed(time)実行ごとに同じ結果が必要ない場合は、呼び出し元のどこかに追加します。


1

Ruby> = 1.8.7

def pick(num, min, max)
  (min..max).to_a.sample(num)
end

p pick(5, 10, 20) #=>[12, 18, 13, 11, 10]


1

質問は正しくありません。均一なサンプリングが必要かどうか。均一なサンプリングが必要な場合、平均的な複雑度Os log s)を持つRに次のコードがあります。ここで、sはサンプルサイズです。

# The Tree growing algorithm for uniform sampling without replacement
# by Pavel Ruzankin 
quicksample = function (n,size)
# n - the number of items to choose from
# size - the sample size
{
  s=as.integer(size)
  if (s>n) {
    stop("Sample size is greater than the number of items to choose from")
  }
  # upv=integer(s) #level up edge is pointing to
  leftv=integer(s) #left edge is poiting to; must be filled with zeros
  rightv=integer(s) #right edge is pointig to; must be filled with zeros
  samp=integer(s) #the sample
  ordn=integer(s) #relative ordinal number

  ordn[1L]=1L #initial value for the root vertex
  samp[1L]=sample(n,1L) 
  if (s > 1L) for (j in 2L:s) {
    curn=sample(n-j+1L,1L) #current number sampled
    curordn=0L #currend ordinal number
    v=1L #current vertice
    from=1L #how have come here: 0 - by left edge, 1 - by right edge
    repeat {
      curordn=curordn+ordn[v]
      if (curn+curordn>samp[v]) { #going down by the right edge
        if (from == 0L) {
          ordn[v]=ordn[v]-1L
        }
        if (rightv[v]!=0L) {
          v=rightv[v]
          from=1L
        } else { #creating a new vertex
          samp[j]=curn+curordn
          ordn[j]=1L
          # upv[j]=v
          rightv[v]=j
          break
        }
      } else { #going down by the left edge
        if (from==1L) {
          ordn[v]=ordn[v]+1L
        }
        if (leftv[v]!=0L) {
          v=leftv[v]
          from=0L
        } else { #creating a new vertex
          samp[j]=curn+curordn-1L
          ordn[j]=-1L
          # upv[j]=v
          leftv[v]=j
          break
        }
      }
    }
  }
  return(samp)  
}

もちろん、パフォーマンスを向上させるためにCで書き換えることもできます。このアルゴリズムの複雑さについては、以下で説明されています。Voytishek、AVランダム選択のアルゴリズムのコストについて。モンテカルロ法Appl。5(1999)、いいえ。1、39-54。 http://dx.doi.org/10.1515/mcma.1999.5.1.39

同じ平均的な複雑さを持つ別のアルゴリズムについて、このペーパーを参照してください。

ただし、均一なサンプリングが必要でなく、すべてのサンプリングされた数値が異なることだけが必要な場合は、状況が劇的に変化します。平均的な複雑度Os)を持つアルゴリズムを記述することは難しくありません。

均一サンプリングについては、P。Gupta、GP Bhattacharjeeも参照してください。(1984)置換なしのランダムサンプリングの効率的なアルゴリズム。International Journal of Computer Mathematics 16:4、ページ201-209。DOI:10.1080 / 00207168408803438

Teuhola、J.およびNevalainen、O.1982。置換なしのランダムサンプリングのための2つの効率的なアルゴリズム。/ IJCM /、11(2):127–140。DOI:10.1080 / 00207168208803304

前回の論文では、著者はハッシュテーブルを使用し、アルゴリズムの複雑度はOs)であると主張しています。もう1つ高速ハッシュテーブルアルゴリズムがあり、すぐにpqR(かなり高速R)に実装されますhttps ://stat.ethz.ch/pipermail/r-devel/2017-October/075012.html


1

APL、18 22バイト

{⍵[0]+(1↑⍺)?⍵[1]-⍵[0]}

二つの引数を取る無名関数宣言とはは、必要な乱数の数、下限と上限をこの順序で含むベクトルです。

a?bピックa0-間の乱数をb交換せずに。取る⍵[1]-⍵[0]ことで範囲サイズを取得します。次に、その範囲から数値(下記参照)を選択し、下限を追加します。Cでは、これは

lower + rand() * (upper - lower)

交換せずに回。APLは右から左に機能するため、括弧は不要です。

条件を正しく理解していると仮定すると、不適切な引数(たとえば、スカラーの代わりにベクトルを渡すなど)を指定すると関数が失敗するため、これは「ロバスト性」基準を満たしません

それがスカラーで1↑⍺はなくベクトルであるイベントでは、の最初の要素を取ります。スカラーの場合、これはスカラー自体です。ベクトルの場合、これは最初の要素です。これにより、関数は「堅牢性」の基準を満たすはずです。

例:

Input: 100 {⍵[0]+⍺?⍵[1]-⍵[0]} 0 100
Output: 34 10 85 2 46 56 32 8 36 79 77 24 90 70 99 61 0 21 86 50 83 5 23 27 26 98 88 66 58 54 76 20 91 72 71 65 63 15 33 11 96 60 43 55 30 48 73 75 31 13 19 3 45 44 95 57 97 37 68 78 89 14 51 47 74 9 67 18 12 92 6 49 41 4 80 29 82 16 94 52 59 28 17 87 25 84 35 22 38 1 93 81 42 40 69 53 7 39 64 62

2
これはコードゴルフではなく最速のコースです。したがって、目標は、タスクを実行するために最短ではなく最速のコードを生成することです。とにかく、あなたは本当にそのような引数からアイテムを選ぶ必要はなく、あなたはそれらの順序を決定することができるので{⍵+⍺?⎕-⍵}、プロンプトが上限で右引数が下限である場合に十分であるはずです
Uriel

0

Scala

object RandSet {
  val random = util.Random 

  def rand (count: Int, lower: Int, upper: Int, sofar: Set[Int] = Set.empty): Set[Int] =
    if (count == sofar.size) sofar else 
    rand (count, lower, upper, sofar + (random.nextInt (upper-lower) + lower)) 
}

object RandSetRunner {

  def main (args: Array [String]) : Unit = {
    if (args.length == 4) 
      (0 until args (0).toInt).foreach { unused => 
      println (RandSet.rand (args (1).toInt, args (2).toInt, args (3).toInt).mkString (" "))
    }
    else Console.err.println ("usage: scala RandSetRunner OUTERCOUNT COUNT MIN MAX")
  }
}

コンパイルして実行:

scalac RandSetRunner.scala 
scala RandSetRunner 200 15 0 100

2行目では、Scalaは高速なバイトコードを生成しますが、起動時間を必要とするため、0から100までの15の値で200テストを実行します。したがって、0から100までの15の値で始まる200は、より多くの時間を消費します。

2 Ghzシングルコアのサンプル:

time scala RandSetRunner 100000 10 0 1000000 > /dev/null

real    0m2.728s
user    0m2.416s
sys     0m0.168s

論理:

組み込みのランダムで再帰的に範囲(max-min)の数値を選択し、minを追加して、セットのサイズが予想されるサイズであるかどうかを確認します。

批評:

  • 大きな範囲の小さなサンプルの場合は高速ですが、タスクがサンプルのほぼすべての要素(1000のうち999の数値)を選択する場合は、すでにセットに含まれている数値を繰り返し選択します。
  • 質問から、テイク10の4から8までの個別の数値などの実行不可能な要求をサニタイズする必要があるかどうかはわかりません。これにより、無限ループが発生しますが、事前のチェックで簡単に回避できます。要求した。

0

スキーム

なぜ3つのパラメーターを渡す必要があるのか​​、なぜ範囲を想定する必要があるのか​​わからない...

(import srfi-1) ;; for iota
(import srfi-27) ;; randomness
(import srfi-43) ;; for vector-swap!

(define rand (random-source-make-integers
               default-random-source))

;; n: length, i: lower limit
(define (random-range n i)
  (let ([v (list->vector (iota n i))])
    (let f ([n n])
      (let* ([i (rand n)] [n (- n 1)])
        (if (zero? n) v
            (begin (vector-swap! v n i) (f n)))))))

0

R

random <- function(count, from, to) {
  rand.range <- to - from

  vec <- c()

  for (i in 1:count) {
    t <- sample(rand.range, 1) + from
    while(i %in% vec) {
      t <- sample(rand.range, 1) + from
    }
    vec <- c(vec, t)
  }

  return(vec)
}

0

C ++

このコードは、範囲から多くのサンプルを描画する場合に最適です。

#include <exception>
#include <stdexcept>
#include <cstdlib>

template<typename OutputIterator>
 void sample(OutputIterator out, int n, int min, int max)
{
  if (n < 0)
    throw std::runtime_error("negative sample size");
  if (max < min)
    throw std::runtime_error("invalid range");
  if (n > max-min+1)
    throw std::runtime_error("sample size larger than range");

  while (n>0)
  {
    double r = std::rand()/(RAND_MAX+1.0);
    if (r*(max-min+1) < n)
    {
      *out++ = min;
      --n;
    }
    ++min;
  }
}

max-minがをはるかに超えない限り、これは簡単に無限ループに陥る可能性がありますn。また、出力シーケンスは単調増加しているため、非常に低い品質のランダム性が得られますが、rand()結果ごとに複数回呼び出すコストがかかります。配列のランダムなシャッフルは、おそらく追加のランタイムの価値があります。
Peter Cordes 2016

0

Q(19文字)

f:{(neg x)?y+til z}

次に、[出力セット内の数のカウント;開始点;範囲のサイズ]としてf [x; y; z]を使用します

たとえば、f [5; 10; 10]は、10〜19の5つの異なる乱数を出力します。

q)\ts do[100000;f[100;1;10000]]
2418 131456j

上記の結果は、1から10,000までの100個の乱数を選択する100,000回の反復でのパフォーマンスを示しています。


0

R、31または40バイト(「範囲」という単語の意味による)

入力に3つの数値がa[1], a[2], a[3]あり、「範囲」とは「a [2]からa [3]までの整数シーケンス」を意味する場合、次のようになります。

a=scan();sample(a[2]:a[3],a[1])

nリサンプリングしようとしている配列があるが、「n範囲から指定された配列の値をリサンプリングする」のように下限と上限の制限の下で、a[1]...a[2]これを使用する場合:

a=scan();sample(n[n>=a[2]&n<=a[3]],a[1])

交換用のビルトインサンプルで前の結果がゴルフにならなかったのは何と驚き!範囲条件を満たすベクトルを作成し、再サンプリングします。

  • 堅牢性:コーナーケース(サンプリングする範囲と同じ長さのシーケンス)がデフォルトで処理されます。
  • 実行時:組み込まれているため、非常に高速です。
  • ランダム性:RNGが呼び出されるたびにシードが自動的に変更されます。

少なくとも私のマシンで0:(2^31)は、Error: cannot allocate a vector of size 16.0 Gb
ジュゼッペの

@Giuseppe最近、私はビッグメモリの問題に取り組んできましたが、その解決策は実際には...より良いマシンで実行することです。タスクの定式化の制限は、メモリではなくプロセッサに関係するので、規則の乱用とは何ですか?あ、お尻です。コードゴルフのチャレンジだと思ったのですが、実は...最速コードです。負けたかな?
アンドレイKostyrka

0

Javascript(外部ライブラリを使用)(64バイト/ 104バイト??)

(a,b,n)=>_.Range(0,n).Select(x=>Math.random()*(b-a)+a).ToArray()

libへのリンク:https : //github.com/mvegh1/Enumerable/

コードの説明:Lambda式は、引数としてmin、max、countを受け入れます。サイズnのコレクションを作成し、各要素を最小/最大基準に適合する乱数にマッピングします。ネイティブJS配列に変換して返します。サイズ5,000,000の入力でもこれを実行しましたが、個別の変換を適用した後も、まだ5,000,000要素を示しています。これが明確性を保証するのに十分安全ではないことが合意された場合、私は答えを更新します

下の画像にいくつかの統計を含めました...

ここに画像の説明を入力してください

編集:下の画像は、すべての要素が異なることを保証するコード/パフォーマンスを示しています。上記の元のコードと同じ引数(0.012秒)の場合よりもはるかに遅い(50,000要素で6.65秒)

ここに画像の説明を入力してください


0

K(OK)、14バイト

解決:

{y+(-x)?1+z-y}

オンラインでお試しください!

例:

> {y+(-x)?1+z-y}. 10 10 20      / note: there are two ways to provide input, dot or
13 20 16 17 19 10 14 12 11 18
> {y+(-x)?1+z-y}[10;10;20]      / explicitly with [x;y;z]
12 11 13 19 15 17 18 20 14 10

説明:

仕様ごとに3つの暗黙的な入力を受け取ります。

  • x、出力セット内の数のカウント、
  • y、下限(両端を含む)
  • z、上限(両端を含む)

{y+(-x)?1+z-y} / the solution
{            } / lambda function with x, y and z as implicit inputs
          z-y  / subtract lower limit from upper limit
        1+     / add 1
   (-x)?       / take x many distinct items from 0..(1+z=y)
 y+            / add lower limit

ノート:

また、q/kdb+ブラケットの追加セットを含むポリグロット:{y+((-)x)?1+z-y}(16バイト)。


0

Axiom +そのライブラリ

f(n:PI,a:INT,b:INT):List INT==
    r:List INT:=[]
    a>b or n>99999999 =>r
    d:=1+b-a
    for i in 1..n repeat
          r:=concat(r,a+random(d)$INT)
    r

上記のf()関数は、a> bのf(n、a、b)の場合、空のリストをエラーとして返します。その他の無効な入力の場合、引数が正しいタイプではないため、Axiomウィンドウに1つのエラーメッセージが表示されて実行されません。例

(6) -> f(1,1,5)
   (6)  [2]
                                                       Type: List Integer
(7) -> f(1,1,1)
   (7)  [1]
                                                       Type: List Integer
(10) -> f(10,1,1)
   (10)  [1,1,1,1,1,1,1,1,1,1]
                                                       Type: List Integer
(11) -> f(10,-20,-1)
   (11)  [- 10,- 4,- 18,- 5,- 5,- 11,- 15,- 1,- 20,- 1]
                                                       Type: List Integer
(12) -> f(10,-20,-1)
   (12)  [- 4,- 5,- 3,- 4,- 18,- 1,- 2,- 14,- 19,- 8]
                                                       Type: List Integer
(13) -> f(10,-20,-1)
   (13)  [- 18,- 12,- 12,- 19,- 19,- 15,- 5,- 17,- 19,- 4]
                                                       Type: List Integer
(14) -> f(10,-20,-1)
   (14)  [- 8,- 11,- 20,- 10,- 4,- 8,- 11,- 3,- 10,- 16]
                                                       Type: List Integer
(15) -> f(10,9,-1)
   (15)  []
                                                       Type: List Integer
(16) -> f(10,0,100)
   (16)  [72,83,41,35,27,0,33,18,60,38]
                                                       Type: List Integer
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.