Java编程随机发红包,红包随机算法Java实现
需求:红包总金额随机后每份金额:最大值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实现相关推荐
- php扑克牌随机发,PHP实现随机发放扑克牌分享!
PHP编程:用PHP实现随机发放扑克牌 描述:一副扑克牌共54张(包括大.小王),用PHP制作一发牌器,向三人随机发牌: 编辑poker.php <?php function poker(){ ...
- java编程实现行列式计算应用,行列式算法(java)
public class Arranger { private double[][] arrangerMatrix; private double arrangerResult=1.0; public ...
- java编程题有难度的_算法与编程面试题 不喜勿喷 难度指数:*****...
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 3.编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad. (大家正在做上面这道 ...
- java实现一个感知机_感知机学习算法Java实现
感知机学习算法Java实现. Perceptron类用于实现感知机, 其中的perceptronOriginal()方法用于实现感知机学习算法的原始形式: perceptronAnother()方法用 ...
- java编程需要数学知识吗_初学Java编程,需要英语和数学基础吗?
原标题:初学Java编程,需要英语和数学基础吗? "学习Java编程英语和数学是必备条件吗?"很多Java零基础学习或者转型IT行业的都会有这样的疑问,其实刚开始学习Java编程是 ...
- java编程工具 初学者_面向初学者的Java编程在线课程
java编程工具 初学者 There are many java programming course online provided by many services. I use Udemy be ...
- 自学个JAVA编程有什么用_怎样自学java编程
4 怎么学java 多线程需要理解机理 很多Java程序员热衷于多线程程序编写,认为是对逻辑能力的挑战.其实在大量应用中根本就不需要编写多线程程序,或者说大多数编写应用程序的程序员不会去写多线程程序. ...
- java编程那些事儿 pdf,科学网—《java编程那些事儿》 前面部分阅读笔记 - 马舒天的博文...
最近用java比较多,就开始看一下JAVA的书,因为封面的标注(根据CSDN同名长篇技术连载改编而成),所以把这本书前面部分简单看了一下,有用的地方自己之前不知道的知识点写一下.本文主要介绍了一些处理 ...
- java编程计算器程序代码_34 个送给 Java 程序员的练手项目合集
人类一生中,99% 的技能都是靠实践学来的,编程更是如此. 在这一点上,编程和打篮球很像:你得亲自上场去打,才能学会篮球:同样,亲手敲下代码,才能真正学会「编程」.太多人看完一本书.几个 G 的视频后 ...
- 用java编程的单片机_单片机可以用java编程或经过技术手段来可以用java编程吗?...
没有java程序用于单片机编程的.主要的一点就是JAVA是属于面向对象的.而单片机编程必须面向机器的. 结合下面的问题一起回答,一般来说,单片机开发是一个行业,它包括了单片机程序开发,但程序开发不能像 ...
最新文章
- python各版本区别_关于python中不同版本的print区别
- 极光推送 请检查参数合法性_极光小课堂 | 极光推送在人脸识别终端管理系统中的应用...
- beyond唱片_如何数字化您的唱片
- 大学生起诉小米获赔流量费1元
- [转贴]ASP.NET下对远程SQL SERVER数据库的备份和恢复的存储过程
- Oracle 数据库基本知识概念
- 小甲鱼c语言_C语言可以不用,但是不能不会!
- win10计算机升级系统,win10系统升级更新方法
- JS中根据入职时间计算工龄
- linux怎么安装网卡驱动固件,linux下安装网卡驱动的方法步骤
- comsol-旋度的形象化概念
- 拼多多砍价用户福利贴:通过python模拟操作进行拼多多砍价
- vue3 效率的提升、composition-api 和 ref 详解
- windows AD域的特点
- python二进制的位运算符
- MCR和MRC汇编指令
- php mysql 简单留言板_PHP+MySql实现一个简单的留言板
- 黑客入门很难吗?这一篇保证你学的明明白白
- Python编程:从入门到实践-第二章:变量和简单数据(语法)
- 华中科技大学431金融学综合考研新祥旭授课计划参考书题型分数线