xgqfrms™, xgqfrms® : xgqfrms's offical website of cnblogs! xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!

使用 js 用最简单的方法实现微信红包的随机算法 All In One

使用 js 用最简单的方法实现微信红包的随机算法 All In One

最简单的方法实现微信红包的随机算法🧧

微信红包实现原理分析

微信红包的随机算法是怎样实现的?

/**

算法需要满足条件:
1. 每个人都可以分到至少 0.01 元;
2. 所有人的分到的红包之和与发出的金额相等,不多不少,刚好分完;
3. 每个人分到金额的概率相等;
*/

/**

假设,发出一个 100元红包,给 10个人分!

算法实现:
1. 按照人数,生成一个等长的数组,且每一个元素的初始化值为 0.01;✅
2. 讲剩余的金额(100 - 10 * 0.01), 按照微信设计的规则(假如是正态分布)进行分配出 10 份; ❓
3. 讲分配好的红包,依次加入到生成的数组中;✅
4. 最后使用 shuffle 算法打乱数组,并返回; ✅
5. 讲计算好的数组,按照抢红包的顺序作为索引值依次取出红包即可.✅

*/

const autoRandomRedPackage = (money, num, limit = 0.01)  => {
  const result = [...new Uint8Array(num)].fill(0.01, 0, num);
  // 正态分布 ❓ TODO...
  // 就地交换, shuffle
  return shuffle(result);
}

const shuffle = (arr = []) => {
  let len = arr.length;
  while (len > 1){
    // Math.floor
    const index = Math.floor(Math.random() * len--);
    // ES6 swap
    [
      arr[len],
      arr[index],
    ] = [
      arr[index],
      arr[len],
    ];
  }
  return arr;
}

// 测试
const test = autoRandomRedPackage(100, 10);
log(`test =`, test)

partly OK

浮点数精度 bug

"use strict";

/**
 *
 * @author xgqfrms
 * @license MIT
 * @copyright xgqfrms
 * @created 2020-09-16
 * @modified
 *
 * @description 最简单的方法实现微信红包的随机算法
 * @difficulty Easy Medium Hard
 * @complexity O(n)
 * @augments
 * @example
 * @link https://www.zhihu.com/question/22625187/answer/1478941580
 * @solutions
 *
 */

const log = console.log;


const shuffle = (arr = []) => {
  let len = arr.length;
  while (len > 1){
    // Math.floor
    const index = Math.floor(Math.random() * len--);
    // ES6 swap
    [
      arr[len],
      arr[index],
    ] = [
      arr[index],
      arr[len],
    ];
  }
  return arr;
}


/**

算法需要满足条件:
1. 每个人都可以分到至少 0.01 元;
2. 所有人的分到的红包之和与发出的金额相等,不多不少,刚好分完;
3. 每个人分到金额的概率相等;
*/

/**

假设,发出一个 100元红包,给 10个人分!

算法实现:
1. 按照人数,生成一个等长的数组,且每一个元素的初始化值为 0.01;✅
2. 将剩余的金额(100 - 10 * 0.01), 按照微信设计的规则(假如是正态分布)进行分配出 10 份; ❓
3. 将分配好的红包,依次加入到生成的数组中;✅
4. 最后使用 shuffle 算法打乱数组,并返回; ✅
5. 将计算好的数组,按照抢红包的顺序作为索引值依次取出红包即可.✅

*/

/*

money = 100;
num = 11;

rest = money - 0.01 * num ;
99.89

99.89 / 0.01;
9989

9989 % 11
1

9988 / 11
908

9988 / 11 * 0.01
9.08

*/

