抽奖

由于最近项目需要做一个抽奖活动,于是实现了一个简单的抽奖算法,可以控制抽奖概率。提到抽奖,想必大家都见过玩过转轮抽奖吧,投一个币,然后转轮抽奖开始转动,当几个轮子的图片都是一样的时候就表示中奖了(当然还有大转盘的玩法,即投币后指针开始转动,转动停止后指针指向的那个位置就是判断中奖与否)。

1、抽奖原理

假设现在有三个转轮,每个转轮上有(1~10)数字图片。中奖情况如下:

大转盘抽奖算法java 抽奖大转盘规则_随机数

每一次投币之后,系统会返回3个数字,中奖情况可以自由设定:

* 三个数字都相同:中大奖

* 三个数字中有两个相同:中小奖或不算中奖

* 三个数字都不同:没中奖

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]
============================抽奖结束===============================