春節摸彩程式展示
Ching-Tang Tseng
Hamilton, New Zealand
2 February 2019
過年了,有個負責華人組織的朋友,請我設計一個亂數抽獎程式,在春節聯歡會上使用,今年的除夕是 2 月 4 日。我答應了,而且耗費了不少時間才落定程式。
本來,一個簡單到直接執行 『 100 rand . 』 就能解決的問題,何須如此大費周章地設計程式?
實踐才是檢驗真理的唯一標準,不要以為問題很簡單,就輕率地回答這個問題。身體力行做事情,才能真正體會出必須設計這個程式的道理。
在我個人現行使用的 FORTH 系統中,亂數產生程式是我自行建立的,建立的方法,以前在網文中已經談過許多次,這篇文章不擬再次討論,這一次,討論正確使用亂數的方法。
拿春節摸彩情況的實際要求,檢驗亂數產生程式的效果,很快就能發現,直接使用產生亂數的函數指令,不能解決抽出的獎號不得重複的問題。
假設參與抽獎的人數總共為 100 人,100 是個隨意假設的數字。在我自己設計的系統中,直接執行『 100 rand . 』,可以得到一個介於 0 到 99 之間隨機出現的數字。理論上,如果真夠隨機,那麼,執行 100 次這樣的指令,就能得出 100 個以混亂狀態出現的整套數字。在評鑑亂數產生程式的術語上,稱這種現象為『全週期』,也就是每個該出現的數字都出現過了。
我們在使用亂數產生函數 rand 時,必須認真看待它的性能,直接用,不會有全週期,亂用,甚至可能會遭遇到亂週期或根本沒有週期。產生這種現象的起因,在於亂數產生程式是根據系統單整數的處理範圍而設計的,基本上,它只在一個單整數的範圍內盡量的亂,然後才提供給別的場合使用。這麼一來,在 0 到 100 的範圍內以 rand 取得亂數時,所得亂數,算是次生亂數,不是原生亂數。
次生亂數的產生規則,有人採用只取原生亂數的最後幾位數字來獲得,單純地只用 mod 運算,就能達到目的。也有人採用只取原生亂數的前幾位數字來獲得,單純地使用 um* nip 運算,也能達到目的。這樣的產生規則,就造成了上述抽出獎號會重複的問題。舉取前兩位數字的例子來說,原生亂數在 64 位元的系統中為 18 位數,太多了,表示不便,我們改以只有五位數來等效說明,那麼,凡為 37xxx 的數字,如: 37125, 37998, 37452, ...等等等,總共有一千個,實際上是 18 位數,那就更多了。經過 100 rand 處理後,都會只得 37 。如此一來,原生夠亂的亂數,這樣使用,就不亂了。
我在實踐程式設計時,遭遇到這樣的問題,於是設計了解決問題的程式,其核心執行的部份,仍然使用 rand 亂數函數,這是一個活用亂數產生程式 rand 的實用範例。
最終落定的程式,使用了恰當的指令名稱,並標示指令執行前後,堆疊上數字的變化需求。因此,下列程式的執行內容,不需要解釋。總抽獎人數,就是那個可以被調整內容的 setting 變數,程式現成設定為 100 。
100 value setting
: LuckyArray
create 1+ cells allot ( n -- )
does> swap cells + ( n -- addr )
;
1000 LuckyArray table
0 value NewNumber
0 value LuckyAmount
0 value flag
: review ( -- )
0 to flag
LuckyAmount 1+ 1
do
NewNumber I table @ =
if -1 to flag leave then
loop ;
: unique ( -- n )
begin
setting rand 1+ to NewNumber
review flag 0=
until
1 +to LuckyAmount
NewNumber LuckyAmount table !
NewNumber ;
: ListTable ( -- )
cr
setting 1+ 1
do
i table @ 4 .R
i 10 mod 0=
if cr then
loop
cr ;
: reset ( -- )
0 to NewNumber 0 to LuckyAmount 0 to flag
setting 1+ 1
do
0 i table !
loop
;
: >table ( n -- )
1 +to LuckyAmount LuckyAmount table !
ListTable
;
: test ( -- )
reset
randomize
ListTable cr cr
setting 1+ 1
do
unique drop
loop
ListTable cr cr
reset
;
: main ( n -- )
page randomize
1+ 1
do
unique dup . (.)
400 ms
girlsay
key drop
loop
ListTable
;
\ Usage:
\ 1. reset or test
\ 2. 3 main
上列程式完成測試後,我接受了新的設計要求,程式的執行畫面,必須在現場以投影機放大,顯示在螢幕上。抽出得獎的數字,應該同步以語音輸出。另外,需要配上前置簡短音樂,博取效果。
數字顯示時,同步以語音輸出,是我的系統原本就已擁有的性能,很容易完成。
顯示數字如果仍只採用慣常的顯示方式,字太小了,不夠清楚。於是,我花了點時間在網上搜索,安裝了一個簡易的大號字體顯示軟體,效果不是很好,但可以比不用時清楚。
每次抽出號碼前,都希望配上一些簡短的前置音樂,要尋找這樣的資源,比較耗時間。我在網上過濾了超過一千首以上的音樂,才勉強取得夠用的資源。後來,還試著自行擷取貝多芬第五交響曲中大約五秒鐘的震撼音樂,耗費了一些時間,完成設計。
FORTH 原本就是一種適合應用於即時控制的程式語言,文首的影音展示,告訴大家這樣的效果。但是,被控制的資源,不是 FORTH 的範疇,而且,網上擁有無數個能被這樣控制的資源,所以,它們都不在此處專門介紹。
沒有留言:
張貼留言