合計が1になるように、N個(たとえば100個)の乱数のリストを作成するにはどうすればよいですか?
で乱数のリストを作ることができます
r = [ran.random() for i in range(1,100)]
リストの合計が1になるようにこれをどのように変更しますか(これは確率シミュレーション用です)。
合計が1になるように、N個(たとえば100個)の乱数のリストを作成するにはどうすればよいですか?
で乱数のリストを作ることができます
r = [ran.random() for i in range(1,100)]
リストの合計が1になるようにこれをどのように変更しますか(これは確率シミュレーション用です)。
回答:
最も簡単な解決策は、実際にN個のランダムな値を取り、合計で割ることです。
より一般的な解決策は 、numpyで入手可能なDirichletディストリビューション http://en.wikipedia.org/wiki/Dirichlet_distributionを使用することです。
分布のパラメーターを変更することにより、個々の数値の「ランダム性」を変更できます。
>>> 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になるか、それらの可能性の間に何かを与えます。
編集(元の回答から5年後):ディリクレ分布に関するもう1つの有用な事実は、ガンマ分布の確率変数のセットを生成し、それらをそれらの合計で割ると、自然に得られることです。
[0,1/s)
)。スケーリングは分布を変更せず、圧縮するだけなので、最初にスケーリングされていない分布とまったく同じように均一になります。この答えはさまざまな分布を示しますが、そのうちの1つだけが均一です。これが意味をなさない場合は、例を実行し、いくつかのヒストグラムを見て明確にします。また、ガウス分布(np.random.normal
)で同じことを試してください。
これを行う最良の方法は、必要な数の数値のリストを作成し、それらをすべて合計で割ることです。このように完全にランダムです。
r = [ran.random() for i in range(1,100)]
s = sum(r)
r = [ i/s for i in r ]
または、@ TomKealyによって提案されているように、合計と作成を1つのループに保持します。
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
各数値を合計で割ると、希望する分布が得られない場合があります。たとえば、2つの数値の場合、ペアx、y = 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)に近いポイントよりもはるかに可能性が高くなります。
2つの変数の場合、x = random.random()、y = 1-xは、幾何学的線分に沿って一様分布を与えます。
3つの変数を使用して、立方体内のランダムな点を選択し、(放射状に、原点を介して)投影しますが、三角形の中心に近い点は、頂点に近い点よりも可能性が高くなります。結果の点は、x + y + z平面の三角形上にあります。その三角形のポイントを公平に選択する必要がある場合、スケーリングは適切ではありません。
問題はn次元で複雑になりますが、非負の整数のすべてのnタプルのセットから均一に選択することで、低精度(ただし、すべての実験科学ファンにとっては高精度!)の推定値を得ることができます。 N、次にそれらのそれぞれをNで除算します。
私は最近、適度なサイズのn、Nに対してそれを行うアルゴリズムを思いつきました。6桁のランダムを与えるには、n = 100およびN = 1,000,000で機能するはずです。で私の答えを参照してください:
0と1で構成されるリストを作成し、99個の乱数を追加します。リストを並べ替えます。連続する違いは、合計が1になる間隔の長さです。
私はPythonに堪能ではないので、これを行うためのよりPython的な方法がある場合はご容赦ください。ただし、意図が明確であることを願っています。
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
これがPython3の更新された実装です。
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のソリューションに加えて、2つのパラメーターを使用して関数を定義することもできます。
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_values <= 1/min_thesh
)
したがって、基本的に、最小しきい値として1の一部を修正してから、他の部分に乱数を作成します。我々は追加min_thesh
などのために合計1を取得するために、すべての番号に:min_thresh = 0.2で、あなたは3つの数字を生成したいと言うことができます。乱数で埋める部分を作成します[1-(0.2x3)= 0.4]。その部分を埋めて、すべての値に0.2を追加するので、0.6も埋めることができます。
これは、乱数生成理論で使用される標準のスケーリングとシフトです。クレジットは私の友人のJeelVaishnav(SOプロファイルがあるかどうかはわかりません)と@sega_saiに送られます。
「リスト内の各要素をリストの合計で除算する」という精神で、この定義により、長さ= PARTS、合計= TOTALの乱数のリストが作成され、各要素はPLACES(またはなし)に丸められます。
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