文档章节

JAVA实现仿微信红包分配规则

小致dad
 小致dad
发布于 06/18 21:23
字数 1652
阅读 12
收藏 0

最近过年发红包拜年成为一种新的潮流,作为程序猿对算法的好奇远远要大于对红包的好奇,这里介绍一种自己想到的一种随机红包分配策略,还请大家多多指教。

算法介绍

一、红包金额限制

      对于微信红包,我们知道没人随机的最小红包是1分,最大金额是200元,这里我们同样来设置红包的范围,下面代码我们统一金钱的单位为分。

//最小红包额度
private static final int MINMONEY = 1;
//最大红包额度
private static final int MAXMONEY = 200 * 100;


二、判断红包金额是否合法

      注意这一步伴随着整个算法,我们不仅要在分配红包之前要判断金额是否合法,同样要在每个人暂定随机金额后也要判断剩余的金额是否合法。

private boolean isRight(int money, int count) {
	double avg = money / count;
	if (avg < MINMONEY) {
		return false;
	}
	if (avg > MAXMONEY) {
		return false;
	}
	return true;
}


三、随机产生一个红包

      这里我们采用随机的方式产生一个在MINMONEY和MAXMONEY之间的一个红包,产生红包之后,我们需要判断剩余的钱是否是合法红包,如果不是合法红包,我们就重新产生分配方案,在重新产生分配方案的时候,我们需要确定一个事情,是产生的红包过大还是过小,如果红包过大,下次就随机一个小值到本次红包金额的一个红包,如果红包金额过小,我们就产生一个红包金额到大值的一个红包。

private int random(int money, int minS, int maxS, int count) {
	//红包数量为1,直接返回金额
	if (count == 1) {
		return money;
	}
	//如果最大金额和最小金额相等,直接返回金额
	if (minS == maxS) {
		return minS;
	}
	int max = maxS > money ? money : maxS;
	//随机产生一个红包
	int one = ((int)Math.rint(Math.random() * (max - minS) + minS))  % max + 1;
	int money1 = money - one;
	//判断该种分配方案是否正确
	if (isRight(money1, count -1)) {
		return one;
	} else {
		double avg = money1 / (count - 1);
		if (avg < MINMONEY) {
			//递归调用,修改红包最大金额
			return random(money, minS, one, count);
		}else if (avg > MAXMONEY) {
			//递归调用,修改红包最小金额
			return random(money, one, maxS, count);
		}
	}
	return one;
}

第三步优化

      首先感谢子冬童鞋给我提供的优化方案:在随机产生红包的时候,我们知道随机一个红包后,剩余的红包个数,这时我们又知道红包的最大值、最小值,那么我们就知道了剩余金额需要在一个范围内,因此也就知道了这个产生的随机红包的最值,所以每次产生下一个红包只需要一次随机即可。举个简单的例子,加入现在有一个5分4个的红包,每个红包要求至少1分,最大20000分,那么在分出第一个红包后,剩余3个红包的钱数要在1 * 3 = 3(分) 和 20000 * 3 = 60000(分)之间,因此也就是说,分配第一个红包的随机金额要在5 - 60000 = -59995(分)和 5 - 3 = 2(分)之间,又由于我们对红包的金额要求在1~20000之间,因此确定第一次随机金额的范围是[1分,2分]。这种随机产生红包的方案要远远优于第三步中提供的方案,在此再次感谢子冬同学提供的优化方案。我们只需要将下述方法替换源代码中的方法即可实现红包随机优化。

	private int random(int money, int minS, int maxS, int count) {
		//红包数量为1,直接返回金额
		if (count == 1) {
			return money;
		}
		//如果最大金额和最小金额相等,直接返回金额
		if (minS == maxS) {
			return minS;
		}
		int max = maxS > money ? money : maxS;
		//分配红包正确情况,允许红包的最大值
		int maxY = money - (count - 1) * minS;
		//分配红包正确情况,允许红包的最小值
		int minY = money - (count - 1) * maxS;
		//随机产生红包的最小值
		int min = minY > minS ? minY : minS;
		//随机产生红包最大值
		max = maxY < max ? maxY : max;
		//随机产生一个红包
		return (int)Math.rint(Math.random() * (max - min) + min);
	}

四、实现红包分配

      这里为了避免某一个红包占用大量资金,我们需要设定非最后一个红包的最大金额,我们把他设置为红包金额平均值的N倍;有了一、二、三中的方法,我们就可以来实现红包的分配了。

//每个红包最大是平均值的倍数
private static final double TIMES = 2.1;   

public List<Integer> splitRedPackets(int money, int count) {
	if (!isRight(money, count)) {
		return null;
	}
	List<Integer> list = new ArrayList<Integer>();
	//红包最大金额为平均金额的TIMES倍
	int max = (int) (money * TIMES / count);
	max = max > MAXMONEY ? MAXMONEY : max;
	for (int i = 0; i < count; i++) {
		int one = random(money, MINMONEY, max, count - i);
		list.add(one);
		money -= one;
	}
	return list;
}

红包分配方案评估

      上面介绍了红包的基本算法,下面我们就对算法进行一次验证,假设有一个200元100份的红包,我们来看一下最后的分配方案。

img

完整代码

 /**  
 *@Description:     
 */ 
package com.lulei.weixin.util;  

import java.util.ArrayList;
import java.util.List;

import com.lulei.util.JsonUtil;
  
public class RedPacketUtil {
	//最小红包额度
	private static final int MINMONEY = 1;
	//最大红包额度
	private static final int MAXMONEY = 200 * 100;
	//每个红包最大是平均值的倍数
	private static final double TIMES = 2.1;
	
