春節剛剛過去,但是紅包的餘溫卻未散去;現在過年發紅包拜年成爲一種新的潮流,作爲程序猿對算法的好奇遠遠要大於對紅包的好奇,這裏介紹一種自己想到的一種隨機紅包分配策略,還請大家多多指教。
算法介紹
一、紅包金額限制
對於微信紅包,我們知道沒人隨機的最小紅包是1分,最大金額是200元,這裏我們同樣來設置紅包的範圍,下面代碼我們統一金錢的單位爲分。
/**
* 1.總金額不能超過200*100 單位是分
* 2.每個紅包都要有錢,最低不能低於1分,最大金額不能超過200*100
*/
private static final int MINMONEY =1;
private static final int MAXMONEY =200*100;
二、判斷紅包金額是否合法注意這一步伴隨着整個算法,我們不僅要在分配紅包之前要判斷金額是否合法,同樣要在每個人暫定隨機金額後也要判斷剩餘的金額是否合法。
/**
* 紅包 合法性校驗
* @param money
* @param count
* @return
*/
private boolean isRight(int money, int count) {
double avg =money/count;
//小於最小金額
if(avg<MINMONEY){
return false;
//大於最大金額
}else if(avg>MAXMONEY){
return false;
}
return true;
}
三、隨機產生一個紅包這裏我們採用隨機的方式產生一個在MINMONEY和MAXMONEY之間的一個紅包,產生紅包之後,我們需要判斷剩餘的錢是否是合法紅包,如果不是合法紅包,我們就重新產生分配方案,在重新產生分配方案的時候,我們需要確定一個事情,是產生的紅包過大還是過小,如果紅包過大,下次就隨機一個小值到本次紅包金額的一個紅包,如果紅包金額過小,我們就產生一個紅包金額到大值的一個紅包。
/**
* 隨機分配一個紅包
* @param money
* @param minS :最小金額
* @param maxS :最大金額(每個紅包的默認Times倍最大值)
* @param count
* @return
*/
private int randomRedPacket(int money, int minS, int maxS, int count) {
//若是隻有一個,直接返回紅包
if(count==1){
return money;
}
//若是最小金額紅包 == 最大金額紅包, 直接返回最小金額紅包
if(minS ==maxS){
return minS;
}
//校驗 最大值 max 要是比money 金額高的話? 去 money 金額
int max = maxS>money ? money : maxS;
//隨機一個紅包 = 隨機一個數* (金額-最小)+最小
int one =((int)Math.rint(Math.random()*(max-minS)+minS));
//剩下的金額
int moneyOther =money-one;
//校驗這種隨機方案是否可行,不合法的話,就要重新分配方案
if(isRight(moneyOther, count-1)){
return one;
}else{
//重新分配
double avg =moneyOther /(count-1);
//本次紅包過大,導致下次的紅包過小;如果紅包過大,下次就隨機一個小值到本次紅包金額的一個紅包
if(avg<MINMONEY){
//遞歸調用,修改紅包最大金額
return randomRedPacket(money, minS, one, count);
}else if(avg>MAXMONEY){
//遞歸調用,修改紅包最小金額
return randomRedPacket(money, one, maxS, count);
}
}
return one;
}
四、實現紅包分配這裏爲了避免某一個紅包占用大量資金,我們需要設定非最後一個紅包的最大金額,我們把他設置爲紅包金額平均值的N倍;有了一、二、三中的方法,我們就可以來實現紅包的分配了。
/**
* 這裏爲了避免某一個紅包占用大量資金,我們需要設定非最後一個紅包的最大金額,我們把他設置爲紅包金額平均值的N倍;
*/
private static final double TIMES =2.1;
/**
* 拆分紅包
* @param money :紅包總金額
* @param count :個數
* @return
*/
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 = randomRedPacket(money,MINMONEY,max,count-i);
list.add(one);
money -=one;
}
return list;
}
五、完整代碼:
package com.zeng.redEnvelopes;
import java.util.ArrayList;
import java.util.List;
/**
* 微信隨機分配紅包算法
*
* @author leo-zeng
*
*/
public class RedEnvelopesDemo {
/**
* 1.總金額不能超過200*100 單位是分
* 2.每個紅包都要有錢,最低不能低於1分,最大金額不能超過200*100
*/
private static final int MINMONEY =1;
private static final int MAXMONEY =200*100;
/**
* 這裏爲了避免某一個紅包占用大量資金,我們需要設定非最後一個紅包的最大金額,我們把他設置爲紅包金額平均值的N倍;
*/
private static final double TIMES =2.1;
/**
* 拆分紅包
* @param money :紅包總金額
* @param count :個數
* @return
*/
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 = randomRedPacket(money,MINMONEY,max,count-i);
list.add(one);
money -=one;
}
return list;
}
/**
* 隨機分配一個紅包
* @param money
* @param minS :最小金額
* @param maxS :最大金額(每個紅包的默認Times倍最大值)
* @param count
* @return
*/
private int randomRedPacket(int money, int minS, int maxS, int count) {
//若是隻有一個,直接返回紅包
if(count==1){
return money;
}
//若是最小金額紅包 == 最大金額紅包, 直接返回最小金額紅包
if(minS ==maxS){
return minS;
}
//校驗 最大值 max 要是比money 金額高的話? 去 money 金額
int max = maxS>money ? money : maxS;
//隨機一個紅包 = 隨機一個數* (金額-最小)+最小
int one =((int)Math.rint(Math.random()*(max-minS)+minS));
//剩下的金額
int moneyOther =money-one;
//校驗這種隨機方案是否可行,不合法的話,就要重新分配方案
if(isRight(moneyOther, count-1)){
return one;
}else{
//重新分配
double avg =moneyOther /(count-1);
//本次紅包過大,導致下次的紅包過小;如果紅包過大,下次就隨機一個小值到本次紅包金額的一個紅包
if(avg<MINMONEY){
//遞歸調用,修改紅包最大金額
return randomRedPacket(money, minS, one, count);
}else if(avg>MAXMONEY){
//遞歸調用,修改紅包最小金額
return randomRedPacket(money, one, maxS, count);
}
}
return one;
}
/**
* 紅包 合法性校驗
* @param money
* @param count
* @return
*/
private boolean isRight(int money, int count) {
double avg =money/count;
//小於最小金額
if(avg<MINMONEY){
return false;
//大於最大金額
}else if(avg>MAXMONEY){
return false;
}
return true;
}
public static void main(String[] args) {
//隨機一個188.88 5個紅包
RedEnvelopesDemo dd = new RedEnvelopesDemo();
//單位是分
System.out.println(dd.splitRedPackets(18888, 5));
}
}
六、紅包分配方案評估上面介紹了紅包的基本算法,下面我們就對算法進行一次驗證,假設有一個200元100份的紅包,我們來看一下最後的分配方案。
轉自:http://www.llwjy.com/blogdetail/80ad983554a0668be92b5b53a486c55e.html