Alias随机算法

  1 /// <summary>
  2     /// Alias别名算法
  3     /// </summary>
  4     public class AliasMethod
  5     {
  6         /* The probability and alias tables. */
  7         private int[] _alias;
  8         private double[] _probability;
  9 
 10         public AliasMethod(List<Double> probabilities)
 11         {
 12 
 13             /* Allocate space for the probability and alias tables. */
 14             _probability = new double[probabilities.Count];
 15             _alias = new int[probabilities.Count];
 16 
 17             /* Compute the average probability and cache it for later use. */
 18             double average = 1.0 / probabilities.Count;
 19 
 20             /* Create two stacks to act as worklists as we populate the tables. */
 21             var small = new Stack<int>();
 22             var large = new Stack<int>();
 23 
 24             /* Populate the stacks with the input probabilities. */
 25             for (int i = 0; i < probabilities.Count; ++i)
 26             {
 27                 /* If the probability is below the average probability, then we add
 28                  * it to the small list; otherwise we add it to the large list.
 29                  */
 30                 if (probabilities[i] >= average)
 31                     large.Push(i);
 32                 else
 33                     small.Push(i);
 34             }
 35 
 36             /* As a note: in the mathematical specification of the algorithm, we
 37              * will always exhaust the small list before the big list.  However,
 38              * due to floating point inaccuracies, this is not necessarily true.
 39              * Consequently, this inner loop (which tries to pair small and large
 40              * elements) will have to check that both lists aren't empty.
 41              */
 42             while (small.Count > 0 && large.Count > 0)
 43             {
 44                 /* Get the index of the small and the large probabilities. */
 45                 int less = small.Pop();
 46                 int more = large.Pop();
 47 
 48                 /* These probabilities have not yet been scaled up to be such that
 49                  * 1/n is given weight 1.0.  We do this here instead.
 50                  */
 51                 _probability[less] = probabilities[less] * probabilities.Count;
 52                 _alias[less] = more;
 53 
 54                 /* Decrease the probability of the larger one by the appropriate
 55                  * amount.
 56                  */
 57                 probabilities[more] = (probabilities[more] + probabilities[less] - average);
 58 
 59                 /* If the new probability is less than the average, add it into the
 60                  * small list; otherwise add it to the large list.
 61                  */
 62                 if (probabilities[more] >= average)
 63                     large.Push(more);
 64                 else
 65                     small.Push(more);
 66             }
 67 
 68             /* At this point, everything is in one list, which means that the
 69              * remaining probabilities should all be 1/n.  Based on this, set them
 70              * appropriately.  Due to numerical issues, we can't be sure which
 71              * stack will hold the entries, so we empty both.
 72              */
 73             while (small.Count > 0)
 74                 _probability[small.Pop()] = 1.0;
 75             while (large.Count > 0)
 76                 _probability[large.Pop()] = 1.0;
 77         }
 78 
 79         /**
 80          * Samples a value from the underlying distribution.
 81          *
 82          * @return A random value sampled from the underlying distribution.
 83          */
 84         public int next()
 85         {
 86 
 87             long tick = DateTime.Now.Ticks;
 88             var seed = ((int)(tick & 0xffffffffL) | (int)(tick >> 32));
 89             unchecked
 90             {
 91                 seed = (seed + Guid.NewGuid().GetHashCode() + new Random().Next(0, 100));
 92             }
 93             var random = new Random(seed);
 94             int column = random.Next(_probability.Length);
 95 
 96             /* Generate a biased coin toss to determine which option to pick. */
 97             bool coinToss = random.NextDouble() < _probability[column];
 98 
 99             return coinToss ? column : _alias[column];
100         }
101     }

初始化:

var alias = new AliasMethod(nList.Select(o => (double)o.probability).ToList());//初始化aliasMethod实例,传入概率List

使用:

var column = alias.next();//随机抽取,返回构造器传入概率List的索引

 

posted @ 2019-12-25 15:44  Eric.Hsu  阅读(476)  评论(0)    收藏  举报