Java仿實現微信紅包分配算法

春節剛剛過去,但是紅包的餘溫卻未散去;現在過年發紅包拜年成爲一種新的潮流,作爲程序猿對算法的好奇遠遠要大於對紅包的好奇,這裏介紹一種自己想到的一種隨機紅包分配策略,還請大家多多指教。

算法介紹

一、紅包金額限制

      對於微信紅包,我們知道沒人隨機的最小紅包是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





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章