这段时间工做巨多,直到今天才有时间写点东西。但是要输出点什么比较好呢?这时候看到了隔壁桌面上放着一张机打的写着一组一组数字的纸,有了!那要不抽个奖吧!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))
}
复制代码
赏心悦目?