Conmajia
Jan. 29th, 2019
早在2012年,我曾經針對 C# System.Random 不同的初始化方案專門做過一次試驗,得出了單次默認初始化即可獲得質量很好的隨機數的結論。可是這么多年過去,C# 從2.0升到了4.7,還能在網上看到很多新手(甚至是老鳥)被一些想當然的奇怪思想誤導,費時費力地脫褲子放屁。

有些人總覺得用點額外的、生僻的玩意兒會顯得很炫技,很厲害。正如修真小說里,稀有的古代神器多半比量產的現代裝備牛逼,在編程的時候,用上那么幾個不常用,或者當前問題環境下一般沒人用的patterns or callbacks什么的,仿佛就擁有了高貴的血統,讓人不明覺厲。然而再華麗的古羅馬戰車,也比不上穿梭在街頭巷尾的五菱宏光;再炫酷的開發技巧,也敵不過沒頭沒腦的overdesign。
以前有種說法,覺得用隨機性相當大的GUID做種子來初始化Random就能得到比new Random()更隨機的輸出結果。這種不知從何而來的莫名自信一直延續到了今天。

且不說這堆一脈相承的智障操作對性能的影響,很顯然,他們對真隨機數、偽隨機數這些概念有點誤會,對計算機生成隨機數的原理也不甚了然[1]。他們只是看到那一長串變幻無窮的GUID后,心中的虔誠感油然而生。然而回過神,迎面撲來的卻是現實的一盆刺骨冷水:對於 Random,任何多余的初始化都不過是拖后腿的累贅而已。
一頓操作猛如虎,一看戰績0-5
已經足夠好的 Random
一個隨機數發生器好壞的評判標准,首先看它在值域的分布概率是不是符合均勻分布(uniform distribution),也就是說它取得任何一個值的概率都是相同的。其次看它的性能。不管你的Random用到了多么炫酷亮瞎狗眼的神技,只要它的性能不夠,狂吃資源,那它一定是個辣雞。如果產生一個隨機數需要5分鍾,那么任何音樂軟件的“隨機播放”都將變得索然無味。而對於那些熱衷於使用花里胡哨的玩意兒來做隨機數種子的方案,性能永遠不可能超過默認構造函數。畢竟你每次生成隨機數的時候都必須把這幫家伙初始化一遍,否則就和默認初始化完全一樣,這些花里胡哨將變得毫無意義。數學理論不說了,先讓秀兒們看看默認的Random到底夠不夠隨機。
Case #1 的數據,是用默認構造函數初始化生成的數據統計概率直方圖。\(x\) 軸是輸出的隨機數數值,范圍從0-99,共100個數。\(y\) 軸是對應數值出現的概率。每次試驗生成 10,000,000(一千萬)個隨機數,每個case進行10次試驗,共生成100,000,000(一億)個隨機數,得到單次試驗平均用時0.76秒。令 \(\tau=0.76\),后面的case都按類似的方法進行,並以 \(\tau\) 作為性能基准,排除計算平台的干擾。
new Random(),用時:$\tau=0.76$
看,典型的均勻分布。在一千萬大數據量的支撐下,100個可能值每個的輸出概率都達到了完美的1%,帶內波動小於 ±0.000,05,還有什么可挑剔的呢?
那么,現在開始試驗備受推崇的GUID初始化隨機數發生器了。當然,這句話也可以拗口地說成隨機生成隨機數發生器(generate random generator randomly),反正都是秀嘛。
找個比較簡單的GUID例子:

