난수 리스트 생성, 합계 1
- 이 질문은 다음과 같은 이유로 합이 M인 N개의 난수를 얻는 것의 중복이 아닙니다.
- 대부분의 답변은 이론에 관한 것이지 이 질문에 답하기 위한 파이썬의 특정 코딩 솔루션이 아닙니다.
- 여기서 허용되는 답변은 이 질문에 답하는 중복 답변의 답변보다 5년 더 오래된 답변입니다.
- 중복 승인된 답변이 이 질문에 응답하지 않습니다.
N개의 난수 목록(100개라고 말함)을 어떻게 작성하면 그들의 합이 1이 될 수 있습니까?
로 난수 목록을 만들 수 있습니다.
r = [ran.random() for i in range(1,100)]
리스트의 합이 1이 되도록 수정하려면 어떻게 해야 합니까(확률 시뮬레이션을 위한 것입니다).
가장 간단한 해결책은 N개의 랜덤 값을 취하여 합으로 나누는 것입니다.
더 일반적인 솔루션은 numpy 단위로 사용할 수 있는 Dirichlet 분포를 사용하는 것입니다.
분포의 모수를 변경하여 개별 숫자의 "랜덤성"을 변경할 수 있습니다.
>>> import numpy as np, numpy.random
>>> print np.random.dirichlet(np.ones(10),size=1)
[[ 0.01779975 0.14165316 0.01029262 0.168136 0.03061161 0.09046587
0.19987289 0.13398581 0.03119906 0.17598322]]
>>> print np.random.dirichlet(np.ones(10)/1000.,size=1)
[[ 2.63435230e-115 4.31961290e-209 1.41369771e-212 1.42417285e-188
0.00000000e+000 5.79841280e-143 0.00000000e+000 9.85329725e-005
9.99901467e-001 8.37460207e-246]]
>>> print np.random.dirichlet(np.ones(10)*1000.,size=1)
[[ 0.09967689 0.10151585 0.10077575 0.09875282 0.09935606 0.10093678
0.09517132 0.09891358 0.10206595 0.10283501]]
주 매개 변수에 따라 디리클레 분포는 모든 값이 1./N에 가까운 벡터를 제공하거나(여기서 N은 벡터의 길이), 대부분의 벡터 값이 ~0이고 단일 1이 있을 벡터를 제공하거나, 이러한 가능성 사이에 무언가를 제공합니다.
EDIT(원답 후 5년):디리클레 분포에 대한 또 다른 유용한 사실은 감마 분포 랜덤 변수 집합을 생성한 다음 이들 변수의 합으로 나눈다면 자연스럽게 디리클레 분포를 얻을 수 있다는 것입니다.
이렇게 하는 가장 좋은 방법은 단순히 원하는 숫자의 목록을 만든 다음 모든 숫자를 합으로 나누는 것입니다.그들은 이런 식으로 완전히 무작위입니다.
r = [ran.random() for i in range(1,100)]
s = sum(r)
r = [ i/s for i in r ]
또는 @TomKealy가 제안한 것처럼 합계와 생성을 한 루프에 유지합니다.
rs = []
s = 0
for i in range(100):
r = ran.random()
s += r
rs.append(r)
성능을 는 가장빠성위사다용합다니음을해능을른을 사용하세요.numpy
:
import numpy as np
a = np.random.random(100)
a /= a.sum()
그리고 확률 분포에 대해 원하는 분포를 난수에 지정할 수 있습니다.
a = np.random.normal(size=100)
a /= a.sum()
타이밍 ----
In [52]: %%timeit
...: r = [ran.random() for i in range(1,100)]
...: s = sum(r)
...: r = [ i/s for i in r ]
....:
1000 loops, best of 3: 231 µs per loop
In [53]: %%timeit
....: rs = []
....: s = 0
....: for i in range(100):
....: r = ran.random()
....: s += r
....: rs.append(r)
....:
10000 loops, best of 3: 39.9 µs per loop
In [54]: %%timeit
....: a = np.random.random(100)
....: a /= a.sum()
....:
10000 loops, best of 3: 21.8 µs per loop
각 숫자를 총계로 나누면 원하는 분포를 얻을 수 없습니다.예를 들어, 두 개의 숫자를 사용할 경우 쌍 x, y = random.random.random.random.random.random.random은 0<=x<1, 0<=y<1>에서 균일하게 점을 선택합니다.이 점(x,y)을 (x,y)에서 원점까지의 선을 따라 x+y=1의 선으로 "연결"하는 합으로 나눕니다.근처의 점(0.5,0.5)이 근처의 점(0.1,0.9)보다 훨씬 더 가능성이 높습니다.
두 변수의 경우 x = 랜덤.random(), y = 1-x는 기하학적 선 세그먼트를 따라 균일한 분포를 제공합니다.
변수가 3개인 경우 입방체에서 임의의 점을 선택하고 원점을 통해 방사형으로 투영하지만 삼각형의 중심 근처의 점이 정점 근처의 점보다 더 가능성이 높습니다.결과 점은 x+y+z 평면의 삼각형에 있습니다.삼각형에서 점을 치우치지 않은 선택이 필요한 경우 스케일링은 좋지 않습니다.
문제는 n차원에서 복잡해지지만, N에 더하는 음이 아닌 정수의 모든 n-튜플 집합에서 균일하게 고른 다음 각각을 N으로 나누면 낮은 정밀도(그러나 모든 실험실 과학 팬들에게 높은 정확도!) 추정치를 얻을 수 있습니다.
저는 최근에 적당한 크기의 n, n을 위한 알고리즘을 생각해냈습니다.n=100 및 N=1,000,000에 대해 작동하여 6자리 랜덤을 제공합니다.내 답변 보기:
0과 1로 구성된 목록을 만든 다음 99개의 난수를 추가합니다.목록을 정렬합니다.연속적인 차이는 최대 1을 더한 구간의 길이가 됩니다.
저는 파이썬에 능숙하지 못합니다. 그러니 좀 더 파이썬적인 방법이 있다면 용서해 주십시오.하지만 의도가 분명하기를 바랍니다.
import random
values = [0.0, 1.0]
for i in range(99):
values.append(random.random())
values.sort()
results = []
for i in range(1,101):
results.append(values[i] - values[i-1])
print results
다음은 Python 3의 업데이트된 구현입니다.
import random
def sum_to_one(n):
values = [0.0, 1.0] + [random.random() for _ in range(n - 1)]
values.sort()
return [values[i+1] - values[i] for i in range(n)]
print(sum_to_one(100))
@pjs 솔루션 외에도 두 개의 매개 변수로 함수를 정의할 수 있습니다.
import numpy as np
def sum_to_x(n, x):
values = [0.0, x] + list(np.random.uniform(low=0.0,high=x,size=n-1))
values.sort()
return [values[i+1] - values[i] for i in range(n)]
sum_to_x(10, 0.6)
Out:
[0.079058655684546,
0.04168649034779022,
0.09897491411670578,
0.065152293196646,
0.000544800901222664,
0.12329662037166766,
0.09562168167787738,
0.01641359261155284,
0.058273232428072474,
0.020977718663918954]
숫자에 , 된 숫자는 임로선숫대즉자한최경설는임우려정, 생숫최소한는하을자값계소에한택된의성즉)min_thresh
),
rand_prop = 1 - num_of_values * min_thresh
random_numbers = (np.random.dirichlet(np.ones(10),size=1)[0] * rand_prop) + min_thresh
수만한 숫자num_of_values(num_of_values)를할 수 .num_values <= 1/min_thesh
)
따라서 기본적으로 최소 임계값에 대해 1의 일부를 고정한 다음 다른 부분에 난수를 만듭니다.추가합니다min_thesh
모든 숫자에 대해 합 1을 구합니다.min_thresh=0, methresh00.2로 3개의 숫자를 생성하려고 합니다.임의의 숫자 [1 - (0.2x3) = 0.4]로 채울 부분을 만듭니다.우리는 그 부분을 채우고 모든 값에 0.2를 더하므로 0.6도 채울 수 있습니다.
이것은 난수 생성 이론에 사용되는 표준 스케일링 및 시프트입니다.크레딧은 제 친구 Jel Vaishnav(SO 프로필이 있는지 확실하지 않습니다)와 @sega_sai에게 갑니다.
@sega_sai 답변에서 영감을 받아 최신의 권장 numpy 구현 [2022년 3월]
from numpy.random import default_rng
rng = default_rng()
rng.dirichlet(np.ones(10),size=1)
>>> array([[0.01279836, 0.16891858, 0.01136867, 0.17577222, 0.27944229,
0.06244618, 0.19878224, 0.02481954, 0.01478089, 0.05087103]])
참조:
- Numpy 랜덤 샘플링 문서
- 랜덤 생성기 설명서 및 랜덤 디리클레 생성기 설명서
100개의 난수를 생성하는 것은 어떤 범위인지는 중요하지 않습니다.생성된 숫자를 합하고 각 개체를 총계로 나눕니다.
다른 해결책은 랜덤을 사용하는 것입니다.선택 및 합계로 나누기:
import random
n = 5
rand_num = [random.choice(range(0,100)) for r in range(n)] # create random integers
rand_num = [i/sum(rand_num) for i in rand_num] # normalize them
다음을 통해 쉽게 수행할 수 있습니다.
r.append(1 - sum(r))
이 정의는 "목록의 각 요소를 목록의 합으로 표시"라는 정신에 따라 각 요소를 PLACE(또는 없음)로 반올림하여 길이 = PARTS, sum = TOTAL의 난수 목록을 생성합니다.
import random
import time
PARTS = 5
TOTAL = 10
PLACES = 3
def random_sum_split(parts, total, places):
a = []
for n in range(parts):
a.append(random.random())
b = sum(a)
c = [x/b for x in a]
d = sum(c)
e = c
if places != None:
e = [round(x*total, places) for x in c]
f = e[-(parts-1):]
g = total - sum(f)
if places != None:
g = round(g, places)
f.insert(0, g)
log(a)
log(b)
log(c)
log(d)
log(e)
log(f)
log(g)
return f
def tick():
if info.tick == 1:
start = time.time()
alpha = random_sum_split(PARTS, TOTAL, PLACES)
log('********************')
log('***** RESULTS ******')
log('alpha: %s' % alpha)
log('total: %.7f' % sum(alpha))
log('parts: %s' % PARTS)
log('places: %s' % PLACES)
end = time.time()
log('elapsed: %.7f' % (end-start))
결과:
Waiting...
Saved successfully.
[2014-06-13 00:01:00] [0.33561018369775897, 0.4904215932650632, 0.20264927800402832, 0.118862130636748, 0.03107818050878819]
[2014-06-13 00:01:00] 1.17862136611
[2014-06-13 00:01:00] [0.28474809073311597, 0.41609766067850096, 0.17193755673414868, 0.10084844382959707, 0.02636824802463724]
[2014-06-13 00:01:00] 1.0
[2014-06-13 00:01:00] [2.847, 4.161, 1.719, 1.008, 0.264]
[2014-06-13 00:01:00] [2.848, 4.161, 1.719, 1.008, 0.264]
[2014-06-13 00:01:00] 2.848
[2014-06-13 00:01:00] ********************
[2014-06-13 00:01:00] ***** RESULTS ******
[2014-06-13 00:01:00] alpha: [2.848, 4.161, 1.719, 1.008, 0.264]
[2014-06-13 00:01:00] total: 10.0000000
[2014-06-13 00:01:00] parts: 5
[2014-06-13 00:01:00] places: 3
[2014-06-13 00:01:00] elapsed: 0.0054131
pjs 방법의 정신으로:
a = [0, total] + [random.random()*total for i in range(parts-1)]
a.sort()
b = [(a[i] - a[i-1]) for i in range(1, (parts+1))]
소수점 이하로 반올림하려면 다음을 수행합니다.
if places == None:
return b
else:
b.pop()
c = [round(x, places) for x in b]
c.append(round(total-sum(c), places))
return c
언급URL : https://stackoverflow.com/questions/18659858/generating-a-list-of-random-numbers-summing-to-1
'programing' 카테고리의 다른 글
SQL Server 쿼리에서 정수를 10진수로 변환하는 방법은 무엇입니까? (0) | 2023.08.10 |
---|---|
C에서 소켓 연결 상태를 찾는 방법은 무엇입니까? (0) | 2023.08.05 |
XML 그리기 가능을 사용한 수직선 (0) | 2023.08.05 |
Docker 바탕 화면에서 데이터 볼륨 찾기(Windows) (0) | 2023.08.05 |
PHP DateTime __construct() 위치 x에서 시간 문자열(xxxxxxxx)을 구문 분석하지 못했습니다. (0) | 2023.08.05 |