如何生成總和固定的若干個隨機數?

比如生成5個非負的總和為100的隨機數。

很容易想到的方法是先生成一個0到100的隨機數m,再生成0到100-m的隨機數,如此類推,但是這樣的問題是大多數情況下的結果都是前面的數字比較大而後面的數字很小。

我覺得理論上這樣的演算法是可行的,但是為什麼結果看起來不夠隨機呢?

請問如何更均勻地生成這樣的隨機數?

--------------------------------

補充問題:如果要求每個數都在0到30範圍內呢?


同反對「隨便生成,然後按比例縮放」。

很多知友在吐槽不知道題主想要什麼分布,也有很多知友(也許包括題主)沒有搞清楚"均勻「。

題主在描述最後說到」請問如何更均勻地生成這樣的隨機數?「。

均勻的意思應該理解為」在樣本空間內概率密度處處相等的分布函數「。

有且只有這樣一種理解。

針對這裡的情況,樣本空間是指符合x_{i} geq 0 sum_{1}^{5}{x_{i} } =100的高維區域。在這個區域內,概率密度處處相等的分布才是均勻的。

「隨便生成,然後按比例縮放」會怎麼樣呢?動手算一算便知,放縮法的概率密度是不符合這個條件的。5個變數覺得麻煩的童鞋,可以算算用放縮法得到限制和的兩個變數,看看他們的概率密度和」均勻「有什麼不同。

易知,生成4個隨機數,然後作為分割點,取每段長度依次作為隨機數,是符合均勻定義的。

樓下有位寫代碼的同學,其實他恰恰用圖像展示出了前者不符合均勻,而後者符合均勻。而他可能認為在圖像中頻率應該是一條水平線,這才是均勻,因而得出了只有一半正確的結論。其實是不可能有頻率函數是一條水平線,和還能為定值的那種分布的。

請大家正確的理解均勻吧~~~~


0-100,分為5份,實際上是找到4個分界點

當然不能找一個,然後在剩下的裡面找第二個了,那樣會遞減

正確的方式是,直接隨機生成四個0-100的數,使其作為分界點,取得五個數字即可

如何驗證?

如此進行足夠多次,每一份的平均值在20附近就算成功了


反對「隨便生成,然後按比例縮放」

比如生成5個非負的總和為100的隨機數。總和固定,相當於少了一個自由度,也就是只有四個隨機數是自由的。

下面是一個思路僅供參考

1 generate 4 samples from your truncated distribution f(x), 0&2 sort them, for example 0&3 Your target random number would be

Y1=X1-0; Y2=X2-X1; Y3=X3-X2; Y4=X4-X3; Y5=100-X5


其實問題是你不知道你想要什麼樣的分布,所以實際上你提的問題不是良定義的。其他答主給出了兩種不錯的思路,但是你需要認識到一件事情:它們不等價(得到的分布是不同的)。

然後你加上不超過30的限制以後難度似乎大大增加了,他們的兩個演算法似乎都不能直接擴展到這種情況。

接下來我也來貢獻兩個不太好的思路吧。

1. 隨便找一個不帶30限制的演算法(參見其他答主),然後反覆生成直到所有數字不超過30。

這個演算法如果你只是限制為30的話,期望上重複不了幾次就會出解,但是對於別的參數組合可能會坑爹。

這個演算法的好處是你得到的分布就是原來演算法的分布被帶30的限制卡一下剩下的那個東西。如果原來是均勻的分布,現在還是。

2.

開個a數組,清零;
for (int i = 0; i &< 100; ++i) { j=a數組裡小於30的元素個數; r=rand_int(0, j); k=a數組裡第r個小於30的元素下標; ++a[k]; }

這個辦法有個特別的好處是你可以通過把這裡的隨機選元素變成加權隨機選元素來逼近幾乎所有你能想得到的分布。缺點是它慢死了。


隨機生成若干個0~1的浮點數,求總數後按比例分配,這樣能減少取整造成截斷誤差。轉成整數的時候需要注意:

  • 取整的時候要四捨五入
  • 最後一個數用 100 減去其他數得到,否則加起來可能會差 1

限定每個數在 30 以內的辦法是用上面的演算法生成一組數,如果滿足所有的數都小於 30 則輸出,否則重新生成。大概生成3-4次會得到一次你要的數。


你先要定分布才行啊,不然是耍流氓啊

如果要求0-30,的均勻分布的話,那就先取4個數,總和在70-100之間就把還剩多少作為第5個數,否則就重新取唄


題主搞亂了的一個事情似乎是「均勻」與「同分布」,假設你是在給一個遊戲設計一個隨機分贓的機制,那其實每個人分得錢數的分布一致應該就夠了吧。所以 @梁展瑞同學畫圖提到的第二種辦法,即隨機取四點作為切斷點其實是可取的,因為如果分別記錄五個變數,看他們分別的分布的話,大致是這個樣子

