该方法构造一个别名表,每次采样时,通过两次rand()来决定采样值,构造的Alias表如下:
val | 1 | 2 | 3 | 4 |
alias | 3 | 4 | 4 | - |
prob | 0.4 | 0.8 | 0.6 | 1.0 |
根据该表,采样的过程如下,在第一轮我们按 1/N 的概率选择一个采样值,第二轮中根据 alias 表中的概率,看是选择该值还是其别名;如对于val=1,其在第一轮被选中的概率是 1/4=0.25,在第二轮中选择 val=1 而不是其别名(alias=3)的概率是prob=0.4;因此采样值val=1的概率为 0.25*0.4 = 0.1,与PDF符合。读者可自行验证其它几个采样值是否与PDF相符。采用该方法的采样复杂度为O(1)。
具体用代码显现
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Random;
import java.util.TreeMap;
public final class AliasMethod {
/* The random number generator used to sample from the distribution. */
private final Random random;
/* The probability and alias tables. */
private final int[] alias;
private final double[] probability;
/**
* Constructs a new AliasMethod to sample from a discrete distribution and
* hand back outcomes based on the probability distribution.
* <p/>
* Given as input a list of probabilities corresponding to outcomes 0, 1,
* ..., n - 1, this constructor creates the probability and alias tables
* needed to efficiently sample from this distribution.
*
* @param probabilities The list of probabilities.
*/
public AliasMethod(List<Double> probabilities) {
this(probabilities, new Random());
}
/**
* Constructs a new AliasMethod to sample from a discrete distribution and
* hand back outcomes based on the probability distribution.
* <p/>
* Given as input a list of probabilities corresponding to outcomes 0, 1,
* ..., n - 1, along with the random number generator that should be used
* as the underlying generator, this constructor creates the probability
* and alias tables needed to efficiently sample from this distribution.
*
* @param probabilities The list of probabilities.
* @param random The random number generator
*/
public AliasMethod(List<Double> probabilities, Random random) {
/* Begin by doing basic structural checks on the inputs. */
if (probabilities == null || random == null)
throw new NullPointerException();
if (probabilities.size() == 0)
throw new IllegalArgumentException("Probability vector must be nonempty.");
/* Allocate space for the probability and alias tables. */
probability = new double[probabilities.size()];
alias = new int[probabilities.size()];
/* Store the underlying generator. */
this.random = random;
/* Compute the average probability and cache it for later use. */
final double average = 1.0 / probabilities.size();
/* Make a copy of the probabilities list, since we will be making
* changes to it.
*/
probabilities = new ArrayList<Double>(probabilities);
/* Create two stacks to act as worklists as we populate the tables. */
Deque<Integer> small = new ArrayDeque<Integer>();
Deque<Integer> large = new ArrayDeque<Integer>();
/* Populate the stacks with the input probabilities. */
for (int i = 0; i < probabilities.size(); ++i) {
/* If the probability is below the average probability, then we add
* it to the small list; otherwise we add it to the large list.
*/
if (probabilities.get(i) >= average)
large.add(i);
else
small.add(i);
}
/* As a note: in the mathematical specification of the algorithm, we
* will always exhaust the small list before the big list. However,
* due to floating point inaccuracies, this is not necessarily true.
* Consequently, this inner loop (which tries to pair small and large
* elements) will have to check that both lists aren't empty.
*/
while (!small.isEmpty() && !large.isEmpty()) {
/* Get the index of the small and the large probabilities. */
int less = small.removeLast();
int more = large.removeLast();
/* These probabilities have not yet been scaled up to be such that
* 1/n is given weight 1.0. We do this here instead.
*/
probability[less] = probabilities.get(less) * probabilities.size();
alias[less] = more;
/* Decrease the probability of the larger one by the appropriate
* amount.
*/
probabilities.set(more,
(probabilities.get(more) + probabilities.get(less)) - average);
/* If the new probability is less than the average, add it into the
* small list; otherwise add it to the large list.
*/
if (probabilities.get(more) >= 1.0 / probabilities.size())
large.add(more);
else
small.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. Due to numerical issues, we can't be sure which
* stack will hold the entries, so we empty both.
*/
while (!small.isEmpty())
probability[small.removeLast()] = 1.0;
while (!large.isEmpty())
probability[large.removeLast()] = 1.0;
}
/**
* Samples a value from the underlying distribution.
*
* @return A random value sampled from the underlying distribution.
*/
public int next() {
/* Generate a fair die roll to determine which column to inspect. */
int column = random.nextInt(probability.length);
/* Generate a biased coin toss to determine which option to pick. */
boolean coinToss = random.nextDouble() < probability[column];
/* Based on the outcome, return either the column or its alias. */
/* Log.i("1234","column="+column);
Log.i("1234","coinToss="+coinToss);
Log.i("1234","alias[column]="+coinToss);*/
return coinToss ? column : alias[column];
}
private static int o1=1;
private static int o2=2;
private static int o3=3;
private static int o4=10;
private static int o5=20;
private static int o6=30;
private static int o7=40;
private static int o8=50;
public static String outPrize(int num){
TreeMap<String, Double> map = new TreeMap<String, Double>();
map.put("一等奖", 0.0001*num*2);
map.put("二等奖", 0.001*num);
map.put("三等奖", 0.005);
map.put("四等奖", 0.01);
map.put("五等奖", 0.04);
map.put("六等奖", 0.1);
map.put("七等奖", 0.2);
map.put("八等奖", 0.35);
map.put("未中奖", 0.29);
List<Double> list = new ArrayList<Double>(map.values());
List<String> gifts = new ArrayList<String>(map.keySet());
AliasMethod method = new AliasMethod(list);
String val=gifts.get(method.next());
if(val.equals("一等奖")){
o1=o1-1;
}
if(val.equals("二等奖")){
o2=o2-1;
}
if(val.equals("三等奖")){
o3=o3-1;
}
if(val.equals("四等奖")){
o4=o4-1;
}
if(val.equals("五等奖")){
o5=o5-1;
}
if(val.equals("六等奖")){
o6=o6-1;
}
if(val.equals("七等奖")){
o7=o7-1;
}
if(val.equals("八等奖")){
o8=o8-1;
}
/*Map<String, AtomicInteger> resultMap = new HashMap<String, AtomicInteger>();
for (int i = 0; i < 5000; i++) {
int index = method.next();
String key = gifts.get(index);
if (!resultMap.containsKey(key)) {
resultMap.put(key, new AtomicInteger());
}
resultMap.get(key).incrementAndGet();
}
Integer timestampTwo=(int) (0+Math.random()*100);
for (String key : resultMap.keySet()) {
System.out.println(key + "==" + resultMap.get(key));
if(timestampTwo==resultMap.get(key).intValue()){
System.out.println("哎呀 "+key);
}
}*/
return val;
}
public static void main(String[] args) throws Exception {
int num = 1;
for(int i=0;i<10;i++){
if(i%100==0){
num=num+1;
}
System.out.println("第"+i+"次--哎呀 "+outPrize(num));
}
System.out.println("一等奖剩余"+o1);
System.out.println("二等奖剩余"+o2);
System.out.println("三等奖剩余"+o3);
System.out.println("四等奖剩余"+o4);
System.out.println("五等奖剩余"+o5);
System.out.println("六等奖剩余"+o6);
System.out.println("七等奖剩余"+o7);
System.out.println("八等奖剩余"+o8);
}
}
参考地址:
http://blog.csdn.net/haolexiao/article/details/65157026