需求:红包总金额随机后每份金额:最大值200, 最小值0.01;最大份数100;

工具:idea

参考:微信红包算法以及带上下限的红包算法

基于微信红包算法以及带上下限的红包算法优化,修改了部分极值时,报错的问题,例如0.03元的红包分为2份。

import com.alibaba.fastjson.JSON;

import org.junit.Assert;

import org.junit.Test;

import java.math.BigDecimal;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

import java.util.Random;

/**

* 参考:https://blog.csdn.net/paincupid/article/details/82054647

* @author crossoverJie

* Date: 03/01/2018 16:52

* @since JDK 1.8

*/

public class RedPacketTools {

public static BigDecimal MONEY_1_100 = new BigDecimal("0.01");

public static final BigDecimal TIMES_100 = new BigDecimal(100);

public static BigDecimal MONEY_200 = new BigDecimal(200);

private static final Random random = new Random();

/**

* 红包随机算法(分和元转换)

* @param money 红包总金额,单位为元

* @param count 红包份数

* @return 返回值的金额单位为元

*/

public static List createBonusList(BigDecimal money, int count) {

List moneys = new ArrayList<>(count);

int _money = money.multiply(TIMES_100).intValue();

int rdMin = MONEY_1_100.multiply(TIMES_100).intValue();

int rdMax = MONEY_200.multiply(TIMES_100).intValue();

int[] _moneys = createBonusList(_money, count, rdMin, rdMax, 0.8);

for (int m: _moneys) {

moneys.add(new BigDecimal(m).divide(TIMES_100));

}

return moneys;

}

/**

* 返回一次抽奖在指定中奖概率下是否中奖

* @param rate 中奖概率

* @return xxx

*/

private static boolean canReward(double rate) {

return Math.random() <= rate;

}

/**

* 返回min~max区间内随机数,含min和max

* @param min xxx

* @param max xxx

* @return xxx

*/

private static int getRandomVal(int min, int max) {

return random.nextInt(max - min + 1) + min;

}

/**

* 带概率偏向的随机算法,概率偏向subMin~subMax区间

* 返回boundMin~boundMax区间内随机数(含boundMin和boundMax),同时可以指定子区间subMin~subMax的优先概率

* 例:传入参数(10, 50, 20, 30, 0.8),则随机结果有80%概率从20~30中随机返回,有20%概率从10~50中随机返回

* @param boundMin 边界

* @param boundMax xxx

* @param subMin xxx

* @param subMax xxx

* @param subRate xxx

* @return xxx

*/

private static int getRandomValWithSpecifySubRate(int boundMin, int boundMax, int subMin, int subMax, double subRate) {

if (canReward(subRate)) {

return getRandomVal(subMin, subMax);

}

return getRandomVal(boundMin, boundMax);

}

/**

* 随机分配第n个红包

* @param totalBonus 总红包量

* @param totalNum 总份数

* @param sendedBonus 已发送红包量

* @param sendedNum 已发送份数

* @param rdMin 随机下限

* @param rdMax 随机上限

* @return xxx

*/

private static int randomBonusWithSpecifyBound(int totalBonus, int totalNum, int sendedBonus,

int sendedNum, int rdMin, int rdMax, double bigRate) {

double avg = totalBonus / (double)totalNum; // 平均值

int leftLen = (int) Math.ceil(avg - rdMin);

int rightLen = (int) Math.floor(rdMax - avg);

int boundMin = 0;

int boundMax = 0;

// 大范围设置小概率

if (leftLen == rightLen) {

boundMin = Math.max((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMax), rdMin);

boundMax = Math.min((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMin), rdMax);

} else if (rightLen- leftLen > 0) {

// 上限偏离

int standardRdMax = (int) Math.ceil(avg + leftLen); // 右侧对称上限点

int _rdMax = canReward(bigRate) ? rdMax : standardRdMax;

boundMin = Math.max((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * standardRdMax), rdMin);

boundMax = Math.min((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMin), _rdMax);

} else {

// 下限偏离

int standardRdMin = (int) Math.floor(avg - rightLen); // 左侧对称下限点

int _rdMin = canReward(bigRate) ? rdMin : standardRdMin;

boundMin = Math.max((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * rdMax), _rdMin);

boundMax = Math.min((totalBonus - sendedBonus - (totalNum - sendedNum - 1) * standardRdMin), rdMax);

}

// 已发平均值偏移修正-动态比例

if (boundMin == boundMax) {

return getRandomVal(boundMin, boundMax);

}

double currAvg = sendedNum == 0 ? (double)avg : (sendedBonus / (double)sendedNum); // 当前已发平均值

double middle = (boundMin + boundMax) / 2.0;

int subMin = boundMin, subMax = boundMax;

// 期望值

double exp = avg - (currAvg - avg) * sendedNum / (double)(totalNum - sendedNum);

if (middle > exp) {

subMax = (int) Math.round((boundMin + exp) / 2.0);

} else {

subMin = (int) Math.round((exp + boundMax) / 2.0);

}

int expBound = (boundMin + boundMax) / 2;

int expSub = (subMin + subMax) / 2;

double subRate = (exp - expBound) / (double)(expSub - expBound);

return getRandomValWithSpecifySubRate(boundMin, boundMax, subMin, subMax, subRate);

}

/**

* 生成红包一次分配结果

* @param totalBonus xxx

* @param totalNum xxx

* @param rdMin xxx

* @param rdMax xxx

* @param bigRate 指定大范围区间的概率

* @return xxx

*/

private static int[] createBonusList(int totalBonus, int totalNum, int rdMin, int rdMax, double bigRate) {

int sendedBonus = 0;

int sendedNum = 0;

int[] bonusList = new int[totalNum];

while (sendedNum < totalNum) {

int bonus = randomBonusWithSpecifyBound(totalBonus, totalNum, sendedBonus, sendedNum, rdMin, rdMax, bigRate);

bonusList[sendedNum] = bonus;

sendedNum++;

sendedBonus += bonus;

}

return bonusList;

}

@Test

public void testCommon() {

BigDecimal totalMoney = new BigDecimal("0.03");

int totalAmount = 2;

int loopTimes = 100;

long start = System.currentTimeMillis();

for (int k = 0; k < loopTimes; k++) {

List bonusList = createBonusList(totalMoney, totalAmount);

System.out.println(JSON.toJSONString(bonusList));

BigDecimal sum = new BigDecimal(0);

for (BigDecimal m : bonusList) {

sum = sum.add(m);

}

Assert.assertEquals(totalMoney.compareTo(sum), 0);

// System.out.println("sum:" + sum + ",人数:" + totalAmount);

// System.out.println("-----------------");

}

long end = System.currentTimeMillis();

System.out.println("金额:" + totalMoney + "\t人数:" + totalAmount + "\t时间间隔:" + (end - start) + "毫秒");

}

@Test

public void testAllInteger() {

// int moneyMax = 1;

// int moneyMax = 200; // 慎用测试

// int perMoneyAmountLoopTimes = 100000;

int moneyMax = 10;

int perMoneyAmountLoopTimes = 10;

// 金额范围1 ~ 200

for (int i = 1; i <= moneyMax; i++) {

int money = i;

int amountMax = i;

for (int amount = 1; amount <= amountMax; amount++) {

// long start = System.currentTimeMillis();

// 金额:money,份数:amount的红包,循环perMoneyAmountLoopTimes次随机测试

for (int k = 0; k < perMoneyAmountLoopTimes; k++) {

int[] bonusList = createBonusList(money, amount, 1, 200, 0.8);

System.out.println(Arrays.toString(bonusList));

int sum = 0;

for (int m : bonusList) {

sum += m;

}

Assert.assertEquals(sum, money);

// System.out.println("sum:" + sum + ",人数:" + amount);

// System.out.println("-----------------");

}

// long end = System.currentTimeMillis();

// System.out.println("金额:" + money + "\t人数:" + amount + "\t时间间隔:" + (end - start) + "毫秒");

// System.out.println("----------------------------------------------------");

}

System.out.println("金额:" + i + "(分)");

}

}

@Test

public void testAllFloat() {

// 金额范围0.01 ~ 200

// int moneyMax = 1;

// int moneyMax = 20000; // 慎用测试

// int perMoneyAmountLoopTimes = 100000;

int moneyMax = 10;

int perMoneyAmountLoopTimes = 10;

for (int i = 1; i <= moneyMax; i++) {

BigDecimal money = new BigDecimal(i).divide(TIMES_100);

int amountMax = Math.min(i, 100);

for (int amount = 1; amount <= amountMax; amount++) {

// long start = System.currentTimeMillis();

// 金额:money,份数:amount的红包,循环perMoneyAmountLoopTimes次随机测试

for (int k = 0; k < perMoneyAmountLoopTimes; k++) {

List bonusList = createBonusList(money, amount);

System.out.println(JSON.toJSONString(bonusList));

BigDecimal sum = new BigDecimal(0);

for (BigDecimal m : bonusList) {

sum = sum.add(m);

}

Assert.assertEquals(money.compareTo(sum), 0);

// System.out.println("sum:" + sum + ",人数:" + amount);

// System.out.println("-----------------");

}

// long end = System.currentTimeMillis();

// System.out.println("金额:" + money + "\t人数:" + amount + "\t时间间隔:" + (end - start) + "毫秒");

// System.out.println("----------------------------------------------------");

}

System.out.println("金额:" + i + "(分)");

}

}

}

