openresty及lua的隨機函式

冷侃發表於2019-03-18

我們都知道,所謂的隨機都是偽隨機,隨機的結果是由隨機演算法和隨機種子決定的。

所以,當我們沒有初始化的時候,如果直接使用math.random(),那麼出來的值肯定是每次都一樣,因為種子等於0。

因此,我們都會在程式啟動前,我們先呼叫一下種子

--隨機種子初始化
math.randomseed(tostring(os.time()):reverse():sub(1, 6))

但是,這一切在operensty的多程式裡面被打破了,因為隨機以後的因子。。各個程式不共享。。

如果,有2個程式,那麼意味著,同樣的值會出現兩遍,比如在遊戲裡面開寶箱,很容易被玩家抓到規律。

好吧,除非將隨機因子放入共享記憶體,並且自己實現隨機演算法,不然的話。。math.random是廢了!

好了,不需要隨機種子的隨機方法,我想起了。。/dev/random

以下文件摘自https://zh.wikipedia.org/wiki//dev/random

由於不可抗拒原因,大家可以看看截圖

我們只要知道這樣的隨機方式,不可預測的真隨機,來自硬體的因子,並且記得

  • /dev/random是阻塞的
  • /dev/urandom是非阻塞的

那麼,對我們而言/dev/urandom是夠的,所以。。我還是把程式碼貼一下就好了

local MAX_UINT = math.pow(2,32)

--由於隨機函式是非常頻繁的,所以,還是做了一個隨機池在那邊,先隨機一大部分放著
local cache_results = {}
local cache_index = 0

local table_getn = table.getn

--種子不需要初始化了
local function init()
end

--從系統隨機檔案中讀取
local function urandom()
    --看下快取裡面還有沒有
    local this_count = table_getn(cache_results) - cache_index
    if this_count <= 0 then
        --每次讀取128個整數,512個位元組快取下來
        local COUNT = 1024
        local frandom = assert(io.open("/dev/urandom", "rb"))
        local s = frandom:read(4 * COUNT)
        assert(s:len() == 4 * COUNT)

        for i=1,COUNT do
            --讀取4個位元組作為一個整數
            local v = 0
            for c = 1, 4 do
                v = 256 * v + s:byte(i*c)
            end
            cache_results[i] = v
        end
        io.close(frandom)

        --重新把
        this_count = table_getn(cache_results)
        cache_index = 0
    end

    cache_index = cache_index + 1
    return cache_results[cache_index]
end

local rand = urandom

--隨機一個0-1之間的浮點數
local function randFloat()
    return rand()/(MAX_UINT+1)
end

--隨機給定範圍[a,b]的整數
local function randInt(a, b)
    assert(b >= a)
    local v = rand()
    return a + math.floor((b-a+1)*randFloat())
end

return {
    init = init,
    rand = rand,
    urandom = urandom,
    randInt = randInt,
    randFloat = randFloat
}

 

相關文章