무작위 비 감소 시퀀스 샘플링


20

입력 : 코드에 편리한 어떤 형태로든 두 개의 정수 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 아, 알겠습니다. 효율적으로 계산하는 방법을 알아내는 것은 재미있는 도전이었습니다.
ETHproductions

각 시퀀스의 출력 확률이 동일해야한다는 요구 사항은 32 비트 또는 48 비트 상태 일 수있는 대부분의 정원 종류 PRNG를 만족시킬 수 없습니다. Wolfram에 따르면 1, ..., 100의 535 퀸 틸리 온 20 요소 서브 시퀀스가 ​​있습니다 (이 중 몇 개가 감소하지 않는지 확인하지 않았습니다). 2 ^ 64는 18 개의 퀸 틸리 온입니다.
Sinan Ünür

답변:


1

사실 , (14) 12 바이트

이 답변을 기반으로 Emigna의 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

파이썬, 89 바이트

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

생성 증가 순서보다는 비 - 감소 이것은 단지 임의의 부분 집합 하나는 간단 것 k사이의 숫자 1n, 분류.

그러나 연속되는 숫자 사이의 각 간격을 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의 사이에 서열을 증가, 1n+k-1. 그런 순서를 세기 위해 별과 바 인수 에서 동일한 bijection이 사용됩니다 .

이 코드는 python 함수를 사용합니다.이 함수 는 입력 목록에서 바꾸지 않고 샘플 random.sample을 가져옵니다 k. 정렬하면 순서가 증가합니다.


이것은 인상적입니다. 방법에 대한 설명을 추가해 주시겠습니까?

지금 바빠서 나중에 설명하겠습니다.
xnor

나는 90 바이트를 세었다 ... (그리고 import*1 바이트를 절약 할 수있다 )
Rod

@로드 감사합니다, 나는 그것에 대해 잊었다.
xnor


7

파이썬, 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).

파이썬 random에는 Bernoulli 변수가 내장되어 있지 않은 것 같습니다 : 1 확률로, 그렇지 않으면 0. 따라서 0에서 1 사이의 임의의 값 random이 아래 로 떨어지는 지 확인합니다 k/(n+k-1). 파이썬 2는 float 나누기의 비율이므로, 대신 분모를 곱합니다 : k>random()*(n+k-1).


numpy가 여기서 도움이 될까요?

@Lembik 좋은 생각이지만 가져와야 할 것 같습니다 numpy.random. 너무 깁니다.
xnor

5

자바 스크립트 (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++]

마지막으로 kj 에 저장 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()호출 을 제거하여 2 바이트를 절약 $argv[2]하고 $k대신 인수에 대한 액세스를 단축하도록 설정
  • foreach에서 인덱스를 제거하여 2 바이트를 절약했습니다. 이는 단순히 증가하는 숫자이기 때문입니다. $i대신 각 반복을 늘리십시오.

첫 번째 JavaScript와 이제 PHP 모든 최고의 과학 프로그래밍 언어 :) 감사합니다.

@Lembik, 천만에요. 기본 PRNG를 사용합니다. 암호로 사용하지 마십시오 . :)
지난
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.