一次生活引發的“Lua隨機數生成”問題

Preface

其實是打算寫個隨機性語言選擇器的。我和小夥伴打算每天換一門語言來對話,於是就採用隨機函數的方法去幫助我們去選擇!這次比較匆忙,腳本就暫且這樣,以後優化,文章美化就暫時這樣,以後再調整。
這個隨機數生成問題目前主要是分成4個部分。最常見的,一次改進的,二次改進的和三次改進。以下主要改進的是randomseed的取值~

程序1:初始的最常見的隨機數生成

-- 常見的隨機數生成
math.randomseed(os.time())
for i=1, 7 do
    print(math.random())
end  

一次改進

改進的核心:
可以查看這篇文章,裏面有說明random()的 seed 很小或者seed 變化很小,產生的隨機序列仍然很相似。Lua 隨機數生成問題,點我

核心的語句如下:

-- 該語句是作用是:將os.time()的結果反轉,然後取高6位,這樣seed的變化就大了,
math.randomseed(tonumber(tostring(os.time()):reverse():sub(1, 6)))

以下是用來幫助選擇語言的實例代碼:

-- 全局變量,在這裏控制概率的大小
English = 13
Cantonese = 7
Japanese = 8
Korean = 67

-- 隨機函數
function rnd()
    local ret = 0
        -- 基於系統時間的隨機數種子,用這種方式防止極端時間內造成隨機數相同的情況
        -- 事實上,這個還是不行,最多隻是減輕症狀,後文再細說)
    math.randomseed(tonumber(tostring(os.time()):reverse():sub(1, 6)))
    -- 因爲這個隨機函數有點不好,所以取第三個信任度比較高
    for i=1, 3 do
        n = math.random(1, 10001)
        ret = n
    end
    return ret
end

-- 對於隨機函數粗來的結果進行處理,通過取餘來決定哪種語言
while true do
    local number = rnd()
    if number % Cantonese == 0 then
        print("隨機數爲:"..number)
        print("粵語")
        break
    elseif number % English == 0 then
        print("隨機數爲:"..number)
        print("English")
        break
    elseif number % Japanese == 0 then
        print("隨機數爲:"..number)
        print("日本語")
        break
    --[[
    elseif number % Korean == 0 then
        print("隨機數爲:"..number)
        print("한국어")
        break
    ]]
    end
end

這個程序還是有問題,時間緊迫,先放着!先去洗澡了
其實,這個問題還是沒有解決~因爲,經過測試發現,在Lua5.3裏面(其他版本未經測試),在極其短的時間裏面,生成的隨機序列仍然相同~還是不能達到根本的要求。


二次改進:

-- 調用sockt庫,從而達到毫秒級精度
local socket = require("socket")
math.randomseed(socket.gettime())
while true do
    for i=1, 3 do
        n = math.random()
    end
    -- 依舊取第三個數,輸出後發現差別可能隨機數會有很多的差別
    -- 說明:因爲socket.gettime()得到的結果是0.xxxxxxxxx級別的,無法作爲程序中的種子用,所以放大
    print(math.floor(n * 1000001))
end

輸出結果樣例:(部分)

944548
118388
148720

863934
417030
699109
202577
950752
172985
150584
111708

793783
48523
8192
721530
558812
807733
364529
57704
950531
737050
434112
866802
155902
336599
326696

722702
992281
477198
424833

842600
675044
40556
87856
680879
236654
454290
536366
433633
319076
343280

617953
936876

輸出後,發現雖然這次差別度很大,變化相差很大,可以達到隨機選擇語言的需求了。但是對於,紅色的標記的序列來說,差別沒有達到理想中的狀態(我不是處女座也不是雙魚座的!


三次改進

此次改進是基於第一次改進的理念改進的,將得到的序列進行逆序取高位,這樣效果應該會好很多。代碼如下:

local socket = require("socket")
math.randomseed(socket.gettime())
while true do
    for i=1, 3 do
        n = math.random()
    end
    print(tonumber(tostring(math.floor(n * 1000001)):reverse()))
end

樣例輸出:

558083
7541
886742
27599
528329
444804
469048
986654
825257
47004
223707
696051
828321
270441
928724
265446
57508
754028
799111
724897
2945
326045
207465
184182
786509
170548
47116
888255
361378

這樣就有個別幾個沒有連續重複的了~從統計學角度來說,有也是在允許範圍的吧~(弄完這個,感覺可能沒有必要!是的,感覺!其實,二次改進的蠻好的

最後,改進的實例代碼如下:

-- 全局變量,在這裏控制概率的大小
English = 11
Cantonese = 7
Japanese = 8
Korean = 67

-- 隨機函數
function rnd()
    local socket = require("socket")
    local ret = 0

    -- 用socket.gettime()獲取更加精確的時間,math.floor()取其整數部分,省略小數部分
    -- 轉換成string,逆序
    -- 再轉換成number
    local sockt_number = tonumber(tostring(math.floor(socket.gettime() * 1000001)):reverse())
    math.randomseed(sockt_number)
    -- 因爲這個隨機函數有點不好,所以取第三個信任度比較高
    for i=1, 3 do
        n = math.random(1, 10001)
        ret = n
    end
    return ret
end

-- 對於隨機函數粗來的結果進行處理,通過取餘來決定哪種語言
while true do
    local number = rnd()
    print("隨機數爲:"..number)
    if number % Cantonese == 0 then
        print("粵語")
        break
    elseif number % English == 0 then
        print("English")
        break
    elseif number % Japanese == 0 then
        print("日本語")
        break
    --[[
    elseif number % Korean == 0 then
        print("한국어")
        break
    ]]
    end
end

改進前,因爲while的永真循環,程序運行時間極其短暫,所以隨機數取的是一樣的,取餘操作都是在做無用功。改進過後,隨機數改進後的程序,就可以超快地得到結果。當然了,也可以對求餘操作進行改進,更快地得到求餘後的結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章