	/**
	 * @param money
	 * @param count
	 * @return
	 * @Author:lulei  
	 * @Description: 拆分红包
	 */
	public List<Integer> splitRedPackets(int money, int count) {
		if (!isRight(money, count)) {
			return null;
		}
		List<Integer> list = new ArrayList<Integer>();
		//红包最大金额为平均金额的TIMES倍
		int max = (int) (money * TIMES / count);
		max = max > MAXMONEY ? MAXMONEY : max;
		for (int i = 0; i < count; i++) {
			int one = random(money, MINMONEY, max, count - i);
			list.add(one);
			money -= one;
		}
		return list;
	}
	
	/**
	 * @param money
	 * @param minS
	 * @param maxS
	 * @param count
	 * @return
	 * @Author:lulei  
	 * @Description: 随机红包额度
	 */
	private int random(int money, int minS, int maxS, int count) {
		//红包数量为1,直接返回金额
		if (count == 1) {
			return money;
		}
		//如果最大金额和最小金额相等,直接返回金额
		if (minS == maxS) {
			return minS;
		}
		int max = maxS > money ? money : maxS;
		//随机产生一个红包
		int one = ((int)Math.rint(Math.random() * (max - minS) + minS))  % max + 1;
		int money1 = money - one;
		//判断该种分配方案是否正确
		if (isRight(money1, count -1)) {
			return one;
		} else {
			double avg = money1 / (count - 1);
			if (avg < MINMONEY) {
				//递归调用,修改红包最大金额
				return random(money, minS, one, count);
			}else if (avg > MAXMONEY) {
				//递归调用,修改红包最小金额
				return random(money, one, maxS, count);
			}
		}
		return one;
	}
	
	/**
	 * @param money
	 * @param count
	 * @return
	 * @Author:lulei  
	 * @Description: 此种红包是否合法
	 */
	private boolean isRight(int money, int count) {
		double avg = money / count;
		if (avg < MINMONEY) {
			return false;
		}
		if (avg > MAXMONEY) {
			return false;
		}
		return true;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub  
		RedPacketUtil util = new RedPacketUtil();
		System.out.println(JsonUtil.parseJson(util.splitRedPackets(20000, 100)));
	}
}

 

本文转载自:https://blog.csdn.net/xiaojimanman/article/details/50708860

共有 人打赏支持
小致dad
粉丝 134
博文 535
码字总数 580132
作品 0
济南
技术主管
关于电商中复杂促销手段的一个解决思路-规则表达式

一般来说,只要是面向零售,不管是B2C,C2C还是B2B2C模式,商城的促销模式都是差不多的,比如满就送,满x减x,满x赠x,满x折扣x,组合销售,优惠券等等。所有促销活动都有明确的起止周期,超...

jim19770812
2014/08/13
0
10
Java微信开发_00_资源汇总贴

1.微信公众平台技术文档(https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432) 2.微信企业号开发接口文档(http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%BB%E......

rayner
2017/08/07
0
0
Java虚拟机内存管理(一)—内存划分

Java 与 C++ 之间有一堵由内存动态分配和垃圾收集技术所围成的 “高墙”,墙外面的人想进去,墙里面的人却想出来。——《深入理解Java虚拟机:JVM高级特性与最佳时实践(第二版)》周志明 Ja...

Wizey
08/30
0
0
Java虚拟机JVM内部体系结构

Java虚拟机JVM内部体系结构 易百教程移动端:请扫描本页面底部(右侧)二维码并关注微信公众号,回复:"教程" 选择相关教程阅读或直接访问:http://m.yiibai.com 。 JVM(Java虚拟机)是一个抽象...

LYQ1990
07/18
0
0
一个疑似JVM GC BUG的案例分析

概述 一般来我们公司面试,我都会习惯性地问点JVM相关的问题,当然如果他觉得JVM掌握得不错,我会适当多问点,毕竟知音难觅,难得在这么一条”狭路上相逢”。(如果你想在技术深度上有所积累,...

PerfMa
09/03
0
0

没有更多内容

加载失败,请刷新页面

加载更多

AIX 系统 (资源)

AIX--zabbix agent的编译安装以及遇到的“坑” 荐 : http://blog.51cto.com/lythjq/2128354 AIX--操作系统安装(AIX 6.1) : http://blog.51cto.com/lythjq/2128119...

寰宇01
42分钟前
0
0
Spring 转换 model 为 json 时增加属性

缘起 目前的项目中有个需求是在附件对象转换成 json 时增加个 url 属性,以前的方式是在返回附件对象或列表时候做一次统一处理,这次想看看 spring 或者 jackson fasterxml 是否自带类似功能...

郁也风
42分钟前
1
0
10大PHP比特币开源项目

如果你是一个Phper,如果你希望学习区块链,那么本文列出的 10个开源的Php比特币项目,将有助于你了解在自己的应用中 如何加入对比特币的支持。 如果你希望快速掌握使用Php对接比特币钱包的方...

汇智网教程
今天
1
0
springclould feign客户端添加全局参数

用springclould feign作为调用服务的客户端,一般来说参数可以写在feignclient的方法参数里 有时需要所有feign请求都统一添加一些参数,例如token用于鉴权等,可以这样做: 添加一个配置类,...

canneljls
今天
1
0
win32截屏并rgb24转yuv420

//最终f的内存布局为BGRA格式,需要保证buf长度足够(>w*h*4)void ScreenCap(void* buf, int w, int h){ HWND hDesk = GetDesktopWindow(); HDC hScreen = GetDC(hDesk); ......

styleman
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部