const autoRandomRedPackage = (money, num, limit = 0.01)  => {
  const result = [...new Uint8Array(num)].fill(0.01, 0, num);
  // 正态分布 ❓ TODO...
  // 1. 将剩余的红包,均分✅,如果有余数,随机的添加到数组的一个元素上
  const restLimit = (money - limit * num) / limit;
  //9989
  const reminderLimit = (restLimit % num);
  // 1
  const reminderMoney = reminderLimit * limit;
  // 1 * 0.01
  const averageLimit = (restLimit - reminderLimit) / num;
  // 908
  // const averageMoney = averageLimit * limit;
  // 9.08
  for (let i = 0; i < num; i++) {
    const index = parseInt(Math.random() * averageLimit);
    const randomMoney = index * limit;
    log(`average index`, index, randomMoney)
    const leftMoney = (averageLimit - index) * limit;
    // 2. 在平均后的范围内,计算出一个随机数,将分配好的红包,依次加入到生成的数组中;✅
    result[i] += randomMoney;
    // 3. 分配后剩余的红包,随机加入到生成的数组中;✅
    const j = parseInt(Math.random() * num);
    result[j] += leftMoney;
  }
  // 4. 将平均后的余数红包,随机加入到生成的数组中;✅
  if(reminderMoney > 0) {
    const index = parseInt(Math.random() * num);
    result[index] += reminderMoney;
  }
  // 就地交换, shuffle
  // return shuffle(result);
  return {
    total: shuffle(result).reduce((acc, i) => acc += i),// test total
    result: shuffle(result),
  };
}

// 测试
// const test = autoRandomRedPackage(100, 10);
const test = autoRandomRedPackage(100, 11);
log(`test =`, test);


/*

精度 bug

435 * 0.01
4.3500000000000005


35 * 0.01
0.35000000000000003

5 * 0.01
0.05

3 * 0.01
0.03

*/

/*

$ node red-package.js

average index 302 3.02
average index 615 6.15
average index 615 6.15
average index 416 4.16
average index 633 6.33
average index 96 0.96
average index 272 2.72
average index 830 8.3
average index 578 5.78
average index 701 7.01
average index 435 4.3500000000000005
test = {
  total: 100,
  result: [
    8.32,
    7.02,
    9.09,
    7.66,
    8.41,
    9.39,
    11.670000000000002,
    7.03,
    4.17,
    19.380000000000003,
    7.859999999999999
  ]
}

*/


perfect OK


"use strict";

/**
 *
 * @author xgqfrms
 * @license MIT
 * @copyright xgqfrms
 * @created 2020-09-16
 * @modified
 *
 * @description 最简单的方法实现微信红包的随机算法
 * @difficulty Hard
 * @complexity O(n)
 * @augments
 * @example
 * @link https://www.zhihu.com/question/22625187/answer/1478941580
 * @solutions
 *
 */

const log = console.log;

const shuffle = (arr = []) => {
  let len = arr.length;
  while (len > 1){
    const index = Math.floor(Math.random() * len--);
    [
      arr[len],
      arr[index],
    ] = [
      arr[index],
      arr[len],
    ];
  }
  return arr;
}


/**

算法需要满足条件:
1. 每个人都可以分到至少 0.01 元;
2. 所有人的分到的红包之和与发出的金额相等,不多不少,刚好分完;
3. 每个人分到金额的概率相等;
*/

/**

假设,发出一个 100元红包,给 10个人分!

算法实现:
1. 按照人数,生成一个等长的数组,且每一个元素的初始化值为 0.01;✅
2. 将剩余的金额(100 - 10 * 0.01), 按照微信设计的规则(假如是正态分布)进行分配出 10 份; ❓
3. 将分配好的红包,依次加入到生成的数组中;✅
4. 最后使用 shuffle 算法打乱数组,并返回; ✅
5. 将计算好的数组,按照抢红包的顺序作为索引值依次取出红包即可.✅

*/

