haizlin / fe-interview

前端面试每日 3+1,以面试题来驱动学习,提倡每日学习与思考,每天进步一点!每天早上5点纯手工发布面试题(死磕自己,愉悦大家),6000+道前端面试题全面覆盖,HTML/CSS/JavaScript/Vue/React/Nodejs/TypeScript/ECMAScritpt/Webpack/Jquery/小程序/软技能……

Home Page:http://www.h-camel.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[js] 第155天 用js模拟实现微信抢红包的算法,并说明你的思路

haizhilin2013 opened this issue · comments

第155天 用js模拟实现微信抢红包的算法,并说明你的思路

开个监听器,找到红包消息对应的特征,进而可以监听红包消息,然后在这个回调函数里面写入打开红包的代码,当然,单纯的js肯定实现不了,需要调用别的语言和api接口

普惠红包

除了最后一人,其他每个人随机范围为 (0, 20),也相当于每人平均 10 块吧。
可以再打乱一次顺序来避免最后一人问题。

function grabMoney(total, maxTimes) {
  //  结果数组   剩余次数           剩余金额
  var res = [], times = maxTimes, rest = total;
  while (--times > 1) {
    var r = random(0, rest / times * 2);
    var _r = r.toFixed(2).replace('.', ''); // 处理双精度运算问题
    var _rest = rest.toFixed(2).replace('.', '');
    rest = (_rest - _r) / 100;
    r = parseFloat(r.toFixed(2));
    res.push(r);
  }
  res.push(rest);
  return res;
}

耗时红包

每人随机范围为 (0, 25),超出总价就重新排,低于总价则平摊。
由于有除法,余数计算误差大,所以系统会贪掉一丢丢。

function grabMoney(total, maxTimes) {
  var res = new Array(maxTimes);
  var grabed = 0;
  for (var i=0; i<res.length; i++) {
    var r = random(0, total / 4);
    r = parseFloat(r.toFixed(2));
    grabed += r;
    if (grabed > total) return grabMoney(total, maxTimes);
    res[i] = r;
  }
  if (grabed < total) {
    var average = (total - grabed) / total;
    res = res.map(function(item) {
      return item + average;
    });
  }
  return res;
}
/**
 * 抢红包算法
 * @param {*} money 红包金额数
 * @param {*} people 红包人数
 */
function drawLucky (money, people) {
  let remainMoney = money
  let remainPeople = people

  return {
    // 每一次抢到的红包金额
    draw () {
      let per = remainMoney / remainPeople
      let curMoney = 0
      if (remainPeople === 1) {
        curMoney = Math.round(remainMoney * 100) / 100 // 使数字保留两位小数
        remainPeople--
      } else if (remainPeople > 1) {
        curMoney = Math.round(per * (Math.random() * 1.6 + 0.2) * 100) / 100 // 使数字保留两位小数【随机分配剩余金额,份数为(0.2, 1.8)之间】
        remainPeople--
      }
      remainMoney -= curMoney
      return curMoney
    }
  }
}

let test = drawLucky(100, 20)
let amount = 0
for (let i = 0; i < 20; i++) {
  let cur = test.draw()
  amount += cur
  console.log(cur)
}
console.log(amount)

感觉真实情况下还要知道实时剩余人数和金额,考虑到网络情况,有点难;

I decided to use closure to solve this problem.
When creating red pockets, users will be asked to provide three arguments.
The first argument is the total amount of money they want to put into the red pockets.
The second argument is the number of red pockets.
The third argument indicates whether the amount of money in each red pocket is determined randomly.
When createRedPockets is called, an array of red pockets (positive numbers) will be created and a function is returned.
The function returns a red pocket when there are still red pockets (positive numbers) left in the array. Otherwise, it returns 0.

const createRedPockets = (total, num = 1, random = false) => { let redPockets; if (random) { let weights = []; for (let i = 0; i < num; i++) { weights.push(Math.random() + 1); } const sum = weights.reduce((a, b) => a + b); weights = weights.map(weight => weight / sum); redPockets = weights.map(weight => weight * total); } else { redPockets = Array(num).fill(total / num); } return () => { if (!redPockets.length) return 0; return redPockets.pop(); }; };

function getRandomMoney(remainMoney,remainSize){
let moneyList=[];
const min=0.01;
let max,money;
while (remainSize>1){
max=remainMoney/remainSize*2;
money=Math.random()max;
money=money<0.01 ? 0.01 : money;
money=Math.round(money
100)/100;
moneyList.push(money);
remainSize--;
remainMoney-=money;
}

moneyList.push(Math.round(remainMoney*100)/100);
return moneyList;
}
const maxMoney=100; //红包总金额
const maxSize=20; //红包个数
console.log(getRandomMoney(maxMoney,maxSize))