抽奖活动(一)-Alias算法
2017-07-15 10:45
791 查看
抽奖活动在项目开发中其实是比较常见的问题,这篇文章主要介绍一下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算法的基本原理:
我们以一下概率分布为主
概率分布如下图:
然后将各个分类的概率乘于4(注意:这个数字是事件的总数,目的是将每种事件的的概率都填补成1),结果为:
下面将要进行的是一个“借”概率的过程,由图可知,第三种第四种的概率分别为12/10和16/10,大于1,而前两种的概率小于一,将第三种多余的2/10拿出来放到第一种上面,将第四种多余的6/10拿出4/10分到第一种上,剩下的2/10分到第二种上,就会使每种概率都为一。结果如下:
暂时将每种事件“借”来的概率默认为这个事件本身自我的概率,由此可见,每种事件的概率都为一,每种事件发生的概率都相同了,到这,有人就会问,第三种第四种明明概率大于一,就算是借出去也应该是自己的,怎么能分给第一种第二种呐?
对,下面我将解答这个疑问:
将每种概率都转化成一,这是一个平均资源的过程,保证资源落到每块的概率相等,比如:向分配好的区域内随机放40个事件,按照概率分布,每个区域都会有10个事件,这个过程就叫平均资源,然后再第一个区域内的10个事件就会有2个事件属于第三种,有4个事件属于第四种,这个过程我称为“内部消化”,这样就能保证概率大于一的事件能“维护”好自己的权益了。
以上便是我对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.1 | 0.2 | 0.3 | 0.4 |
然后将各个分类的概率乘于4(注意:这个数字是事件的总数,目的是将每种事件的的概率都填补成1),结果为:
下面将要进行的是一个“借”概率的过程,由图可知,第三种第四种的概率分别为12/10和16/10,大于1,而前两种的概率小于一,将第三种多余的2/10拿出来放到第一种上面,将第四种多余的6/10拿出4/10分到第一种上,剩下的2/10分到第二种上,就会使每种概率都为一。结果如下:
暂时将每种事件“借”来的概率默认为这个事件本身自我的概率,由此可见,每种事件的概率都为一,每种事件发生的概率都相同了,到这,有人就会问,第三种第四种明明概率大于一,就算是借出去也应该是自己的,怎么能分给第一种第二种呐?
对,下面我将解答这个疑问:
将每种概率都转化成一,这是一个平均资源的过程,保证资源落到每块的概率相等,比如:向分配好的区域内随机放40个事件,按照概率分布,每个区域都会有10个事件,这个过程就叫平均资源,然后再第一个区域内的10个事件就会有2个事件属于第三种,有4个事件属于第四种,这个过程我称为“内部消化”,这样就能保证概率大于一的事件能“维护”好自己的权益了。
以上便是我对Alias算法通俗的理解,我的这篇文章只是帮助大家理解一下这个算法,至于它的计算过程和专业的计算原理,大家可以百度一下,百度上关于这个算法的原理讲解的文章有很多。
如有不足,欢迎及时提出,谢谢!
相关文章推荐
- 简单的活动抽奖算法&方案
- 在营销活动中的抽奖算法放送
- 在营销活动中的抽奖算法放送
- 关于网站抽奖活动算法的尝试
- 关于网站抽奖活动算法的尝试
- C#&PHP&Java实现Alias Method概率抽奖算法
- 随机获取礼物活动总结(抽奖算法)
- Java从一个连续的自然数区间中,随机获取几个数(这几个数是固定的,一种抽奖算法及节省空间地保存结果)
- PHP控制中奖概率的抽奖算法
- 贪心算法 安排活动的问题 最大限度的利用有限资源
- 【算法导论】贪心算法之活动安排问题
- java抽奖算法
- “飞信积分我知道”每日抽奖活动(附答案)
- php抽奖概率算法
- 贪婪算法(活动安排问题)
- Java抽奖抢购算法
- 抽奖概率算法
- 【贪心+优先队列】1428 活动安排问题【51nod】【难度:2级算法题】
- 【算法导论实验5】贪心-活动安排问题与背包问题
- Machine Learning读书会,面试算法讲座,创业活动,算法班(15年10月)