교체없이 무작위 샘플링


10

범위에서 그려진 고유 한 난수 세트를 출력하는 함수를 만듭니다. 세트의 요소 순서는 중요하지 않지만 (정렬 될 수도 있음), 함수가 호출 될 때마다 세트의 내용이 다를 수 있어야합니다.

이 함수는 원하는 순서대로 3 개의 매개 변수를받습니다.

  1. 출력 세트의 수
  2. 하한 (포함)
  3. 상한 (포함)

모든 숫자가 0 (포함)에서 2 31 (제외) 범위의 정수라고 가정하십시오 . 원하는 방식으로 출력을 전달할 수 있습니다 (콘솔에 쓰기, 배열 등).

심사

기준에는 3 개의 R이 포함됩니다.

  1. 런타임- 자유롭게 또는 쉽게 사용할 수있는 컴파일러가있는 쿼드 코어 Windows 7 시스템에서 테스트 (필요한 경우 링크 제공)
  2. 견고성 -함수가 코너 케이스를 처리하거나 무한 루프에 빠지거나 잘못된 결과를 생성합니다-유효하지 않은 입력의 예외 또는 오류가 유효합니다
  3. 임의성 -임의 분포로 쉽게 예측할 수없는 임의의 결과를 생성해야합니다. 내장 난수 생성기를 사용하는 것이 좋습니다. 그러나 명백한 편견이나 예측 가능한 패턴이 없어야합니다. Dilbert의 회계 부서에서 사용 하는 난수 생성기 보다 우수해야합니다.

강력하고 임의적 인 경우 런타임으로 떨어집니다. 강력하거나 무작위로 실패하면 입장이 크게 손상됩니다.


출력이 DIEHARD 또는 TestU01 테스트 와 같은 것을 통과해야 합니까 , 아니면 임의성을 어떻게 판단합니까? 아, 그리고 코드가 32 또는 64 비트 모드에서 실행되어야합니까? (그것은 최적화를 위해 큰 차이를 만들 것입니다.)
Ilmari Karonen

TestU01은 아마도 조금 가혹한 것 같습니다. 기준 3은 균일 한 분포를 의미합니까? 또한 왜 반복되지 않는 요구 사항입니까? 그것은 특히 무작위가 아닙니다.
Joey

@ 조이, 확실합니다. 교체없이 무작위 샘플링입니다. 아무도 목록의 다른 위치가 독립적 인 랜덤 변수라고 주장하지 않는 한 아무런 문제가 없습니다.
피터 테일러

아 참으로. 그러나 샘플링의 무작위성을 측정하기 위해 잘 확립 된 라이브러리와 도구가 있는지 확실하지 않습니다 :-)
Joey

@IlmariKaronen : RE : Randomness : 그 전에 구현이 너무나도 무례한 것을 보았습니다. 그들은 치열한 편견을 가지고 있거나 연속 주행에서 다른 결과를 산출 할 능력이 없었습니다. 그래서 우리는 암호화 수준의 무작위성을 말하는 것이 아니라 Dilbert의 회계 부서의 난수 생성기 보다 무작위 입니다.
Jim McKeeth

답변:


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 개 숫자)와 넓은 범위에서 숫자를 샘플링하는 경우 모두 잘 수행됩니다. .

파이썬의 내장 생성기에서 임의의 품질에 대해 확실하지 않지만 일정 범위에서 균일하게 정수를 생성 할 수있는 생성기에서 교체하는 것은 비교적 간단합니다.


1
파이썬은 Mersenne Twister를 사용 하므로 비교적 괜찮습니다.
ESultanik

1

파이썬 2.7

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

내장 무작위 방법을 사용하여 자신의 입장이 무엇인지 확실하지 않지만 여기서는 계속 진행됩니다. 좋고 짧다

편집 : 방금 range ()가 큰 목록을 만들고 싶지 않다는 것을 알았습니다. 메모리 오류가 발생합니다. 다른 방법이 있는지 알 수 있습니다 ...

edit2 : 범위가 잘못된 기능이었습니다 .xrange가 작동합니다. 최대 정수는 실제로 2**31-1파이썬입니다

테스트:

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

1

최소값과 최대 값 사이의 고유 한 랜덤 정수 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

루비> = 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

아르 자형

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

1

