Pengambilan sampel acak tanpa penggantian


10

Buat fungsi yang akan menampilkan sekumpulan angka acak berbeda yang diambil dari suatu rentang. Urutan elemen dalam himpunan tidak penting (mereka bahkan dapat diurutkan), tetapi harus mungkin untuk isi himpunan berbeda setiap kali fungsi dipanggil.

Fungsi akan menerima 3 parameter dalam urutan apa pun yang Anda inginkan:

  1. Hitungan angka dalam output yang ditetapkan
  2. Batas bawah (inklusif)
  3. Batas atas (inklusif)

Asumsikan semua angka adalah bilangan bulat dalam kisaran 0 (inklusif) hingga 2 31 (eksklusif). Output dapat dikirimkan kembali dengan cara apa pun yang Anda inginkan (tulis ke konsol, sebagai array, dll.)

Menilai

Kriteria termasuk 3 R

  1. Run-time - diuji pada mesin Windows 7 quad-core dengan kompiler apa pun yang tersedia secara bebas atau mudah (berikan tautan jika perlu)
  2. Robustness - apakah fungsi menangani kasus sudut atau akan jatuh ke loop tak terbatas atau menghasilkan hasil yang tidak valid - pengecualian atau kesalahan pada input yang tidak valid valid
  3. Keacakan - ini harus menghasilkan hasil acak yang tidak mudah diprediksi dengan distribusi acak. Menggunakan generator bilangan acak bawaan baik-baik saja. Tetapi seharusnya tidak ada bias yang jelas atau pola yang dapat diprediksi. Perlu lebih baik daripada generator angka acak yang digunakan oleh Departemen Akuntansi di Dilbert

Jika itu kuat dan acak maka turun ke run-time. Gagal menjadi kuat atau acak sangat menyakitkan kedudukannya.


Apakah output seharusnya untuk lulus sesuatu seperti diehard atau TestU01 tes, atau bagaimana akan Anda menilai keacakan? Oh, dan haruskah kode dijalankan dalam mode 32 atau 64 bit? (Itu akan membuat perbedaan besar untuk optimasi.)
Ilmari Karonen

TestU01 mungkin agak keras, kurasa. Apakah kriteria 3 menyiratkan distribusi yang seragam? Juga, mengapa persyaratan yang tidak berulang ? Itu tidak terlalu acak, kalau begitu.
Joey

@ Joey, tentu saja. Ini pengambilan sampel acak tanpa penggantian. Selama tidak ada yang mengklaim bahwa posisi yang berbeda dalam daftar adalah variabel acak independen, tidak ada masalah.
Peter Taylor

Ah, memang. Tapi saya tidak yakin apakah ada perpustakaan yang mapan dan alat untuk mengukur keacakan sampel :-)
Joey

@IlmariKaronen: RE: Keacakan: Saya pernah melihat implementasi sebelumnya yang sangat tidak acak. Entah mereka memiliki bias yang berat, atau kurang memiliki kemampuan untuk menghasilkan hasil yang berbeda pada putaran berturut-turut. Jadi kita tidak berbicara keacakan tingkat kriptografi, tetapi lebih acak daripada penghasil angka acak Departemen Akuntansi di Dilbert .
Jim McKeeth

Jawaban:


6

Python

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

Saya mungkin baru saja menemukan kembali beberapa algoritma yang terkenal, tetapi idenya adalah untuk (secara konseptual) melakukan shuffle Fisher-Yates parsial dari kisaran lower..upperuntuk mendapatkan nawalan panjang rentang yang dikocok secara seragam.

Tentu saja, menyimpan seluruh jajaran akan lebih mahal, jadi saya hanya menyimpan lokasi di mana elemen telah ditukar.

Dengan cara ini, algoritme harus berkinerja baik baik dalam kasus di mana Anda sampel angka dari kisaran ketat (misalnya 1000 angka dalam kisaran 1..1000), serta kasus di mana Anda mengambil sampel angka dari rentang besar .

Saya tidak yakin tentang kualitas keacakan dari generator built-in di Python, tetapi relatif mudah untuk menukar generator apa pun yang dapat menghasilkan bilangan bulat secara seragam dari beberapa rentang.


1
Python menggunakan Mersenne Twister , jadi itu relatif baik.
ESultanik

1

python 2.7

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

tidak yakin apa posisi Anda saat menggunakan metode acak bawaan, tapi begini saja. bagus dan pendek

sunting: cukup perhatikan bahwa rentang () tidak suka membuat daftar besar. menyebabkan kesalahan memori. akan melihat apakah ada cara lain untuk melakukan ini ...

sunting2: range adalah fungsi yang salah, xrange berfungsi. Bilangan bulat maksimum sebenarnya 2**31-1untuk python

uji:

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

1

C

Mengembalikan array yang berisi x int acak unik antara min dan maks. (penelepon harus bebas)

#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;
}

