[python] 주어진 (숫자) 분포로 난수 생성

다른 값에 대한 확률이있는 파일이 있습니다.

1 0.1
2 0.05
3 0.05
4 0.2
5 0.4
6 0.2

이 분포를 사용하여 난수를 생성하고 싶습니다. 이를 처리하는 기존 모듈이 있습니까? 자체적으로 코딩하는 것은 매우 간단합니다 (누적 밀도 함수 작성, 임의의 값 [0,1] 생성 및 해당 값 선택). 이것은 일반적인 문제 여야하며 누군가가 함수 / 모듈을 생성 한 것 같습니다. 그것.

생일 목록 (표준 random모듈의 배포를 따르지 않음)을 생성하고 싶기 때문에 이것이 필요합니다 .



답변

scipy.stats.rv_discrete당신이 원하는 것일 수도 있습니다. values매개 변수 를 통해 확률을 제공 할 수 있습니다 . 그런 다음 rvs()분포 객체 의 방법 을 사용하여 난수를 생성 할 수 있습니다.

코멘트에 유진 Pakhomov에 의해 지적, 당신은 또한 전달할 수 p에 키워드 매개 변수 numpy.random.choice(), 예를

numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])

Python 3.6 이상을 사용 random.choices()하는 경우 표준 라이브러리에서 사용할 수 있습니다 . Mark Dickinson답변을 참조하십시오 .


답변

Python 3.6부터는 Python의 표준 라이브러리에 솔루션이 random.choices있습니다.

사용법 예 : OP 질문에있는 것과 일치하는 모집단과 가중치를 설정해 보겠습니다.

>>> from random import choices
>>> population = [1, 2, 3, 4, 5, 6]
>>> weights = [0.1, 0.05, 0.05, 0.2, 0.4, 0.2]

이제 choices(population, weights)단일 샘플을 생성합니다.

>>> choices(population, weights)
4

선택적 키워드 전용 인수를 k사용하면 한 번에 둘 이상의 샘플을 요청할 수 있습니다. 이것은 random.choices샘플을 생성하기 전에 호출 될 때마다 수행 해야하는 준비 작업이 있기 때문에 유용 합니다. 한 번에 많은 샘플을 생성하면 준비 작업을 한 번만 수행하면됩니다. 여기서 우리는 백만 개의 샘플을 생성하고 collections.Counter우리가 얻는 분포가 우리가 준 가중치와 대략 일치하는지 확인하는 데 사용 합니다.

>>> million_samples = choices(population, weights, k=10**6)
>>> from collections import Counter
>>> Counter(million_samples)
Counter({5: 399616, 6: 200387, 4: 200117, 1: 99636, 3: 50219, 2: 50025})


답변

CDF를 사용하여 목록을 생성하면 이진 검색을 사용할 수 있다는 이점이 있습니다. 전처리를 위해 O (n) 시간과 공간이 필요하지만 O (k log n)에서 k 개의 숫자를 얻을 수 있습니다. 일반적인 파이썬리스트는 비효율적이기 때문에 array모듈 을 사용할 수 있습니다 .

일정한 공간을 고집하면 다음을 수행 할 수 있습니다. O (n) 시간, O (1) 공간

def random_distr(l):
    r = random.uniform(0, 1)
    s = 0
    for item, prob in l:
        s += prob
        if s >= r:
            return item
    return item  # Might occur because of floating point inaccuracies


답변

아마 늦었을 수도 있습니다. 그러나 매개 변수를 numpy.random.choice()전달하여을 사용할 수 있습니다 p.

val = numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])


답변

(좋아요, 나는 당신이 수축 포장을 요구한다는 것을 알고 있지만, 아마도 자체 개발 솔루션은 당신의 취향에 충분히 간결하지 않았을 것입니다. 🙂

pdf = [(1, 0.1), (2, 0.05), (3, 0.05), (4, 0.2), (5, 0.4), (6, 0.2)]
cdf = [(i, sum(p for j,p in pdf if j < i)) for i,_ in pdf]
R = max(i for r in [random.random()] for i,c in cdf if c <= r)

나는이 표현의 결과를 눈으로 보아 이것이 효과가 있는지 의사에게 확인했다.

sorted(max(i for r in [random.random()] for i,c in cdf if c <= r)
       for _ in range(1000))


답변

나는 커스텀 연속 분포에서 무작위 샘플그리는 솔루션을 썼습니다 .

나는 당신과 비슷한 유스 케이스 (예 : 주어진 확률 분포로 임의의 날짜를 생성)에 이것을 필요로했습니다.

당신은 단지 기능 random_custDist과 라인이 필요합니다 samples=random_custDist(x0,x1,custDist=custDist,size=1000). 나머지는 장식입니다 ^^.

import numpy as np

#funtion
def random_custDist(x0,x1,custDist,size=None, nControl=10**6):
    #genearte a list of size random samples, obeying the distribution custDist
    #suggests random samples between x0 and x1 and accepts the suggestion with probability custDist(x)
    #custDist noes not need to be normalized. Add this condition to increase performance. 
    #Best performance for max_{x in [x0,x1]} custDist(x) = 1
    samples=[]
    nLoop=0
    while len(samples)<size and nLoop<nControl:
        x=np.random.uniform(low=x0,high=x1)
        prop=custDist(x)
        assert prop>=0 and prop<=1
        if np.random.uniform(low=0,high=1) <=prop:
            samples += [x]
        nLoop+=1
    return samples

#call
x0=2007
x1=2019
def custDist(x):
    if x<2010:
        return .3
    else:
        return (np.exp(x-2008)-1)/(np.exp(2019-2007)-1)
samples=random_custDist(x0,x1,custDist=custDist,size=1000)
print(samples)

#plot
import matplotlib.pyplot as plt
#hist
bins=np.linspace(x0,x1,int(x1-x0+1))
hist=np.histogram(samples, bins )[0]
hist=hist/np.sum(hist)
plt.bar( (bins[:-1]+bins[1:])/2, hist, width=.96, label='sample distribution')
#dist
grid=np.linspace(x0,x1,100)
discCustDist=np.array([custDist(x) for x in grid]) #distrete version
discCustDist*=1/(grid[1]-grid[0])/np.sum(discCustDist)
plt.plot(grid,discCustDist,label='custom distribustion (custDist)', color='C1', linewidth=4)
#decoration
plt.legend(loc=3,bbox_to_anchor=(1,0))
plt.show()

지속적인 사용자 정의 배포 및 개별 샘플 배포

이 솔루션의 성능은 확실하지 않지만 가독성을 선호합니다.


답변

다음을 기준으로 항목 목록을 작성하십시오 weights.

items = [1, 2, 3, 4, 5, 6]
probabilities= [0.1, 0.05, 0.05, 0.2, 0.4, 0.2]
# if the list of probs is normalized (sum(probs) == 1), omit this part
prob = sum(probabilities) # find sum of probs, to normalize them
c = (1.0)/prob # a multiplier to make a list of normalized probs
probabilities = map(lambda x: c*x, probabilities)
print probabilities

ml = max(probabilities, key=lambda x: len(str(x)) - str(x).find('.'))
ml = len(str(ml)) - str(ml).find('.') -1
amounts = [ int(x*(10**ml)) for x in probabilities]
itemsList = list()
for i in range(0, len(items)): # iterate through original items
  itemsList += items[i:i+1]*amounts[i]

# choose from itemsList randomly
print itemsList

목표 목록을 더 작게 만들기 위해 최대 공약수로 양을 정규화하는 것이 최적화 일 수 있습니다.

또한 이것은 흥미로울 수 있습니다.