Değiştirilmeden rastgele örnekleme


10

Bir aralıktan çizilen bir dizi farklı rasgele sayı çıktısı verecek bir işlev oluşturun. Kümedeki öğelerin sırası önemsizdir (sıralanabilirler), ancak işlev her çağrıldığında küme içeriğinin farklı olması mümkün olmalıdır.

İşlev, istediğiniz sırayla 3 parametre alır:

  1. Çıkış kümesindeki sayı sayısı
  2. Alt sınır (dahil)
  3. Üst sınır (dahil)

Tüm sayıların 0 (dahil) ile 2 31 (hariç) aralığında tamsayı olduğunu varsayalım . Çıktı istediğiniz şekilde geri aktarılabilir (konsola yazma, dizi olarak vb.)

YARGILAMAK

Kriterler 3 R'leri içerir

  1. Çalışma zamanı - herhangi bir derleyici serbest veya kolay olan dört çekirdekli bir Windows 7 makinesinde test edilmiştir (gerekirse bir bağlantı sağlayın)
  2. Sağlamlık - işlev köşe vakalarını işler mi yoksa sonsuz döngüye girer mi yoksa geçersiz sonuçlar üretir mi - geçersiz girişte bir istisna veya hata geçerlidir
  3. Rasgelelik - rasgele bir dağılımla kolayca tahmin edilemeyen rasgele sonuçlar üretmelidir. Dahili rasgele sayı üretecini kullanmak iyidir. Ancak belirgin önyargılar veya belirgin öngörülebilir kalıplar olmamalıdır. Dilbert Muhasebe Departmanı tarafından kullanılan rasgele sayı üretecinden daha iyi olması gerekiyor

Sağlam ve rasgele ise çalışma zamanına iner. Sağlam veya rastgele olmamak, duruşlarını büyük ölçüde incitir.


Çıktının DIEHARD veya TestU01 testleri gibi bir şeyi geçmesi mi gerekiyor , yoksa rasgeleliğini nasıl değerlendireceksiniz? Oh, ve kod 32 veya 64 bit modunda mı çalışmalı? (Bu, optimizasyon için büyük bir fark yaratacaktır.)
Ilmari Karonen

TestU01 muhtemelen biraz sert, sanırım. Kriter 3, muntazam bir dağılımı ima ediyor mu? Ayrıca, neden tekrarlanmayan gereksinim? O zaman bu rastgele değil.
Joey

@Joey, kesinlikle. Değiştirilmeden rastgele örnekleme. Hiç kimse listedeki farklı pozisyonların bağımsız rastgele değişkenler olduğunu iddia etmediği sürece sorun yoktur.
Peter Taylor

Ah, gerçekten. Ama örneklemenin rastgelelik ölçmek için köklü kütüphaneler ve araçlar olup olmadığından emin değilim :-)
Joey

@IlmariKaronen: RE: Rastgelelık: Daha önce bu konuda acımasızca zahmetsiz uygulamalar gördüm. Ya ağır bir önyargıya sahiplerdi ya da ardışık koşularda farklı sonuçlar üretme yeteneğinden yoksundular. Yani kriptografik seviye rastgele değil, Muhasebe Departmanı'nın Dilbert'teki rastgele sayı üretecinden daha rastgele konuşuyoruz .
Jim McKeeth

Yanıtlar:


6

piton

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

Muhtemelen iyi bilinen bir algoritmayı yeniden icat ettim, ancak fikir (kavramsal olarak) eşit olarak karıştırılmış bir aralığın lower..upperuzunluk nönekini elde etmek için aralığın kısmi bir Fisher-Yates shuffle'ı yapmaktır .

Tabii ki, tüm aralığı saklamak oldukça pahalı olurdu, bu yüzden sadece elemanların değiştirildiği yerleri saklıyorum.

Bu şekilde algoritma, hem dar bir aralıktan (örneğin 1..1000 aralığındaki 1000 sayı) sayıları örneklediğinizde hem de geniş bir aralıktan sayıları örneklediğiniz durumda iyi performans göstermelidir. .

Python'daki yerleşik jeneratörün rasgele kalitesinden emin değilim, ancak bazı aralıklardan düzgün bir şekilde tamsayılar üretebilen herhangi bir jeneratörde takas etmek nispeten basit.


1
Python Mersenne Twister kullanıyor , bu yüzden nispeten iyi.
ESultanik

1

python 2.7

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

yerleşik rastgele yöntemleri kullanarak durumunuzun ne olduğundan emin değilim, ama yine de buradasınız. hoş ve kısa

edit: range () 'nin büyük listeler yapmaktan hoşlanmadığını fark ettim. bir bellek hatasına yol açar. Bunu yapmanın başka bir yolu olup olmadığını göreceksiniz ...

edit2: aralık yanlış bir işlevdi, xrange çalışıyor. Maksimum tam sayı aslında 2**31-1python içindir

Ölçek:

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

1

C

Min. Ve maks. Arasında x benzersiz rastgele ints içeren bir dizi döndürür. (arayan serbest olmalıdır)

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