Bekerja dengan menghasilkan bilangan bulat acak berurutan dalam kisaran, lalu mengocoknya. Tambahkan seed(time)penelepon di suatu tempat jika Anda tidak ingin hasil yang sama setiap kali dijalankan.


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

R

s <- function(n, lower, upper) sample(lower:upper,n); s(10,0,2^31-2)

1

Pertanyaannya tidak benar. Apakah Anda perlu pengambilan sampel yang seragam atau tidak? Dalam pengambilan sampel kasus seragam diperlukan Saya memiliki kode berikut di R, yang memiliki rata-rata kompleksitas O ( s log s ), di mana s adalah ukuran sampel.

# 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)  
}

Tentu saja, seseorang dapat menulis ulang dalam C untuk kinerja yang lebih baik. Kompleksitas dari algoritma ini dibahas dalam: Rouzankin, PS; Voytishek, AV Pada biaya algoritma untuk pemilihan acak. Metode Monte Carlo Appl. 5 (1999), no. 1, 39-54. http://dx.doi.org/10.1515/mcma.1999.5.1.39

Anda dapat melihat melalui makalah ini untuk algoritma lain dengan kompleksitas rata-rata yang sama.

Tetapi jika Anda tidak perlu pengambilan sampel yang seragam, hanya mengharuskan semua nomor sampel berbeda, maka situasinya berubah secara dramatis. Tidak sulit untuk menulis algoritma yang memiliki kompleksitas rata-rata O ( s ).

Lihat juga untuk pengambilan sampel yang seragam: P. Gupta, GP Bhattacharjee. (1984) Algoritma yang efisien untuk pengambilan sampel acak tanpa penggantian. Jurnal Internasional Matematika Komputer 16: 4, halaman 201-209. DOI: 10.1080 / 00207168408803438

Teuhola, J. dan Nevalainen, O. 1982. Dua algoritma yang efisien untuk pengambilan sampel acak tanpa penggantian. / IJCM /, 11 (2): 127-140. DOI: 10.1080 / 00207168208803304

Dalam makalah terakhir penulis menggunakan tabel hash dan mengklaim bahwa algoritma mereka memiliki kompleksitas O ( s ). Ada satu lagi algoritma tabel hash yang lebih cepat, yang akan segera diimplementasikan dalam pqR (R cukup cepat): https://stat.ethz.ch/pipermail/r-devel/2017-October/075012.html


1

APL, 18 22 byte

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

Menyatakan fungsi anonim yang mengambil dua argumen dan . adalah jumlah angka acak yang Anda inginkan, adalah vektor yang berisi batas bawah dan atas, dalam urutan itu.

a?bmengambil anomor acak antara 0 btanpa penggantian. Dengan mengambil, ⍵[1]-⍵[0]kami mendapatkan ukuran kisaran. Lalu kami memilih angka (lihat di bawah) dari rentang itu dan menambahkan batas bawah. Dalam C, ini akan menjadi

lower + rand() * (upper - lower)

kali tanpa penggantian. Kurung tidak diperlukan karena APL beroperasi dari kanan ke kiri.

Dengan asumsi saya sudah memahami kondisinya dengan benar, ini gagal kriteria 'kekokohan' karena fungsinya akan gagal jika diberikan argumen yang tidak tepat (misal, meneruskan vektor alih-alih skalar ).

Dalam hal itu adalah vektor daripada skalar, 1↑⍺ambil elemen pertama . Untuk skalar, ini adalah skalar itu sendiri. Untuk vektor, itu elemen pertama. Ini harus membuat fungsi memenuhi kriteria 'ketahanan'.

Contoh:

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
Ini bukan golf kode, tetapi cose tercepat, oleh karena itu tujuannya adalah menghasilkan kode tercepat untuk melakukan tugas daripada yang terpendek. Bagaimanapun, Anda tidak benar-benar perlu memilih item dari argumen seperti itu, dan Anda dapat menentukan pesanan mereka, jadi {⍵+⍺?⎕-⍵}cukuplah, di mana prompt adalah untuk batas atas dan arg kanan adalah batas bawah
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")
  }
}

kompilasi dan jalankan:

scalac RandSetRunner.scala 
scala RandSetRunner 200 15 0 100

Baris kedua akan menjalankan 200 tes dengan 15 nilai dari 0 hingga 100, karena Scala menghasilkan bytecode cepat tetapi membutuhkan waktu startup. Jadi 200 dimulai dengan 15 nilai dari 0 hingga 100 akan menghabiskan lebih banyak waktu.

Sampel pada Core Tunggal 2 Ghz:

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

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

Logika:

Menggunakan built-in angka acak dan rekursif dalam rentang (maks-mnt), menambahkan min dan memeriksa, jika ukuran set adalah ukuran yang diharapkan.

Kritik:

  • Ini akan cepat untuk sampel kecil rentang besar, tetapi jika tugasnya adalah untuk mengambil hampir semua elemen sampel (999 angka dari 1000) itu akan berulang kali memilih angka, sudah ada di set.
  • Dari pertanyaan, saya tidak yakin, apakah saya harus membersihkan terhadap permintaan yang tidak terpenuhi seperti Ambil 10 angka yang berbeda dari 4 menjadi 8. Ini sekarang akan mengarah pada perulangan tanpa akhir, tetapi dapat dengan mudah dihindari dengan cek sebelumnya yang akan saya tambahkan jika diminta.

