抽奖活动(一)-Alias算法

抽奖活动在项目开发中其实是比较常见的问题,这篇文章主要介绍一下Alias算法解决随机类型概率问题:

对于开发抽奖活动的任务来说,奖品一般放置在数据库中,而概率分为一下两种:

第一种是:所有奖项的概率和为1,也就是说本次活动所有参与人员都会中奖,中奖的等级随奖品的概率而定;

第二种是:所有的奖项的概率和小于1,也就是说存在未中奖的情况,其实这种情况也可以归结为第一种,将剩余的概率归到未中奖事件上,然后再将未中奖看做一个奖项,这种情况就和第一种相似了。

而我今天主要对第二种进行剖析一下,如果掌握了第二种,我想第一种也就迎刃而解了。

首先看一下代码:

package com.thinkive.app.bonus.business.utils;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Random;
import com.thinkive.base.jdbc.DataRow;
public class AliasMethod {

    private final double[] probability;
    private final int[] alias;
    private final int length;
    private final Random rand;


    public AliasMethod(List<Double> prob) {
this(prob, new Random());
    }


    public AliasMethod(List<Double> prob, Random rand) {
/* Begin by doing basic structural checks on the inputs. */
if (prob == null || rand == null)
    throw new NullPointerException();
if (prob.size() == 0)
    throw new IllegalArgumentException("Probability vector must be nonempty.");


this.rand = rand;
this.length = prob.size();
this.probability = new double[length];
this.alias = new int[length];


double[] probtemp = new double[length];
Deque<Integer> small = new ArrayDeque<Integer>();
Deque<Integer> large = new ArrayDeque<Integer>();


/* divide elements into 2 groups by probability */
for (int i = 0; i < length; i++) {
    probtemp[i] = prob.get(i) * length; /* initial probtemp */
    if (probtemp[i] < 1.0)
small.add(i);
    else
large.add(i);
}


while (!small.isEmpty() && !large.isEmpty()) {
    int less = small.pop();
    int more = large.pop();
    probability[less] = probtemp[less];
    alias[less] = more;
    probtemp[more] = probtemp[more] - (1.0 - probability[less]);
    if (probtemp[more] < 1.0)
small.add(more);
    else
large.add(more);
}
/*
* At this point, everything is in one list, which means that the
* remaining probabilities should all be 1/n. Based on this, set them
* appropriately.
*/
while (!small.isEmpty())
    probability[small.pop()] = 1.0;
while (!large.isEmpty())
    probability[large.pop()] = 1.0;
    }
    /**
     * Samples a value from the underlying distribution.
     * 
     */
    public int next() {
/* Generate a fair die roll to determine which column to inspect. */
int column = rand.nextInt(length);


/* Generate a biased coin toss to determine which option to pick. */
boolean coinToss = rand.nextDouble() < probability[column];


/* Based on the outcome, return either the column or its alias. */
return coinToss ? column : alias[column];
    }


    /* 概率测试 */
    public static void main(String[] argv) {
List<Double> prob = new ArrayList<Double>();


//prob.add(0.25); /* 0.01% 几率命中 */
//prob.add(0.25); /* 50% 几率命中 */
//prob.add(0.25); /* 50% 几率命中 */
prob.add(0.001);//一等奖
prob.add(0.05);//二等奖
prob.add(0.1);//三等奖
prob.add(0.3);
prob.add(0.549);
String[] str={"一等奖","二等奖","三等奖","四等奖","未中奖"};
int[] test={0,0,0,0};
AliasMethod am = new AliasMethod(prob);
for(int i=0;i<100;i++){
    System.out.println(str[am.next()]);
}
    }
}


运行结果大家可以自行测试一下,中奖次数符合概率分布。

下面就介绍一下的Alias算法的基本原理:

我们以一下概率分布为主

奖项一等奖二等奖三等奖未中奖
概率0.10.20.30.4
概率分布如下图:


然后将各个分类的概率乘于4(注意:这个数字是事件的总数,目的是将每种事件的的概率都填补成1),结果为:


下面将要进行的是一个“借”概率的过程,由图可知,第三种第四种的概率分别为12/10和16/10,大于1,而前两种的概率小于一,将第三种多余的2/10拿出来放到第一种上面,将第四种多余的6/10拿出4/10分到第一种上,剩下的2/10分到第二种上,就会使每种概率都为一。结果如下:


暂时将每种事件“借”来的概率默认为这个事件本身自我的概率,由此可见,每种事件的概率都为一,每种事件发生的概率都相同了,到这,有人就会问,第三种第四种明明概率大于一,就算是借出去也应该是自己的,怎么能分给第一种第二种呐?

对,下面我将解答这个疑问:

将每种概率都转化成一,这是一个平均资源的过程,保证资源落到每块的概率相等,比如:向分配好的区域内随机放40个事件,按照概率分布,每个区域都会有10个事件,这个过程就叫平均资源,然后再第一个区域内的10个事件就会有2个事件属于第三种,有4个事件属于第四种,这个过程我称为“内部消化”,这样就能保证概率大于一的事件能“维护”好自己的权益了。

以上便是我对Alias算法通俗的理解,我的这篇文章只是帮助大家理解一下这个算法,至于它的计算过程和专业的计算原理,大家可以百度一下,百度上关于这个算法的原理讲解的文章有很多。

如有不足,欢迎及时提出,谢谢!

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值