javascript - 樂透 lottery - 號碼產生器

本文想要分享,如何用javascript制作一個抽籤程式,然後是以樂透號碼為範例。

一、

首先,我們必須要知道如何使用亂數產生器,在javascript裡面 Math.random() 可以取得一個介於 0 ~ 1 (不包含1) 的浮點數亂數,而在實際使用上我們希望得到的是某個正整數,也就是編號幾號的東西,所以我們得動點手腳運算。

比如現在有一個陣列 arr[10],裡面放著 arr['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'] 十樣東西,我們要挑出一個,所以我們希望得到的亂數是 arr 的 index: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 其中一個號碼,於是做這樣的運算 Math.floor(Math.random() * 10),比如 0.452 * 10 = 4.52 去掉小數位數得到 4,比如 0.999 * 10 = 9.99 去掉小數位數得到 9。* 10 是因為有10個選項,而Math.floor是去掉小數位數只取整數部份的涵式。

這樣的運算會使得每個東西的機會均等嗎?

當亂數 0 ~ 0.099... * 10 得到 0 ~ 0.99... 取 0
當亂數 0.1 ~ 0.199... * 10 得到 1 ~ 1.99... 取 1
當亂數 0.2 ~ 0.299... * 10 得到 2 ~ 2.99... 取 2
當亂數 0.3 ~ 0.399... * 10 得到 3 ~ 3.99... 取 3
當亂數 0.4 ~ 0.499... * 10 得到 4 ~ 4.99... 取 4
當亂數 0.5 ~ 0.599... * 10 得到 5 ~ 5.99... 取 5
當亂數 0.6 ~ 0.699... * 10 得到 6 ~ 6.99... 取 6
當亂數 0.7 ~ 0.799... * 10 得到 7 ~ 7.99... 取 7
當亂數 0.8 ~ 0.899... * 10 得到 8 ~ 8.99... 取 8
當亂數 0.9 ~ 0.999... * 10 得到 9 ~ 9.99... 取 9

均等的! 所以這算式OK

二、

接下來,當我們要從一堆東西中抽出一個組合,就表示說我們抽的東西不可以重複。

那麼最簡單的方法不是網路上常見的那些,萬一亂數重複,再跑一次,因為這樣程式會浪費不少時間,你可以假設你每次亂數跑出來的又剛好是重複的,那你的程式不就無窮無盡,所以那方法不好。

那最好的方法是什麼呢?就是直接把已經抽過的東西排除在下一次抽取之外。就絕對不會重複!怎麼排除?簡單說就是放到一邊去...

程式概念是這樣,舉例我有10個東西要抽3個:





演算法:

for (i = 0; i < 挑選數量; i++) {
抽號 = i + Math.floor(Math.random() * (選項總數 - i));
紀錄(Array[抽號]);
if (i != 抽號) 交換(Array, i抽號);
}

因為我們在抽取位置的過程中是使用亂數產生器,所以號碼被放在甚麼位置、是不是有依序排序,是沒有差別的。

三、

最後,定義我們要抽的內容物是什麼,以及要抽幾個。

下面是我寫的樂透號碼產生器 Source Code:


/* !
 * 樂透號碼產生器
 */

function lots(options) {
if (typeof options == 'object') $.extend(this.options, options);
}

lots.prototype = {
options: {
tokens: [] // 選取物集合
},

// 設定選取物集合
setTokens: function(tokens) {
if (tokens instanceof Array) this.options.tokens = tokens;
},

// 增加選取物集合
addToken: function(token) {
this.options.tokens.push(token);
},

// 從集合內挑選出指定數量的物品
getTokens: function(num) {
var result = [];
if (num <= this.options.tokens.length) {
var i, target;
for (i = 0; i < num; i++) {
target = i + Math.floor(Math.random() * (this.options.tokens.length - i));
result.push(this.options.tokens[target]);
if (i != target) switchNumber(this.options.tokens, i, target);
}
}
return result;
}
};


//
function lottory(options) {
if (typeof options == 'object') $.extend(this.options, options);
}

lottory.prototype = {
options: {
minNumber: 1, // 預設最小選擇號碼
maxNumber: 49, // 預設最大選擇號碼

minSPNumber: 1, // 預設最小特別號
maxSPNumber: 6, // 預設最大特別號

tokenNum: 6, // 預設選取數量

hasSP: false // 是否有特別號
},

// 根據設定產生一組號碼
genNumbers: function() {
var result = {};

// 檢查設定的號碼範圍是否合理
if (this.options.minNumber < this.options.maxNumber) {

// 可選擇的數量
var choiceNum = this.options.maxNumber - this.options.minNumber + 1;

// 選擇數小於可選擇數 OK
if (this.options.tokenNum <= choiceNum) {
var i, target, myLots = new lots();

// 產生選取號
for (i = this.options.minNumber; i <= this.options.maxNumber; i++) {
myLots.addToken(i);
}

// 亂數選取號碼
result.numbers = myLots.getTokens(this.options.tokenNum);

// 亂數選取特別號
if (this.options.hasSP) {
result.sp = this.options.minSPNumber + Math.floor(Math.random() * (this.options.maxSPNumber - this.options.minSPNumber + 1));
}
}
}
return result;
}
};


// 交換Array中的兩個元素
function switchNumber(arr, a, b) {
var temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}


// 測試使用
function testLott() {
var $rArea = $('#rArea').empty(),
lott = new lottory({
minNumber: 1,
maxNumber: 49,
tokenNum: 6,
hasSP: false
});

// 產生一組號碼
var result = lott.genNumbers();
// 將號碼遞增排序一下
result.numbers = result.numbers.sort(function(a, b) {return (a < b) ? -1 : (a > b) ? 1 : 0;});

// 顯示結果
$rArea.append($('<div/>').html('號碼: ' + result.numbers.join(', ')));
if (result.hasOwnProperty('sp')) $rArea.append($('<div/>').html('特別號: ' + result.sp));
}

留言

這個網誌中的熱門文章

HTML - CSS - footer floating toolbar bottom 瓢浮置底的工具列

自己設計讓網頁支援多國語系的架構