這段時間工做巨多,直到今天才有時間寫點東西。但是要輸出點什麼比較好呢?這時候看到了隔壁桌面上放着一張機打的寫着一組一組數字的紙,有了!那要不抽個獎吧!javascript
抽獎,講究一個隨機,只要把隨機搖號解決了不就無論什麼規則都能抽了?因此咱們須要一個能夠產生隨機數的函數。不只要隨機數,咱們還須要的是一個閉區間隨機取整數的函數。因此前端
const random = (m, n) => m + Math.floor(Math.random() * (n - m))
複製代碼
沒吃過豬肉還見過豬跑,號碼確定是抽一個少一個,因此根本不存在隨機數能相同的狀況!因此這個方法是不能用的。那麼,拿來抽的方法確定得知足三個條件:java
關於隨機不重複我最早想到的是利用sort
對數組隨機打散的方法。這個時候可能會有其餘聲音:typescript
要不要這麼水?
sort
用來排序的不懂?數組
水不水不知道,看一下MDN。瀏覽器
arr.sort([compareFunction])dom
- 若是 compareFunction(a, b) 小於 0 ,那麼 a 會被排列到 b 以前;
- 若是 compareFunction(a, b) 等於 0 , a 和 b 的相對位置不變。備註: ECMAScript 標準並不保證這一行爲,並且也不是全部瀏覽器都會遵照(例如 Mozilla 在 2003 年以前的版本);
- 若是 compareFunction(a, b) 大於 0 , b 會被排列到 a 以前。
- compareFunction(a, b) 必須老是對相同的輸入返回相同的比較結果,不然排序的結果將是不肯定的。
so函數
// arr 數組,num 取個數
const random = (arr:number[], num: number) =>
arr.sort(() => Math.random() - 0.5).splice(0, num)
複製代碼
首先,咱們須要經過百度查一下業界知名抽獎活動典型的遊戲規則:優化
某抽獎遊戲分爲紅色區和藍色區,紅色區由1-33共三十三個號碼組成,藍色區由1-16共十六個號碼組成。須要在紅色區選擇6個號碼和在藍色區選擇1個號碼合成一次有效號碼組。ui
做爲賭狗前端,提取這段話幾個關鍵點:
別說了我腦子裏又有函數了
// ...random
const dualColor = () => {
const reds = [1, 2, ..33]
const blues = [1, 2, ..16]
const red = random(reds, 6)
const blue = random(blues, 1)
return [red, blue]
}
複製代碼
這個時候咱們看一眼方法,你這方法不對啊!以紅色爲例子,咱們的函數表達結果是:直接把號碼打散後流出前 6 個,而遊戲規則指出號碼不只一個一個放出,並且放的過程當中並無中止打散。因此這個隨機函數顯然不合理。那還能怎麼辦,改啊!
// 打散後流出第一個和剩下的號碼組
function random(arr) {
const newarr = arr.sort(() => Math.random() - 0.5)
const val = newarr.shift()
return [val, newarr]
}
function dualColor() {
let redballs = [1, 2, ..33]
let blueballs = [1, 2, ..16]
let red = [], blue = []
for (let i = 0; i < 6; i++) {
const balls = random(redballs)
red.push(balls[0])
redballs = balls[1]
}
blue.push(random(blueballs)[0])
return [red, blue]
}
複製代碼
說實話這麼寫程序可憋死我了…
若是按照正常隨機數方法那麼作的話,我確實提供一個最大值一個最小值就結束了,關鍵這是個數組,又沒有什麼range
之類的能夠用,不會真的有人手寫 1 ~ 33 吧。不過咱們能夠曲線撈一下
// 0 ~ 9
const arr = [...Array(10).keys()]
複製代碼
因此第一點改造兩個球數組
let reds: number[] = [...Array(33).keys()].map(i => i+1)
let blues: number[] = [...Array(16).keys()].map(i => i+1)
複製代碼
爲何 +1
呢?號碼也沒從 0
開始的啊!
咱們須要讓抽獎更靈活一些,畢竟只要是抽的,我全都要。我想讓隨機函數能接受一個數組和我想要的個數,再返回結果。
一樣的會出現須要循環取值的狀況,爲何咱們不用神奇的尾遞歸呢?
function randomVal( arr: number[], total: number, temp: number[] = [], ): number[] {
const [head, ...body] = arr
.sort(() => Math.random() - 0.5)
return !total
? temp
: randomVal(body, total - 1, temp.concat(head));
}
複製代碼
改!均可以改!咱們能夠改爲存放起始值和終止值的元組
function dualColor() {
const reds: [number, number] = [1, 33]
const blues: [number, number] = [1, 16]
return [randomVal(reds, 6), randomVal(blues, 1)]
}
複製代碼
相應的隨機函數也作點小改動
function randomVal( fromto: number[], total: number, temp: number[] = [], ): number[] {
const [head, ...body] = (temp.length
? fromto
: [...Array(fromto[1]).keys()]
.map(item => item + 1)
.splice(fromto[0] - 1)
).sort(() => Math.random() - 0.5);
return !total
? temp
: randomVal(body, total - 1, temp.concat(head))
}
複製代碼
除此以外還有三種: (一)從紅色區中選擇7--20個,從藍色區中選擇1個。 (二)從紅色區中選擇6個,從藍色區中選擇2--16個。 (三)從紅色區中選擇7--20個,從藍色區中選擇2--16個。
雖然我看不懂,可是這麼改就沒問題了吧
// 我管你幾個,全給你安排上!
function dualColor(red: number = 6, blue: number = 1) {
const reds: [number, number] = [1, 33]
const blues: [number, number] = [1, 16]
return [randomVal(reds, red), randomVal(blues, blue)]
}
複製代碼
因此最後解決方案是
function dualColor(red: number = 6, blue: number = 1) {
const reds: [number, number] = [1, 33]
const blues: [number, number] = [1, 16]
return [randomBall(reds, red), randomBall(blues, blue)]
}
function randomBall( fromto: number[], total: number, temp: number[] = [], ): number[] {
const [head, ...body] = (temp.length
? fromto
: [...Array(fromto[1]).keys()]
.splice(fromto[0] - 1)
.map(item => item + 1)
).sort(() => Math.random() - 0.5);
return !total
? temp
: randomBall(body, total - 1, temp.concat(head))
}
複製代碼
賞心悅目?