抽奖算法:
package test;
import java.util.List;
public class PrizeMathRandom {
/**
* 根据Math.random()产生一个double型的随机数,判断每个奖品出现的概率
* RegisteredMarketingCouponMapping该对象为抽奖活动与奖品挂关系表的实体
* @param prizes
* @return random:奖品列表prizes中的序列(prizes中的第random个就是抽中的奖品)
*/
public int getPrizeIndex(List<RegisteredMarketingCouponMapping> prizes) {
// DecimalFormat df = new DecimalFormat("######0.00");
int random = -1;
try{
//计算总权重
double sumWeight = 0;
for(RegisteredMarketingCouponMapping p : prizes){
//如果奖品的数量为0或者不出奖则置奖品的数量为0,让其无法抽中
sumWeight += Double.parseDouble(p.getWinningRate())*(p.getCouponQty() == 0 || p.getCanAward().equals("0") ? 0 : 1);
}
if(sumWeight <= 0){
System.out.println("老板破产了");
return random;
}
//产生随机数,0-1之间的随机数
double randomNumber = Math.random();
//根据随机数在所有奖品分布的区域并确定所抽奖品
double d1 = 0;
double d2 = 0;
for(int i=0;i<prizes.size();i++){
d2 += Double.parseDouble(prizes.get(i).getWinningRate())*(prizes.get(i).getCouponQty() == 0 || prizes.get(i).getCanAward().equals("0") ? 0 : 1)/sumWeight;
if(i==0){
d1 = 0;
}else{
d1 +=Double.parseDouble(prizes.get(i-1).getWinningRate())*(prizes.get(i-1).getCouponQty() == 0 || prizes.get(i-1).getCanAward().equals("0") ? 0 : 1)/sumWeight;
}
if(randomNumber >= d1 && randomNumber <= d2){
random = i;
break;
}
}
}catch(Exception e){
System.out.println("生成抽奖随机数出错,出错原因:" +e.getMessage());
}
return random;
}
public static void main(String[] args) {
List<RegisteredMarketingCouponMapping> prizes = new ArrayList<RegisteredMarketingCouponMapping>();
String[] canAwards = {"1", "0", "1", "1", "0", "0", "0", "0"};
Integer[] qty = { 100, 80, 1, -1, 30, 150, 80, 150};
String[] winningRates = {"0", "0.125", "0.0001", "0.0003", "0.025", "0.2", "0.1", "0.2"};
for(int i = 0; i < 8; i++){
RegisteredMarketingCouponMapping prize = new RegisteredMarketingCouponMapping();
prize.setId("0000" + i);
prize.setCanAward(canAwards[i]);
prize.setCouponQty(qty[i]);
prize.setWinningRate(winningRates[i]);
prizes.add(prize);
}
PrizeMathRandom random = new PrizeMathRandom();
for(int i = 0; i < 100; i++){
System.out.println("index----------->" + random.getPrizeIndex(prizes));
}
}
}
调用上面抽奖算法的例子
抽奖用到的实体类:
package test;
import java.io.Serializable;
/**
* 抽奖活动-奖品关系实体类
* @author robin_wang
*
*/
public class RegisteredMarketingCouponMapping implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* 抽奖活动-奖品id
*/
private String id;
/**
* 抽奖活动活动id
*/
private String registeredMarketingId;
/**
* 奖品id
*/
private String couponId;
/**
* 中奖概率
*/
private String winningRate;
/**
* 奖品剩余数量
* 如果为负数则为无穷多
* 如果为0,不参与后台抽奖仅做微端展示
* 如果大于0则参与后台出奖,抽中时数量减1
*/
private Integer couponQty;
/**
* 是否可出奖 1 可出奖 0不出奖
*/
private String canAward;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id == null ? null : id.trim();
}
public String getRegisteredMarketingId() {
return registeredMarketingId;
}
public void setRegisteredMarketingId(String registeredMarketingId) {
this.registeredMarketingId = registeredMarketingId == null ? null : registeredMarketingId.trim();
}
public String getCouponId() {
return couponId;
}
public void setCouponId(String couponId) {
this.couponId = couponId == null ? null : couponId.trim();
}
public Integer getCouponQty() {
return couponQty;
}
public void setCouponQty(Integer couponQty) {
this.couponQty = couponQty;
}
public String getWinningRate() {
return winningRate;
}
public void setWinningRate(String winningRate) {
this.winningRate = winningRate;
}
public String getCanAward() {
return canAward;
}
public void setCanAward(String canAward) {
this.canAward = canAward;
}
}
本次做的抽奖活动逻辑:
一、业务需求:
1、微商城的会员可以进行抽奖,而且如果会员分享了活动页面,则会添加1次抽奖机会
2、抽奖活动和奖品挂关系时,要设置奖品的剩余数量、权重(概率)、是否出奖,奖品必须为8
个
二、建表:
奖品、抽奖活动、抽奖活动-奖品关系表、抽奖活动和会员关系表(考虑会员每分享一次活动多一次抽奖机会所建)、中奖记录表、会员奖品表(该表的奖品只有优惠券)
三、
抽奖的算法是根据每个奖品的权重进行的,所以权重可以填任意数字,本次考虑与用户好
理解,权重以概率相称,而且都是小于1的小数,所有奖品和必须为1。
四、做法:
1、奖品表初始化一个谢谢参与,始终不动,不在后台系统列表展示,也不能选择
2、为抽奖活动添加奖品不足8个保存时,后台会将剩下的几个全部以谢谢参与奖代替
3、编辑抽奖活动的概率时,谢谢参与奖的概率不能编辑,非谢谢参与奖的概率发生变化时,系统会自动计算谢谢参与奖的概率和并平分给当前活动的所有谢谢参与奖。谢谢参与奖必须出奖,且数量为无限多,否则出奖会出问题(因为要考虑到没有抽中奖品时转盘一定要落到谢谢参与上而不是说没有奖品了)。
五、不足:
1、因为当时用的是上个系统的数据库,不清楚有哪些表,建了个中奖记录表,一开始以为建错
了,不过误打误撞,因为我们的需求和上个系统有区别,我们的奖品除了优惠券和谢谢参与之
外还有虚拟奖(青海五日游等),微商城购物车页面要获取会员奖品表中的优惠券,然后进行
使用,更新会员奖品表和中奖记录表。虚拟奖线下使用更新中奖记录表。
2、下次做相关的业务可以用剩余数量做权重进行抽奖,而不是多一个概率字段,并且限制它的大小,以及某活动所有关系奖品的概率之和