Aralıkta x sıralı rasgele tamsayı oluşturarak ve sonra onları karıştırarak çalışır. seed(time)Her çalışmada aynı sonuçları istemiyorsanız, arayanda bir yer ekleyin .


1

Yakut> = 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

Soru doğru değil. Tek tip örneklemeye mi ihtiyacınız var? Durumda tekdüze örnekleme tabi olarak ortalamanın karmaşıklığı vardır R aşağıdaki kodu sahip O ( s günlük s ), s örnek boyutudur.

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

Elbette, daha iyi performans için C'de yeniden yazılabilir. Bu algoritmanın karmaşıklığı aşağıda tartışılmaktadır: Rouzankin, PS; Voytishek, AV Rasgele seçim için algoritma maliyeti. Monte Carlo Yöntemleri Uyg. 5 (1999), no. 1, 39-54. http://dx.doi.org/10.1515/mcma.1999.5.1.39

Bu makalede, aynı ortalama karmaşıklığa sahip başka bir algoritma arayabilirsiniz.

Ancak tek tip örneklemeye ihtiyacınız yoksa, sadece tüm örneklenmiş sayıların farklı olmasını gerektiren durum, o zaman dramatik bir şekilde değişir. Ortalama karmaşıklığı O ( s ) olan bir algoritma yazmak zor değildir .

Tek tip örnekleme için bakınız: P. Gupta, GP Bhattacharjee. (1984) Değişmeden rastgele örnekleme için etkili bir algoritma. Uluslararası Bilgisayar Matematik Dergisi 16: 4, sayfa 201-209. DOI: 10.1080 / 00207168408803438

Teuhola, J. ve Nevalainen, O. 1982. Değiştirilmeden rastgele örnekleme için iki etkili algoritma. / IJCM /, 11 (2): 127-140. DOI: 10.1080 / 00207168208803304

Son makalede yazarlar karma tabloları kullanmış ve algoritmalarının O ( s ) karmaşıklığına sahip olduğunu iddia etmişlerdir . Yakında pqR'de (oldukça hızlı R) uygulanacak bir daha hızlı karma tablo algoritması var: https://stat.ethz.ch/pipermail/r-devel/2017-October/075012.html


1

APL, 18 22 bayt

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

İki argüman alan anonim bir işlev bildirir ve . istediğiniz rastgele sayıların sayısı, bu sırayla alt ve üst sınırları içeren bir vektördür.

a?bdeğiştirmeden a0- arasında rastgele sayılar alır b. Alarak ⍵[1]-⍵[0]aralık boyutunu elde ederiz. Sonra bu aralıktan sayılar seçeriz (aşağıya bakın) ve alt sınırı ekleriz. C dilinde, bu olurdu

lower + rand() * (upper - lower)

kez değiştirmeden. APL sağdan sola çalıştığı için parantez gerekmez.

Koşulları doğru anladığımı varsayarsak, bu 'sağlamlık' ölçütlerini geçemez, çünkü yanlış argümanlar verildiğinde işlev başarısız olur (örn . Skaler yerine bir vektör geçirme ).

Skaler değil bir vektör olması durumunda 1↑⍺, ilk elemanını alır . Bir skaler için, bu skalerin kendisidir. Bir vektör için bu ilk unsurdur. Bu, fonksiyonun 'sağlamlık' kriterlerini karşılamasını sağlamalıdır.

Misal:

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
Bu bir kod golf değil, en hızlı bir cose, bu nedenle amaç görevi en kısa sürede yerine getirmek için en hızlı kodu üretmektir. Her neyse, gerçekten böyle argümanlardan öğeleri seçmenize gerek yok ve sıralarını belirleyebilirsiniz, bu yüzden {⍵+⍺?⎕-⍵}yeterli olmalı, istemin üst sınır ve sağ argümanın alt sınır olduğu
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")
  }
}

derleyin ve çalıştırın:

scalac RandSetRunner.scala 
scala RandSetRunner 200 15 0 100

İkinci satır, 0'dan 100'e kadar 15 değerle 200 test gerçekleştirecek, çünkü Scala hızlı bayt kodu üretiyor, ancak biraz başlatma süresine ihtiyaç duyuyor. Yani 200, 0'dan 100'e kadar 15 değerle başlar, daha fazla zaman alır.

2 Ghz Tek Çekirdekli Örnek:

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

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

Mantık:

Aralıktaki dahili (rasgele) ve yinelemeli olarak toplama sayılarını (maks-min) kullanarak, min ekleme ve setin boyutunun beklenen boyutta olup olmadığını kontrol etme.