const autoRandomRedPackage = (money, num, limit = 0.01) => {
  // 精度损失解决方案, 扩大后,再还原❓✅🚀
  money *= 100;
  limit *= 100;
  const result = [...new Uint8Array(num)].fill(limit, 0, num);
  // 1. 将剩余的红包,均分✅,如果有余数,随机的添加到数组的一个元素上
  const restLimit = (money - limit * num) / limit;
  const reminderLimit = (restLimit % num);
  const reminderMoney = reminderLimit * limit;
  const averageLimit = (restLimit - reminderLimit) / num;
  // const averageMoney = averageLimit * limit;
  for (let i = 0; i < num; i++) {
    const index = parseInt(Math.random() * averageLimit);
    const randomMoney = index * limit;
    // log(`average index`, index, randomMoney);
    const leftMoney = (averageLimit - index) * limit;
    // 2. 在平均后的范围内,计算出一个随机数,将分配好的红包,依次加入到生成的数组中;✅
    result[i] += randomMoney;
    // 3. 分配后剩余的红包,随机加入到生成的数组中;✅
    const j = parseInt(Math.random() * num);
    result[j] += leftMoney;
  }
  // 4. 将平均后的余数红包,随机加入到生成的数组中;✅
  if(reminderMoney > 0) {
    const index = parseInt(Math.random() * num);
    result[index] += reminderMoney;
  }
  // 就地交换, shuffle
  // return shuffle(result);
  const total = shuffle(result).reduce((acc, i) => acc += i / 100, 0);
  if(total !== 100) {
    return autoRandomRedPackage(money / 100, num, limit / 100);
  }
  return {
    total: total,// test total
    result: shuffle(result).map(i => i / 100),
  };
  // return {
  //   total: shuffle(result).reduce((acc, i) => acc += i, 0),// test total
  //   result: shuffle(result),
  // };
}

// 测试
const test = autoRandomRedPackage(100, 10);
// const test = autoRandomRedPackage(100, 11);
log(`test =`, test);
log(`total =`, test.result.reduce((acc, i) => acc += i, 0));


/*

$ node ok.js

test = {
  total: 100,
  result: [
    8.47,  3.67,  6.77,
    4.28,  2.15, 13.23,
    9.24, 24.12, 20.32,
    7.75
  ]
}
total = 100

*/


js double 精度损失 bugs

const arr = [
  0.01, 0.01, 0.01,
  0.01, 0.01, 0.01,
  0.01, 0.01, 0.01,
  0.01
];

// [0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01]

arr.reduce((acc, i) => acc += i);
// 0.09999999999999999

arr = [
  0.01, 0.01, 0.01,
  0.01, 0.01, 0.01,
  0.01, 0.01, 0.01,
  0.01
]
(10) [0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01]

arr.reduce((acc, i) => acc += i);
0.09999999999999999

sum = 0;
0
sum += 0.01;
0.01
sum += 0.01;
0.02
sum += 0.01;
0.03
sum += 0.01;
0.04
sum += 0.01;
0.05
sum += 0.01;
0.060000000000000005
sum += 0.01;
0.07
sum += 0.01;
0.08
sum += 0.01;
0.09
sum += 0.01;
0.09999999999999999

// 保留两位精度 ?

js 小数转整数

arr = [
  0.01, 0.01, 0.01,
  0.01, 0.01, 0.01,
  0.01, 0.01, 0.01,
  0.01
];
(10) [0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01]
arr.map(i => i * 100).reduce((acc, i) => acc += i) / 100;
0.1

https://www.cnblogs.com/xgqfrms/p/13689522.html

refs

https://www.cnblogs.com/xgqfrms/p/13687916.html

https://www.cnblogs.com/xgqfrms/p/11977189.html

https://www.cnblogs.com/xgqfrms/p/13688278.html

https://www.zhihu.com/question/22625187/answer/1478941580

(🐞 反爬虫测试!打击盗版⚠️)如果你看到这个信息, 说明这是一篇剽窃的文章,请访问 https://www.cnblogs.com/xgqfrms/ 查看原创文章!



©xgqfrms 2012-2021

www.cnblogs.com/xgqfrms 发布文章使用:只允许注册用户才可以访问!

原创文章,版权所有©️xgqfrms, 禁止转载 🈲️,侵权必究⚠️!


posted @ 2020-09-17 23:10  xgqfrms  阅读(2095)  评论(8编辑  收藏  举报