Java编程随机发红包,红包随机算法Java实现相关推荐

  1. php扑克牌随机发,PHP实现随机发放扑克牌分享!

    PHP编程:用PHP实现随机发放扑克牌 描述:一副扑克牌共54张(包括大.小王),用PHP制作一发牌器,向三人随机发牌: 编辑poker.php <?php function poker(){ ...

  2. java编程实现行列式计算应用,行列式算法(java)

    public class Arranger { private double[][] arrangerMatrix; private double arrangerResult=1.0; public ...

  3. java编程题有难度的_算法与编程面试题 不喜勿喷 难度指数:*****...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 3.编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad. (大家正在做上面这道 ...

  4. java实现一个感知机_感知机学习算法Java实现

    感知机学习算法Java实现. Perceptron类用于实现感知机, 其中的perceptronOriginal()方法用于实现感知机学习算法的原始形式: perceptronAnother()方法用 ...

  5. java编程需要数学知识吗_初学Java编程,需要英语和数学基础吗?

    原标题:初学Java编程,需要英语和数学基础吗? "学习Java编程英语和数学是必备条件吗?"很多Java零基础学习或者转型IT行业的都会有这样的疑问,其实刚开始学习Java编程是 ...

  6. java编程工具 初学者_面向初学者的Java编程在线课程

    java编程工具 初学者 There are many java programming course online provided by many services. I use Udemy be ...

  7. 自学个JAVA编程有什么用_怎样自学java编程

    4 怎么学java 多线程需要理解机理 很多Java程序员热衷于多线程程序编写,认为是对逻辑能力的挑战.其实在大量应用中根本就不需要编写多线程程序,或者说大多数编写应用程序的程序员不会去写多线程程序. ...

  8. java编程那些事儿 pdf,科学网—《java编程那些事儿》 前面部分阅读笔记 - 马舒天的博文...

    最近用java比较多,就开始看一下JAVA的书,因为封面的标注(根据CSDN同名长篇技术连载改编而成),所以把这本书前面部分简单看了一下,有用的地方自己之前不知道的知识点写一下.本文主要介绍了一些处理 ...

  9. java编程计算器程序代码_34 个送给 Java 程序员的练手项目合集

    人类一生中,99% 的技能都是靠实践学来的,编程更是如此. 在这一点上,编程和打篮球很像:你得亲自上场去打,才能学会篮球:同样,亲手敲下代码,才能真正学会「编程」.太多人看完一本书.几个 G 的视频后 ...

  10. 用java编程的单片机_单片机可以用java编程或经过技术手段来可以用java编程吗?...

    没有java程序用于单片机编程的.主要的一点就是JAVA是属于面向对象的.而单片机编程必须面向机器的. 结合下面的问题一起回答,一般来说,单片机开发是一个行业,它包括了单片机程序开发,但程序开发不能像 ...

最新文章

  1. python各版本区别_关于python中不同版本的print区别
  2. 极光推送 请检查参数合法性_极光小课堂 | 极光推送在人脸识别终端管理系统中的应用...
  3. beyond唱片_如何数字化您的唱片
  4. 大学生起诉小米获赔流量费1元
  5. [转贴]ASP.NET下对远程SQL SERVER数据库的备份和恢复的存储过程
  6. Oracle 数据库基本知识概念
  7. 小甲鱼c语言_C语言可以不用,但是不能不会!
  8. win10计算机升级系统,win10系统升级更新方法
  9. JS中根据入职时间计算工龄
  10. linux怎么安装网卡驱动固件,linux下安装网卡驱动的方法步骤
  11. comsol-旋度的形象化概念
  12. 拼多多砍价用户福利贴:通过python模拟操作进行拼多多砍价
  13. vue3 效率的提升、composition-api 和 ref 详解
  14. windows AD域的特点
  15. python二进制的位运算符
  16. MCR和MRC汇编指令
  17. php mysql 简单留言板_PHP+MySql实现一个简单的留言板
  18. 黑客入门很难吗?这一篇保证你学的明明白白
  19. Python编程:从入门到实践-第二章:变量和简单数据(语法)
  20. 华中科技大学431金融学综合考研新祥旭授课计划参考书题型分数线

热门文章

  1. pandas 时间属性函数
  2. 工程伦理慕课答案(带题目供复习)
  3. HTML5绘制国际象棋,如何用纯CSS实现一副国际象棋
  4. 循序渐进学 LoadingDrawable
  5. 谁最优秀?TurboMail WebMail功能盘点
  6. 十二小时制和二十四小时制之间的区别
  7. Vmware搭建hadoop集群(一)
  8. android设置背景平铺
  9. BZOJ 3698: XWW的难题(有源汇上下界最大流)
  10. 硬核机器学习干货,手把手教你写KNN!