Eleştiri:

  • Büyük aralıklardaki küçük örnekler için hızlı olacaktır, ancak görev bir örneğin hemen hemen tüm öğelerini (1000'den 999 sayı) seçmekse, zaten kümede bulunan sayıları tekrar tekrar seçecektir.
  • Sorudan, 4'ten 8'e kadar 10 farklı sayı alın gibi istenmeyen doldurulamayan isteklere karşı sterilize etmek zorunda olup olmadığımdan emin değilim. Bu artık sonsuz bir döngüye yol açacak, ancak eğer talep edilen.

0

düzen

Neden 3 parametre geçti emin değilim neden neden herhangi bir aralık varsaymak gerekir ...

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

Bu kod, aralıktan birçok örnek çizerken en iyisidir.

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

Bu max-min, çok daha büyük olmadıkça sonsuz bir döngüde kolayca sıkışabilir n. Ayrıca, çıkış dizisi monoton olarak artmaktadır, bu nedenle çok düşük kalite rastgele elde edersiniz, ancak yine de rand()sonuç başına birden çok kez arama maliyetini ödersiniz . Dizinin rasgele karıştırılması muhtemelen ekstra çalışma süresine değer olacaktır.
Peter Cordes

0

Q (19 karakter)

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

Sonra [çıkış kümesindeki sayı sayısı; başlangıç ​​noktası; aralık boyutu] olarak f [x; y; z] kullanın

örneğin f [5; 10; 10], 10 ile 19 arasında 5 farklı rasgele sayı verecektir.

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

Yukarıdaki sonuçlar 1 ile 10.000 arasında 100 rasgele sayı seçerek 100.000 yinelemede performansı göstermektedir.


0

R, 31 veya 40 bayt (“aralık” sözcüğünün anlamına bağlı olarak)

Girişte 3 sayı varsa a[1], a[2], a[3]ve “aralık” ile “bir [2] ile [3] arasında bir tam sayı dizisi” anlamına gelirseniz, o zaman aşağıdakine sahipsiniz:

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

Yeniden nörneklemek üzere olduğunuz ancak alt ve üst sınırların kısıtlaması altında, örneğin “belirli bir dizinin naralıktan yeniden örnek değerleri” gibi bir diziniz varsa , bunu a[1]...a[2]kullanın:

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

Önceki sonuç değiştirme tesisleri ile yerleşik örnek göz önüne alındığında neden golf değildi oldukça şaşırdım! Aralık koşulunu sağlayan bir vektör yaratır ve yeniden örneklendiririz.

  • Sağlamlık: Köşe kasaları (örnekleme aralığıyla aynı uzunlukta diziler) varsayılan olarak işlenir.
  • Çalışma zamanı: yerleşik olduğu için son derece hızlı.
  • Rasgelelik: tohum, RNG her çağrıldığında otomatik olarak değiştirilir.

en azından makinemde, 0:(2^31)neden olurError: cannot allocate a vector of size 16.0 Gb
Giuseppe

@Giuseppe Son zamanlarda, büyük bellek sorunları ile çalışıyorum ve bunun çözümü aslında ... daha iyi bir makinede çalışıyor. Görevin formülasyonundaki kısıtlamalar, bellekle değil, işlemciyle ilgilidir, yani ... kural kötüye kullanımı mı? Ah, ben bir kıçım. Ben bir kod golf meydan okuma olduğunu düşündüm , ama aslında ... en hızlı kod. Kaybettim sanırım?
Andreï Kostyrka

0

Javascript (harici kitaplık kullanarak) (64 bayt / 104 bayt ??)

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

Lib bağlantısı: https://github.com/mvegh1/Enumerable/

Kod açıklaması: Lambda ifadesi, min, maks, sayımı bağımsız değişken olarak kabul eder. N boyutunda bir koleksiyon oluşturun ve her öğeyi min / maks ölçütlerine uyan rastgele bir sayıya eşleyin. Yerel JS dizisine dönüştürün ve döndürün. Bunu 5.000.000 büyüklüğünde bir girdi üzerinde de çalıştırdım ve farklı bir dönüşüm uyguladıktan sonra hala 5.000.000 öğe gösterdi. Eğer bu, ayrımcılık için yeterince güvence olmadığına karar verilirse, cevabı güncelleyeceğim

Aşağıdaki resme bazı istatistikler ekledim ...

resim açıklamasını buraya girin

DÜZENLEME: Aşağıdaki resimde her öğenin farklı olacağını garanti eden kod / performans gösterilmektedir. Aynı argümanlar için yukarıdaki orijinal koda (0.012 saniye) kıyasla çok daha yavaş (50.000 öğe için 6.65 saniye)

resim açıklamasını buraya girin


0

K (oK) , 14 bayt

Çözüm:

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

Çevrimiçi deneyin!

Misal:

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

Açıklama:

Spesifikasyon başına 3 örtülü girdi alır:

  • x, çıkış kümesindeki sayı sayısı,
  • y, alt sınır (dahil)
  • z, üst sınır (dahil)

{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

Notlar:

Ayrıca q/kdb+fazladan bir parantez seti içeren bir çokglot : {y+((-)x)?1+z-y}(16 bayt).


0

Axiom + kütüphanesi

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

Yukarıdaki f () işlevi, a> b ile f (n, a, b) durumunda boş listeyi hatalı olarak döndürür. Diğer geçersiz giriş durumlarında, Axiom penceresinde bir hata mesajı ile çalışmaz, çünkü argüman doğru tipte olmayacaktır. Örnekler

(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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.