抽奖
由于最近项目需要做一个抽奖活动,于是实现了一个简单的抽奖算法,可以控制抽奖概率。提到抽奖,想必大家都见过玩过转轮抽奖吧,投一个币,然后转轮抽奖开始转动,当几个轮子的图片都是一样的时候就表示中奖了(当然还有大转盘的玩法,即投币后指针开始转动,转动停止后指针指向的那个位置就是判断中奖与否)。
1、抽奖原理
假设现在有三个转轮,每个转轮上有(1~10)数字图片。中奖情况如下:
* 三个数字都相同:中大奖
* 三个数字中有两个相同:中小奖或不算中奖
* 三个数字都不同:没中奖
2、概率控制
每一个数字都有相应的概率值:
数字 物品 概率
1 苹果 5%
2 橘子 7%
3 香蕉 9%
4 葡萄 13%
5 荔枝 12%
6 柚子 14%
7 橙子 10%
8 柿子 11%
9 西瓜 8%
10 芒果 11%
每个转轮转动一次后会随机(根据概率)选中一个数字。
每个转轮上的数字的顺序是随机分配的。
第一个转轮选取过程(第一个默认顺序):
数字 物品 概率区段
1 苹果 0~5%
2 橘子 5~12%
3 香蕉 12~21%
4 葡萄 21~34%
5 荔枝 34~46%
6 柚子 46~60%
7 橙子 60~70%
8 柿子 70~81%
9 西瓜 81~89%
10 芒果 89~100%
* 在1~100之间产生一个随机数如:56
* 根据产生的随机数 56 放到转轮一中的概率区段去匹配,中奖数字是:6
第二个转轮选取过程:
数字 物品 概率区段
2 橘子 0~7%
1 苹果 7~12%
3 香蕉 12~21%
5 荔枝 21~33%
4 葡萄 33~46%
6 柚子 46~60%
7 橙子 60~70%
8 柿子 70~81%
10 芒果 81~92%
9 西瓜 92~100%
* 在1~100之间产生一个随机数如:77
* 根据产生的随机数77放到转轮一中的概率区段去匹配,中奖数字是:8
第三个转轮选取过程:
数字 物品 概率区段
4 葡萄 0~13%
10 芒果 13~24%
7 橙子 24~34%
1 苹果 34~39%
9 西瓜 39~47%
6 柚子 47~61%
3 香蕉 61~70%
8 柿子 70~81%
5 荔枝 81~93%
2 橘子 93~100%
* 在1~100之间产生一个随机数如:21
* 根据产生的随机数21放到转轮一中的概率区段去匹配,中奖数字是:10
最终的抽奖结果:6,8,10即:柚子,柿子,芒果,未中奖
3、代码
* 奖品类
package com.lottery.model;
/**
* @author zyd
* @date 2012-10-3
* @desc:
* 抽奖奖品
*/
public class Reward {
/**
* 奖品编号
*/
public int index;
/**
* 奖品名称
*/
public String name;
/**
* 中奖概率
*/
public int succPercent;
public Reward(int index, String name, int succPercent) {
super();
this.index = index;
this.name = name;
this.succPercent = succPercent;
}
@Override
public String toString() {
return "Reward [index=" + index + ", name=" + name + ", succPercent="
+ succPercent + "]";
}
}
* 抽奖算法类
package com.lottery.util;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.lottery.model.Reward;
/**
* @author zyd
* @date 2012-10-3
* @desc:
* 抽奖主类
*/
public class Lottery {
public static List<Reward> randomList;
/**
* 获取中奖编码数组
* @param rlist
* @param keyLength
* @return
*/
public List<Reward> getKeys(List<Reward> rlist,int keyLength){
List<Reward> list = new ArrayList<Reward>();
for(int i=0;i<keyLength;i++){
list.add(getKey(rlist));
}
return list;
}
/**
* 获取中奖编码
* @param rlist
* @return
*/
private Reward getKey(List<Reward> rlist) {
//随机列表
List<Reward> randomList = getRandomList(rlist);
//根据随机列表得到的概率区段
List<Integer> percentSteps = getPercentSteps(rlist);
//概率区段的最大值
int maxPercentStep = percentSteps.get(percentSteps.size()-1);
//在概率区段范围内取一个随机数
int randomStep = new Random().nextInt(maxPercentStep);
//中间元素的下标
int keyIndex = 0;
int begin = 0;
int end = 0;
for(int i=0;i<percentSteps.size();i++){
if(i == 0){
begin = 0;
}else{
begin = percentSteps.get(i-1);
}
end = percentSteps.get(i);
//判断随机数值是否在当前区段范围内
if(randomStep>begin && randomStep<=end){
keyIndex = i;
break;
}
}
return randomList.get(keyIndex);
}
/**
* 获取概率区段[如:10,15,25,30,40,60,75,80,90,95,100]
* @param rlist
* @return
*/
private List<Integer> getPercentSteps(List<Reward> rlist) {
List<Integer> percentSteps = new ArrayList<Integer>();
int percent = 0;
for(Reward r: rlist){
percent += r.succPercent;
percentSteps.add(percent);
}
return percentSteps;
}
/**
* 获取随机列表
* @param rlist
* @return
*/
private List<Reward> getRandomList(List<Reward> rlist){
List<Reward> oldList = new ArrayList<Reward>(rlist);
List<Reward> newList = new ArrayList<Reward>();
//随机排序的老序列中元素的下标
int randomIndex = 0;
//随机排序下标的取值范围
int randomLength = 0;
for(int i=0;i<rlist.size();i++){
//指向下标范围
randomLength = oldList.size()-1;
//取值范围元素的个数为多个时,从中随机选取一个元素的下标
if(randomLength != 0){
randomIndex = new Random().nextInt(randomLength);
//取值范围元素的个数为一个时,直接返回该元素的下标
}else{
randomIndex = 0;
}
//在新的序列当中添加元素,同时删除元素取值范围中的randomIndex下标所对应的元素
newList.add(oldList.remove(randomIndex));
}
return newList;
}
}
* 测试类
package com.lottery.test;
import java.util.ArrayList;
import java.util.List;
import com.lottery.model.Reward;
import com.lottery.util.Lottery;
/**
* @author zyd
* @date 2012-10-3
* @desc:
* 测试
*/
public class Test {
public static void main(String[] args){
List<Reward> rlist = initRewards();
Lottery lottery = new Lottery();
List<Reward> rewards = null;
for(int i=0;i<1000;i++){
rewards = lottery.getKeys(rlist, 3);
if(isWinner(rewards)){
System.out.println("============================抽奖开始 第"+i+"次 ===============================");
for(Reward r:rewards){
System.out.println(r);
}
System.out.println("============================抽奖结束===============================");
}
}
}
public static boolean isWinner(List<Reward> list){
boolean isWinner = false;
for(int i=0;i<list.size();i++){
for(int j=i+1;j<list.size();j++){
if(list.get(i).index != list.get(j).index){
return false;
}else{
isWinner =true;
}
}
}
return isWinner;
}
public static List<Reward> initRewards(){
List<Reward> rlist = new ArrayList<Reward>();
rlist.add(new Reward(1, "香蕉", 5));
rlist.add(new Reward(2, "苹果", 15));
rlist.add(new Reward(3, "橘子", 5));
rlist.add(new Reward(4, "葡萄", 15));
rlist.add(new Reward(5, "荔枝", 5));
rlist.add(new Reward(6, "西瓜", 5));
rlist.add(new Reward(7, "柚子", 20));
rlist.add(new Reward(8, "橙子", 10));
rlist.add(new Reward(9, "柿子", 5));
rlist.add(new Reward(10, "芒果", 15));
return rlist;
}
}
* 测试输出
============================抽奖开始 第82次 ===============================
Reward [index=1, name=香蕉, succPercent=5]
Reward [index=1, name=香蕉, succPercent=5]
Reward [index=1, name=香蕉, succPercent=5]
============================抽奖结束===============================
============================抽奖开始 第115次 ===============================
Reward [index=9, name=柿子, succPercent=5]
Reward [index=9, name=柿子, succPercent=5]
Reward [index=9, name=柿子, succPercent=5]
============================抽奖结束===============================
============================抽奖开始 第510次 ===============================
Reward [index=9, name=柿子, succPercent=5]
Reward [index=9, name=柿子, succPercent=5]
Reward [index=9, name=柿子, succPercent=5]
============================抽奖结束===============================
============================抽奖开始 第542次 ===============================
Reward [index=9, name=柿子, succPercent=5]
Reward [index=9, name=柿子, succPercent=5]
Reward [index=9, name=柿子, succPercent=5]
============================抽奖结束===============================
============================抽奖开始 第584次 ===============================
Reward [index=9, name=柿子, succPercent=5]
Reward [index=9, name=柿子, succPercent=5]
Reward [index=9, name=柿子, succPercent=5]
============================抽奖结束===============================
============================抽奖开始 第706次 ===============================
Reward [index=5, name=荔枝, succPercent=5]
Reward [index=5, name=荔枝, succPercent=5]
Reward [index=5, name=荔枝, succPercent=5]
============================抽奖结束===============================