一位賭狗前端的自我修養

YDJFE發表於2018-11-21

這段時間工作巨多,直到今天才有時間寫點東西。可是要輸出點什麼比較好呢?這時候看到了隔壁桌面上放著一張機打的寫著一組一組數字的紙,有了!那要不抽個獎吧!

問題分析

抽獎,講究一個隨機,只要把隨機搖號解決了不就不管什麼規則都能抽了?所以我們需要一個可以產生隨機數的函式。不僅要隨機數,我們還需要的是一個閉區間隨機取整數的函式。所以

const random = (m, n) =>
m + Math.floor(Math.random() * (n - m))複製程式碼

沒吃過豬肉還見過豬跑,號碼肯定是抽一個少一個,所以根本不存在隨機數能相同的情況!所以這個方法是不能用的。那麼,拿來抽的方法肯定得滿足三個條件:

  • 有範圍,是閉區間
  • 起始值肯定大於1
  • 隨機數不能有重複

問題解決

關於隨機不重複我最先想到的是利用sort對陣列隨機打散的方法。這個時候可能會有其他聲音:

要不要這麼水?sort用來排序的不懂?

水不水不知道,看一下MDN。

arr.sort([compareFunction])

  • 如果 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個號碼合成一次有效號碼組。

作為賭狗前端,提取這段話幾個關鍵點:

  • 紅色區 1 ~ 33
  • 藍色區 1 ~ 16
  • 取紅 6 個,取藍 1 個

別說了我腦子裡又有函式了

// ...randomconst 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 ~ 9const 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))
}複製程式碼

賞心悅目?

來源:https://juejin.im/post/5bf3b10fe51d4576bc436030

相關文章