这是我参与「第四届青训营 」笔记创作活动的的第4天
聊聊我在字节青训营学到的分红包算法,需求也很常见,给个金额和数量,然后所有人抢红包,每个人抢到的红包数都是随机的。
基础结构
要实现与测试这一功能,需要写一些 HTML 和 JS 结构,这并不是本文的重点,就直接放代码了
HTML
<div class="app">
<div>
红包金额:
<input id="money" type="number" value="100" />
</div>
<div>
红包数量:
<input id="count" type="number" value="10" />
</div>
<button id="btn">分发红包</button>
<pre id="list"></pre>
</div>
JS
const input1 = document.getElementById('money')
const input2 = document.getElementById('count')
const btn = document.getElementById('btn')
const list = document.getElementById('list')
btn.addEventListener('click', () => {
const money = parseFloat(input1.value)
const count = parseInt(input2.value)
if (money * 100 < count) {
list.innerText = '金额不足'
return
}
let arr = distribute(money, count)
let res = ''
for (const num of arr) {
res += (num / 100).toFixed(2) + '\n'
}
list.innerText = res
})
function distribute(money, count) {
money *= 100
return new Array(count).fill(money / count)
}
本文的核心在于实现 distribute 函数,目前该函数只能平分红包,我们需要它能将金额随机分配
随机分红包
说到随机,第一时间想到的肯定是下面这样
function distribute(money, count) {
money *= 100
let res = []
while (--count) {
let m = (money * Math.random()) | 0
res.push(m)
money -= m
}
res.push(money)
return res
}
这样实现存在问题,有人可能会抢到 0.00 的红包,显然是不合理的。
切西瓜
为了剩余的红包足够分,那我们每次都只分大的那部分,不就好了
就像生活中的切西瓜,每一刀切下去,只切最大的那部分
实现也很简单,找到数组中最大的那项切就好了,代码如下
function distribute(money, count) {
money *= 100
let res = [money]
while (--count) {
let maxMoney = -1,
maxIndex = 0
for (let i = 0; i < res.length; i++) {
if (res[i] > maxMoney) {
maxMoney = res[i]
maxIndex = i
}
}
let ran = Math.max((maxMoney * Math.random()) | 0, 1)
res.splice(maxIndex, 1, ran, maxMoney - ran)
}
return res
}
放隔板
上面那种方法成功解决了红包不够分的问题,但因为每次分的都是最大的那个,大伙的红包趋于平衡,很难有 30 以上的红包,不够刺激
所以我们改造一下最开始失败的那个方法,不是纯随机地取值,而是在不同的位置放隔板
比如想把 100 元分成十份,就是在其中放 9 个隔板,隔板的取值范围为 1~9999(100元 = 10000分)
我们借助数组来确保每次随机的值不同,代码如下
function distribute(money, count) {
money *= 100
let arr = new Array(money).fill(0).map((_, i) => i)
while (--count) {
let ran = Math.floor((money - 2) * Math.random()) + 1
let temp = arr[count]
arr[count] = arr[ran]
arr[ran] = temp
}
let res = arr.slice(1, 10)
res.sort((a, b) => a - b)
res.push(money)
for (let i = res.length - 1; i > 0; i--) {
res[i] = res[i] - res[i - 1]
}
return res
}
该方法唯一的不足就是增加了空间复杂度,可是大家又能发多大的红包呢?
结语
如果喜欢或有所帮助的话,希望能点赞关注,鼓励一下作者。
如果文章有不正确或存疑的地方,欢迎评论指出。