질문이 맞지 않습니다. 균일 한 샘플링이 필요합니까? 균일 한 샘플링이 필요한 경우 평균 복잡도 O ( s 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로 다시 작성할 수 있습니다. 이 알고리즘의 복잡성은 다음에서 논의된다 : Rouzankin, PS; Voytishek, AV 무작위 선택을위한 알고리즘 비용. 몬테 카를로 방법 Appl. 5 (1999), no. 1, 39-54. http://dx.doi.org/10.1515/mcma.1999.5.1.39

이 문서에서 평균 복잡도가 동일한 다른 알고리즘을 살펴볼 수 있습니다.

그러나 균일 한 샘플링이 필요하지 않고 모든 샘플링 된 숫자가 달라야 만하면 상황이 크게 바뀝니다. 평균 복잡도 O ( s ) 를 갖는 알고리즘을 작성하는 것은 어렵지 않습니다 .

균일 샘플링에 대해서는 P. Gupta, GP Bhattacharjee를 참조하십시오. (1984) 교체없이 무작위 샘플링을위한 효율적인 알고리즘. International Journal of Computer Mathematics 16 : 4, 201-209 쪽. DOI : 10.1080 / 00207168408803438

Teuhola, J. 및 Nevalainen, O. 1982. 교체없이 무작위 샘플링을위한 두 가지 효율적인 알고리즘. / IJCM /, 11 (2) : 127–140. DOI : 10.1080 / 00207168208803304

마지막 논문에서 저자는 해시 테이블과 알고리즘이 있다고 주장 사용 O ( )의 복잡성을. 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
이것은 코드 골프가 아니라 가장 빠른 코스이므로 목표는 가장 짧은 코드가 아닌 작업을 수행하는 가장 빠른 코드를 생성하는 것입니다. 어쨌든, 당신은 정말 그런 인수에서 항목을 선택 할 필요가 없습니다, 당신은 그래서 순서를 결정할 수 있습니다 {⍵+⍺?⎕-⍵}하한되고 프롬프트가 상한과 오른쪽 인수를 위해이고, 충분합니다
우리엘

0

스칼라

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

스칼라는 빠른 바이트 코드를 생성하지만 시작 시간이 필요하기 때문에 두 번째 줄은 0에서 100 사이의 15 개 값으로 200 개의 테스트를 실행합니다. 따라서 200부터 시작하여 0에서 100 사이의 15 개 값은 더 많은 시간을 소비합니다.

2Ghz 단일 코어의 샘플 :

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

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

논리:

세트의 크기가 예상 크기인지 여부를 확인하고 범위를 추가하고 (max-min) 범위에서 임의의 숫자를 재귀 적으로 선택하고 최소값을 추가하고 확인합니다.

비평:

  • 큰 범위의 작은 샘플에는 빠르지 만 작업이 샘플의 거의 모든 요소 (1000 개 중 999 개 숫자)를 선택하는 경우 이미 세트에있는 숫자를 반복해서 선택합니다.
  • 이 질문에서 나는 4에서 8까지 10 개의 고유 번호를 취하는 것과 같이 충족 할 수없는 요청에 대해 위생 처리 해야하는지 확실하지 않습니다. 이제 끝없는 루프가 발생하지만 사전 점검으로 쉽게 피할 수 있습니다. 요청했다.

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

아르 자형

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

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 최근에, 나는 큰 메모리 문제로 작업 해 왔으며, 그 해결책은 실제로 더 나은 머신에서 실행되고 있습니다. 작업 구성의 제한은 메모리가 아닌 프로세서와 관련이 있으므로 규칙 남용입니까? 아, 나는 엉덩이입니다. 코드 골프 도전 이라고 생각 했지만 실제로는 가장 빠른 코드입니다. 나는 추측한다?
Andreï Kostyrka

0

자바 스크립트 (외부 라이브러리 사용) (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를 args로 허용합니다. 크기 n의 모음을 만들고 각 요소를 최소 / 최대 기준에 맞는 임의의 숫자로 매핑합니다. 기본 JS 배열로 변환하여 리턴하십시오. 나는 이것을 5,000,000 크기의 입력에서도 실행했으며 별개의 변환을 적용한 후에도 여전히 5,000,000 개의 요소를 보여주었습니다. 이것이 확실성을 보장하기에 충분하지 않다는 데 동의하면 대답을 업데이트 할 것입니다

아래 이미지에 몇 가지 통계가 포함되어 있습니다 ...

여기에 이미지 설명을 입력하십시오

편집 : 아래 이미지는 모든 요소가 구별되도록 보장하는 코드 / 성능을 보여줍니다. 동일한 args (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

공리 + 라이브러리

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 창에 하나의 오류 메시지와 함께 실행되지 않습니다. 예

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