我們的一般編寫隨機如下:
-- 獲取當前系統時間(秒)作為隨機種子,有時也會使用毫秒:os.time() * 1000 math.randomseed(os.time()) --[[ 參數的方式: 1. 不帶參數調用時,獲取的是[0,1)范圍內的隨機浮點數 2. 帶一個整型參數時,獲取的是[1,n]范圍內的隨機整數 3. 帶兩個整型參數m,n時,獲取的是[m,n]范圍內隨機整數 注意Lua5.3以后,參數一定要為整數,否則會返回錯誤: bad argument #1 to 'random' (number has no integer representation) ]] math.random(10, 30)
為避免偽隨機,為何要使用os.time()獲取系統時間秒數作為種子呢?接下來我們看下lua中的random,randomseed在C下的實現,參考資料:
lua源碼下載:http://www.lua.org/ftp/
在線lua C庫:http://www.lua.org/source/5.1/
Math庫:http://lua-users.org/wiki/MathLibraryTutorial
我們可以摘錄下lmathlib.c下關於random,randomseed C實現相關:
// 摘錄:http://www.lua.org/source/5.1/lmathlib.c.html static const luaL_Reg mathlib[] = { {"random", math_random}, {"randomseed", math_randomseed}, {NULL, NULL} }; static int math_random (lua_State *L) { /* the `%' avoids the (rare) case of r==1, and is needed also because on some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX; switch (lua_gettop(L)) { /* 檢測參數 */ case 0: { /* no arguments */ lua_pushnumber(L, r); /* Number between 0 and 1 */ break; } case 1: { /* only upper limit */ int u = luaL_checkint(L, 1); luaL_argcheck(L, 1<=u, 1, "interval is empty"); lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */ break; } case 2: { /* lower and upper limits */ int l = luaL_checkint(L, 1); int u = luaL_checkint(L, 2); luaL_argcheck(L, l<=u, 2, "interval is empty"); lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */ break; } default: return luaL_error(L, "wrong number of arguments"); } return 1; } static int math_randomseed (lua_State *L) { srand(luaL_checkint(L, 1)); return 0; }
看到如上代碼,我們可以了解:
1. 未調用srand,其next隨機種子為1, 此時rand() 產生的隨機數每次運行都會與上一次相同。
2. 若next隨機種子設置的越大,偽隨機的可能性就越小。比如:
-- 隨機種子數值為100,每次執行的結果都是:1 1 1 3 4 2 4 1 4 1 math.randomseed(100) -- 隨機種子數字為os.time(),每次執行結果會發生改變 math.randomseed(os.time()) local randNum = {} for i = 1,10 do table.insert(randNum,math.random(1,5)) end print(table.concat(randNum," "))
在cocos2d-lua下的function.lua中實現了新的隨機數種子:
-- 摘錄於: src/cocos/cocos2d/functions.lua -- 設置隨機數種子 function math.newrandomseed() local ok, socket = pcall(function() return require("socket") end) if ok then // 關於*1000,在32位機器上,可能數值超過機器的最大值限定,而使得設置種子無效 math.randomseed(socket.gettime() * 1000) else math.randomseed(os.time()) -- 此處可修改為數值反轉 end math.random() math.random() math.random() math.random() end
但是你的程序如果在1秒內有多次操作(密碼鎖),產生的隨機數依然存在偽隨機。因此我們可以這樣:
-- 將os.time()獲取的系統秒數數值翻轉(低位變高位),再取高6位,這樣即使time變化很小 -- 但是由於低位變高位,種子數值就會變化很大,這樣1秒內進行多次運行的話,效果會好些 local next = tostring(os.time()):reverse():sub(1, 6) math.randomseed(next)