http://dev.csdn.net/author/yanjun_1982/b682d53ae78846a19eb0b7751a250750.html
也許很多人會(huì)奇怪為什么使用LUA的時(shí)候,第一個(gè)隨機(jī)數(shù)總是固定,而且常常是最小的那個(gè)值,下面我就簡(jiǎn)要的說(shuō)明一下吧,說(shuō)得不好,還請(qǐng)諒解。我現(xiàn)在使用的4.0版本的LUA,看的代碼是5.0的,呵呵
LUA4.0版本中的自帶函數(shù)庫(kù)中有兩個(gè)關(guān)于隨機(jī)數(shù)的函數(shù),一個(gè)是random,一個(gè)是randomseed。random有兩個(gè)參數(shù),用來(lái)設(shè)置隨機(jī)數(shù)的范圍,比如random(1,100)設(shè)置隨機(jī)數(shù)的范圍為1至100之間。由于C中所產(chǎn)生的隨機(jī)序列是固定的,并且第一個(gè)隨機(jī)數(shù)比較小,只有41。LUA重新設(shè)計(jì)了random函數(shù),使得它可以產(chǎn)生范圍固定的隨機(jī)數(shù),但由于LUA的random只是封裝了C的rand函數(shù),使得random函數(shù)也有一定的缺陷,那就是如果random的兩個(gè)輸入?yún)?shù)的值相差很小的時(shí)候,那么隨機(jī)序列的第一個(gè)隨機(jī)數(shù)就會(huì)和第一個(gè)輸入?yún)?shù)很接近,比如第一次調(diào)用random(1,100)的時(shí)候,返回值肯定是1,只有相差大于799時(shí),如random(1,800)第一次調(diào)用才會(huì)返回2,也是很接近1。
由于這個(gè)原因,為了實(shí)現(xiàn)真正的隨機(jī),那么第一次就不能讓玩家調(diào)用random函數(shù),不然玩家就可以獲得一些低概率的東西了。比如if random(1,100) == 1 then ...... do,看起來(lái)是1%的的概率,但是第一次執(zhí)行的時(shí)候是100%成立的,存在一定的隱患。解決這個(gè)問(wèn)題的方法有兩個(gè),一就是第一次random函數(shù)不能讓玩家執(zhí)行,二就是使用randomseed先設(shè)一個(gè)隨機(jī)種子。對(duì)于第一種方法,可能還是有一定的風(fēng)險(xiǎn),畢竟隨機(jī)序列還是固定的,玩家第一次調(diào)用random的時(shí)候還是得到有規(guī)律的返回值。第二種方法比較安全,在服務(wù)器啟動(dòng)的時(shí)候設(shè)置一個(gè)隨機(jī)種子,讓系統(tǒng)產(chǎn)生的隨機(jī)序列不相同,但使用randomseed的時(shí)候也還要注意一個(gè)問(wèn)題,那就是做種子的數(shù)要足夠的大,大于10000就行了。不然randomseed所產(chǎn)生的隨機(jī)序列的第一個(gè)值還是很小。原因是randomseed是直接封裝了C的srand,如果種子的值太小,那么srand所產(chǎn)生的序列和默認(rèn)序列(srand(1)所產(chǎn)生的序列)是相差不大的,序列的第一個(gè)值還是很小。
因此,只要在服務(wù)器啟動(dòng)的時(shí)候調(diào)用一下randomseed(GetTime())就可以解決這個(gè)問(wèn)題了。
還要補(bǔ)充一下,LUA中產(chǎn)生隨機(jī)數(shù)的算法還是有一些問(wèn)題,比如執(zhí)行random(1,3276700),它返回的值最后兩位必為0。這是由LUA本身的隨機(jī)函數(shù)算法決定的。
還是簡(jiǎn)要介紹一下LUA中random函數(shù)的實(shí)現(xiàn)方法吧,主要由源碼中的下面兩行實(shí)現(xiàn):
lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX;
lua_pushnumber(L, (int)floor(r*(u-m+1))+m);
其中m為random函數(shù)的第一個(gè)參數(shù),u為第二個(gè)參數(shù)。由上面的代碼可以看出,如果u-l太小,那么當(dāng)r也很小的時(shí)候,r*(u-m+1)就會(huì)很小(小于1),那么再經(jīng)過(guò)floor運(yùn)算,最經(jīng)結(jié)果就是m。這就可以解釋為什么random產(chǎn)生的第一個(gè)隨機(jī)數(shù)常常會(huì)很接近m。再來(lái)看看當(dāng)m為0,u為327670的時(shí)候會(huì)怎樣。在上面的代碼里,RAND_MAX是一個(gè)宏,它的值是32767,也就是C語(yǔ)言中rand函數(shù)可以返回的最大值(不同的操作系統(tǒng)可能會(huì)有不一樣的最大值)。當(dāng)m為0,u為327670的時(shí)候,那么返回值就是floor(r*(327671)+0),我們?cè)偌僭O(shè)LUA與平臺(tái)無(wú)關(guān)并且rand不會(huì)返回32767(上面用%避免了這個(gè)問(wèn)題),那么r就可以簡(jiǎn)化為rand()/RAND_MAX,代入上式為floor(rand()*327671/32767)+0,就算rand()的返回值是32766,最終的結(jié)果也只有327660.99996......,經(jīng)過(guò)floor運(yùn)算后,最后那位數(shù)必為0。呵呵,我叫這樣的隨機(jī)數(shù)就偽隨機(jī)數(shù)中的偽隨機(jī)數(shù)。實(shí)際上面的公式是不允許化簡(jiǎn)的,即不能簡(jiǎn)單地把r代入r*(u-m+1),至于為什么,呵呵,因?yàn)閞的值并不是rand()/RAND_MAX的值,r是double類(lèi)型的,所以它只是一個(gè)和rand()/RAND_MAX很接近的數(shù)。
引用請(qǐng)注明出處。作者:yanjun_1982 日期:2006年10月11日