코딩이 간단한 O (N + K * log (K)) 방식
인덱스를 교체하지 않고 무작위 샘플을 가져 와서 인덱스를 정렬하고 원본에서 가져옵니다.
indices = random.sample(range(len(myList)), K)
[myList[i] for i in sorted(indices)]
또는 더 간결하게 :
[x[1] for x in sorted(random.sample(enumerate(myList),K))]
최적화 된 O (N)-시간, O (1)-보조 공간 방식
대안으로 수학 트릭을 사용하고 myList
동적으로 변화하는 확률로 숫자를 선택하면서 왼쪽에서 오른쪽으로 반복적으로 이동할 수 있습니다 (N-numbersPicked)/(total-numbersVisited)
. 이 접근 방식의 장점 O(N)
은 정렬을 포함하지 않기 때문에 알고리즘 이라는 것입니다 !
from __future__ import division
def orderedSampleWithoutReplacement(seq, k):
if not 0<=k<=len(seq):
raise ValueError('Required that 0 <= sample_size <= population_size')
numbersPicked = 0
for i,number in enumerate(seq):
prob = (k-numbersPicked)/(len(seq)-i)
if random.random() < prob:
yield number
numbersPicked += 1
개념 증명 및 확률이 올바른지 테스트 :
5 시간 동안 1 조 개의 의사 난수 샘플로 시뮬레이션 :
>>> Counter(
tuple(orderedSampleWithoutReplacement([0,1,2,3], 2))
for _ in range(10**9)
)
Counter({
(0, 3): 166680161,
(1, 2): 166672608,
(0, 2): 166669915,
(2, 3): 166667390,
(1, 3): 166660630,
(0, 1): 166649296
})
확률은 실제 확률에서 1.0001보다 적게 차이가납니다. 이 테스트를 다시 실행하면 순서가 달라서 하나의 순서로 편향되지 않습니다. 더 적은을위한 샘플 테스트를 실행 [0,1,2,3,4], k=3
하고[0,1,2,3,4,5], k=4
있었다 유사한 결과를.
편집 : 사람들이 왜 잘못된 댓글을 투표하거나 찬성하는 것을 두려워하는지 잘 모르겠습니다 ... 아니요,이 방법에는 잘못된 것이 없습니다. =)
(또한 주석에있는 사용자 tegan의 유용한 참고 사항 : 이것이 python2 인 경우 여분의 공간에 대해 정말로 관심이 있다면 평소처럼 xrange를 사용하고 싶을 것입니다.)
edit : Proof : 크기 k
의 모집단 에서 하위 집합을 선택하는 균일 분포 (대체 없음)를 고려하면 임의의 지점 에서 'left'(0,1, ..., i-1)로 분할을 고려할 수 있습니다. 그리고 'right'(i, i + 1, ..., len (seq)). 왼쪽 알려진 하위 집합에서 선택했음을 감안할 때 나머지 매개 변수는 이제는 다르지만 오른쪽 알 수없는 하위 집합의 동일한 균일 분포에서 가져와야합니다. 특히, 선택한 요소 를 포함하는 확률 은 , 또는seq
len(seq)
i
numbersPicked
seq[i]
#remainingToChoose/#remainingToChooseFrom
(k-numbersPicked)/(len(seq)-i)
, 그래서 우리는 그것을 시뮬레이션하고 결과에 대해 반복합니다. (#remainingToChoose == #remainingToChooseFrom이면 나머지 모든 확률은 1이기 때문에 종료되어야합니다.) 이것은 동적으로 생성되는 확률 트리와 유사합니다. 기본적으로 이전 선택에 대한 조건을 지정하여 균일 한 확률 분포를 시뮬레이션 할 수 있습니다 (확률 트리를 성장할 때 현재 분기의 확률을 선택하여 이전 잎과 동일하게, 즉 이전 선택에 조건을 지정합니다. 이 확률은 균일하게 정확히 N / k입니다).
편집 : 티모시 방어막이 언급 저수지 샘플링 이 방법을 때의 일반화,len(seq)
알 수없는 경우 (예 : 생성기 표현식) . 특히 "알고리즘 R"로 표시된 것은 제자리에서 수행되는 경우 O (N) 및 O (1) 공간입니다. 첫 번째 N 요소를 가져 와서 천천히 교체하는 것이 포함됩니다 (유도 증명에 대한 힌트도 제공됨). 또한 wikipedia 페이지에서 찾을 수있는 유용한 분산 변형 및 저수지 샘플링의 기타 변형이 있습니다.
편집 : 아래에 더 의미 상 명백한 방식으로 코드화하는 또 다른 방법이 있습니다.
from __future__ import division
import random
def orderedSampleWithoutReplacement(seq, sampleSize):
totalElems = len(seq)
if not 0<=sampleSize<=totalElems:
raise ValueError('Required that 0 <= sample_size <= population_size')
picksRemaining = sampleSize
for elemsSeen,element in enumerate(seq):
elemsRemaining = totalElems - elemsSeen
prob = picksRemaining/elemsRemaining
if random.random() < prob:
yield element
picksRemaining -= 1
from collections import Counter
Counter(
tuple(orderedSampleWithoutReplacement([0,1,2,3], 2))
for _ in range(10**5)
)
random.sample
정렬 하고 싶지 않습니까?