形狀比較怪異,然後另外想讓每個變數marginally更接近[10,30]上的均勻分布的話,可以取四個[10,30]上的均勻分布隨機變數,然後只在他們的和在[70,90]範圍內時取他們為前四項,方法很傻,速度上會有犧牲,結果並不是嚴格均勻但是比較接近了

私以為,題主真正覺得不可取的應該是類似「取四個[15,25]的均勻隨機變數然後用100減去其和作為第五個」這樣的naive方法,這樣方法得出的變數分布是這樣的

其實第五個變數為啥是這樣也挺好想的...不知道是否存在marginally uniform然後和又固定的變數組合,需要數學更好的同學們來回答了...


實際上,現在大家提出的兩個方法,生成的隨機數都不太均勻(其實要看怎麼定義這裡的「均勻」)。有圖為證,生成圖片的代碼在:https://gist.github.com/ZhanruiLiang/8f800d0db780cd45ba12

實驗方法:重複用要測試的方法生成T次,每次有n個隨機數,範圍在[0,s]。然後畫出這 Tn 個數字的出現頻率。這裡我取 s = 100, n = 5, T = 100000.

縮放法的分布:

取點然後排序的分布:

正在想一個真正是均勻分布的做法。


先隨機生成5個數,再分別乘以 100/sum 呢?


5個桶,100個球隨機扔進去

如果0-30的話,可以設置桶的容量為30


個人認為樓主的演算法沒有問題


關鍵要看你要的隨機是什麼分布。如果你要聯合分布是均勻的,即f(x_1,x_2,x_3,x_4,x_5) propto1, 先算邊際分布,然後再算f(x_2|f_1),f(x_3|x_2,x_1)...一步步抽樣。如果要求所有變數的邊際分布是均勻的,這種分布顯然不存在。E[X_i]=50,E[sum X_i]=250, 但他們的和是100,矛盾。


先假設固定這個「若干」 == a,接著用你的思路和 @文非尹的思路

先生成一個0到100的隨機數m,再生成0到100-m的隨機數,如此類推

然後用同樣方法取足量次數n,獲得一個類似n行a列的矩陣(不足的位數補0),然後每一行的數都打亂隨機排列。之後每一列的數求平均,得到a個平均數,其和正好為100。

如果不要求整數的話,該方法是絕對完美的。但如果要求整數的話就沒那麼簡單了。該方法的缺點是很難取到整數列,用絕大部分計算機的取整法的話(直接舍掉小數),取值會在(100-a, 100]之間,不過可以人為加一個while語句判斷再進一步減小誤差。

如最後m個平均數放在一個叫result[]的數組裡,取得的平均值為x(整數,並小於100),定義sum為求和函數:(python偽碼)

while x&<100:

i = random.randint(0,a-1)

result[i] +=1

x = sum(result)

這樣在a不太大的情況下基本確保了絕對隨機。如果a非常大的話,可以適當地把步長減小,即result[i]+=1改成諸如0.5, 0.1.....這樣更小的數,確保更多的數都有機會得到增加的機會,這樣結果分布會更平均。

當然這種方法最大的缺點是耗時比較長,尤其是要求取整並在a較大的情況下。

提示:如果要求取整並且「若干」也是不固定的話,a可以取任意隨機數。


n個均勻分布隨機數均勻分布的平均值應該是最大值的一半,所以想生成n個總和為m的隨機數可以先生成n-1個最大值為(2*m/n)的隨機數,最後一個數用m減去前面n-1個隨機數的總和

void rand(int *l, int n, int m)

{

int i;

for(i=0;i& {

l[i] = rand() % (2 * m / (n - i));

m -= l[i];

}

l[i] = m;

}

s = 100, n = 5, T = 5000000次結果,可以看出0~25之間的數出現的幾率基本一致,大於30的數出現幾率急劇減小


爪機見諒。

首先定義均勻,假設有向量(x1,x2,x3,x4,x5),有,x1+x2+x3+x4+x5 = 100且每一個x都大於0。那麼其實這裡就可以求這個向量的全集了,然後再在全集內隨機抽取就好。


按你的演算法得出5個數,然後把5個數順序打亂一下看起來不就沒規律了。


推薦閱讀:

隨機取一正整數n,其大於另一已知正整數m的概率是多少?
排序後的正態分布數列相鄰兩個數的差有什麼特點?也符合正態分布嗎?
現代幾何的方法在概率論研究上有什麼應用?
概率論和實變函數(測度論)有什麼聯繫?
三國殺中甄姬洛神的期望值是幾張牌?

TAG:演算法 | 數學 | 概率論 |