0

Skema

Tidak yakin mengapa Anda perlu 3 parameter lulus atau mengapa saya perlu mengasumsikan kisaran apa pun ...

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

Kode ini paling baik ketika menggambar banyak sampel dari kisaran.

#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;
  }
}

Ini dapat dengan mudah terjebak dalam infinite loop kecuali max-minlebih besar dari n. Juga, urutan output meningkat secara monoton, sehingga Anda mendapatkan keacakan kualitas yang sangat rendah tetapi masih membayar biaya panggilan rand()beberapa kali per hasil. Acak acak array mungkin sebanding dengan run-time tambahan.
Peter Cordes

0

Q (19 karakter)

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

Kemudian gunakan f [x; y; z] sebagai [jumlah angka dalam output yang ditetapkan; titik awal; ukuran kisaran]

misalnya f [5; 10; 10] akan menampilkan 5 angka acak berbeda antara 10 dan 19 inklusif.

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

Hasil di atas menunjukkan kinerja pada 100.000 iterasi memilih 100 angka acak antara 1 & 10.000.


0

R, 31 atau 40 byte (tergantung pada arti kata "rentang")

Jika input memiliki 3 angka,, a[1], a[2], a[3]dan "range" yang Anda maksudkan adalah "deret integer dari [2] ke [3]", maka Anda memiliki ini:

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

Jika Anda memiliki larik nyang akan Anda coba sampel ulang, tetapi di bawah batasan batas bawah dan atas, seperti "nilai ulang sampel larik yang diberikan ndari rentang a[1]...a[2]", maka gunakan ini:

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

Saya cukup terkejut mengapa hasil sebelumnya tidak golf mengingat sampel bawaan dengan fasilitas pengganti! Kami membuat vektor yang memenuhi kondisi rentang dan sampel ulang itu.

  • Robustness: kasus sudut (urutan dengan panjang yang sama dengan rentang sampel) ditangani secara default.
  • Run-time: sangat cepat karena sudah terpasang.
  • Keacakan: benih diubah secara otomatis setiap kali RNG dipanggil.

setidaknya di mesin saya, 0:(2^31)menyebabkanError: cannot allocate a vector of size 16.0 Gb
Giuseppe

@ Giuseppe Baru-baru ini, saya telah bekerja dengan masalah memori besar, dan solusi untuk itu sebenarnya ... menjalankannya di mesin yang lebih baik. Pembatasan dalam perumusan tugas berkaitan dengan prosesor, bukan ke memori, jadi apakah itu ... aturan penyalahgunaan? Ah, aku keledai. Saya pikir itu adalah tantangan kode golf , tetapi sebenarnya itu adalah ... kode tercepat. Saya kalah saya kira?
Andreï Kostyrka

0

Javascript (menggunakan perpustakaan eksternal) (64 byte / 104 byte ??)

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

Tautan ke lib: https://github.com/mvegh1/Enumerable/

Penjelasan kode: Ekspresi Lambda menerima min, maks, hitung sebagai argumen. Buat koleksi ukuran n, dan petakan setiap elemen ke angka acak yang sesuai dengan kriteria min / maks. Konversikan ke array JS asli dan kembalikan. Saya menjalankan ini juga pada input ukuran 5.000.000, dan setelah menerapkan transformasi yang berbeda masih menunjukkan 5.000.000 elemen. Jika disetujui bahwa ini tidak cukup aman untuk jaminan perbedaan, saya akan memperbarui jawabannya

Saya memasukkan beberapa statistik pada gambar di bawah ini ...

masukkan deskripsi gambar di sini

EDIT: Gambar di bawah ini menunjukkan kode / kinerja yang menjamin setiap elemen akan berbeda. Ini jauh lebih lambat (6,65 detik untuk 50.000 elemen) vs kode asli di atas untuk args yang sama (0,012 detik)

masukkan deskripsi gambar di sini


0

K (oK) , 14 byte

Larutan:

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

Cobalah online!

Contoh:

> {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

Penjelasan:

Membawa 3 input implisit per spec:

  • x, hitung angka dalam output yang ditetapkan,
  • y, batas bawah (inklusif)
  • z, batas atas (inklusif)

{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

Catatan:

Juga polyglot q/kdb+dengan set kurung tambahan: {y+((-)x)?1+z-y}(16 byte).


0

Aksioma + perpustakaannya

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

Fungsi f () di atas mengembalikan kesalahan daftar kosong, dalam kasus f (n, a, b) dengan a> b. Dalam kasus lain dari input yang tidak valid, itu tidak berjalan dengan satu pesan kesalahan di jendela aksioma, karena argumen tidak akan menjadi tipe yang tepat. Contohnya

(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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.