依然生成 10,000,000個隨機數,主程序內容不變,只需要修改 GetRandomNumber():
new Random(GUID),用時:$52\tau$
沒錯,這確實能得到輸出效果不錯的隨機數。但是,它的帶內波動達到了 ±0.000,2,差不多是 Case #1 的4倍,完全談不上更好。那么性能呢?在效果近似,波動更大的情況下,Case #2 用時達到了 #1 的52倍(39.5秒),這么辣雞的性能還好意思吹您
的 ![]()
呢?
再來看看更秀的,本文最開始那張圖里的例子,用的是GUID×Time×計數器這種秀破天際的初始化方案:
new Random(GUID * Time * count),用時:$56\tau$
您可省省吧!
這段代碼的作者甚至還想到了用unchecked略微優化一下代碼的健壯性。上手就來秀優化,全然不顧丫的壓根兒從算法上就有毛病。習慣成自然,可以猜測他平時在業務工作中沒少這么干。然后是hashcode、time tick各種key一頓花里胡哨得到一個seed來初始化Random。可是這又有什么卵用呢?朋友?為了這個和 Case #1 幾乎一樣效果的輸出結果花掉了 56倍(43秒)的計算時間,您覺得合適嗎??
代碼的質量不是看它用了多少技巧,秀了多少知識,只要花點功夫,這並不難做到。恰恰相反,用最簡單的辦法實現適當功能和良好的性能,才是最困難的。一段代碼是不是實用,你也不可能靠它的字數來判斷,任何結論,要么理論推導,要么試驗驗證。那些被人奉為經典的半吊子大神的話,可能往往只是他們放的狗屁而已。
The End. \(\Box\)
function paintCanvas(canvas,data,title=''){var chart=echarts.init(document.getElementById(canvas));chart.setOption({title:{text:title},tooltip:{},legend:{data:['']},dataZoom:[{id:'dataZoomX',type:'slider',xAxisIndex:[0],filterMode:'filter'}],xAxis:{type:'category',data:data.x},yAxis:{scale:true},series:[{name:'',type:'bar',data:data.y}],grid:{top:10}})} paintCanvas('canvas-uniform',{x:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99],y:[0.009999,0.0099791,0.0099851,0.010045,0.0099323,0.0100227,0.009973,0.0099823,0.0099812,0.0100117,0.0099555,0.0100219,0.0099808,0.0099235,0.0100256,0.0100195,0.0100347,0.0099649,0.0099957,0.0100012,0.0099983,0.0100443,0.0100185,0.0100028,0.0099762,0.0099998,0.010038,0.0099765,0.0100109,0.01005,0.01003,0.0100054,0.0099526,0.010001,0.0100172,0.0100067,0.0099779,0.0100547,0.0100519,0.009982,0.0100288,0.0100559,0.0100406,0.0099738,0.009963,0.0099715,0.0099753,0.0099745,0.0099862,0.0099928,0.0100211,0.009992,0.0100048,0.0099731,0.0100005,0.0100157,0.0100208,0.0099976,0.0099595,0.01004,0.0100246,0.0100253,0.0100169,0.0099769,0.0099607,0.0100206,0.010013,0.0099873,0.0099567,0.0099987,0.0099625,0.0100595,0.0099338,0.0100009,0.0100181,0.0099867,0.0100141,0.010015,0.0099953,0.0100089,0.0100287,0.0100257,0.0100045,0.0100001,0.0100012,0.0100357,0.0099458,0.0100448,0.0099926,0.0099496,0.0100401,0.0099849,0.0099666,0.0100041,0.0100289,0.0099873,0.009978,0.0099922,0.0099923,0.0100045]}); paintCanvas('canvas-guid-complex',{x:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99],y:[0.0099588,0.0099963,0.0099867,0.0100279,0.0099967,0.010009,0.0100268,0.0099682,0.0100487,0.0099889,0.0099977,0.0100182,0.0099335,0.0100526,0.009973,0.0100066,0.0100051,0.0100047,0.0099966,0.0099876,0.0100194,0.0099806,0.0100175,0.0099505,0.0100212,0.0100264,0.009992,0.0100031,0.009983,0.010008,0.0099936,0.0099991,0.0099872,0.0100077,0.0099912,0.010037,0.0099905,0.010021,0.009914,0.0100222,0.0100325,0.0100102,0.0099669,0.0100526,0.0100275,0.0099605,0.009992,0.0100189,0.009977,0.0099784,0.0100042,0.0100493,0.0100071,0.0100028,0.0099897,0.0099899,0.0100025,0.0100122,0.0099531,0.0099807,0.0099904,0.0100486,0.0099704,0.0100065,0.010033,0.0100121,0.0099809,0.0100112,0.0099883,0.0100166,0.0100253,0.0100342,0.0100222,0.0099733,0.010014,0.0100402,0.0100118,0.0099748,0.0099949,0.0099435,0.0100269,0.0100096,0.0100035,0.0099935,0.0100527,0.010029,0.0099903,0.0100189,0.0099248,0.0099613,0.0100053,0.010031,0.0099276,0.0099528,0.0100068,0.0099687,0.0100655,0.0100228,0.0099488,0.0100112]}); paintCanvas('canvas-guid',{x:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99],y:[0.009969,0.0099814,0.010007,0.0099734,0.0100348,0.0099411,0.010003,0.0099791,0.0100223,0.0100081,0.010043,0.0099691,0.0100782,0.0099857,0.0100318,0.0099852,0.0099031,0.0100022,0.0099955,0.0100051,0.0100461,0.0100305,0.0099732,0.010001,0.0100225,0.0099725,0.0099751,0.0099508,0.0100818,0.0100174,0.0099828,0.0099956,0.0100132,0.0099434,0.0100041,0.0100116,0.0100056,0.0099988,0.0099917,0.0099825,0.0100184,0.0100239,0.0100129,0.010003,0.0099602,0.0099697,0.0100037,0.0100334,0.0099445,0.0099953,0.0100062,0.0100498,0.0099619,0.0100386,0.0100099,0.0099716,0.009962,0.009943,0.010002,0.0100403,0.0100361,0.0099616,0.0100443,0.0100754,0.0099895,0.0100405,0.0100066,0.0100115,0.0099725,0.0100516,0.0099657,0.0099764,0.0100091,0.0100129,0.0100226,0.0099746,0.009995,0.0100378,0.0099761,0.010022,0.0099874,0.0099882,0.0099836,0.0100138,0.0100195,0.0100456,0.0099339,0.0099185,0.0099723,0.0099984,0.0100329,0.010027,0.0099859,0.0100038,0.0099408,0.0100047,0.0100058,0.0100318,0.010004,0.0100517]});
所有的真隨機數發生器都需要專用硬件支持,它們中絕大部分受到發明專利保護。
System.Random基於 Donald E. Knuth 的減隨機數生成器算法實現,從實用角度而言,隨機程度已經足夠。 ↩︎
