Line论文中的Alias Sampling Algorithm 分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lily960427/article/details/78582982

http://blog.csdn.net/haolexiao/article/details/65157026
找了一下这篇写的最清晰了,然后结合Line的源码分析一下。

原理:
原文举例如下:比如一个随机事件包含四种情况,每种情况发生的概率分别为: 1/2,1/3,1/12,1/12,问怎么用产生符合这个概率的采样方法。

Alias方法按照均值1/N进行归一化,其总面积为N,并且分为1*N个长方形,每一列面积为1。
通过将概率大于1的事件的面积补到概率小于1的事件中,来保证每列的概率和为1,并且每一列最多两个事件。
具体带图的看原博客。

Alias Method具体算法如下:
1. 将整个概率分布拉平成为一个1*N的长方形即为Alias Table,构建上面那张图之后,储存两个数组:
(1)一个里面存着第i列对应的事件i矩形站的面积百分比【也即其概率】,上图的话数组就为Prab[2/3, 1, 1/3, 1/3],
(2)另一个数组里面储存着第i列不是事件i的另外一个事件的标号,像上图就是Alias[2 NULL 1 1]
2.产生两个随机数,第一个产生1~N 之间的整数i,决定落在哪一列。扔第二次骰子,0~1之间的任意数,判断其与Prab[i]大小,如果小于Prab[i],则采样i,如果大于Prab[i],则采样Alias[i]

在代码中的实现思路:
构建方法:
1.找出其中面积小于等于1的列,如i列,这些列说明其一定要被别的事件矩形填上,所以在Prab[i]中填上其面积
2.然后从面积大于1的列中,选出一个,比如j列,用它将第i列填满,然后Alias[i] = j,第j列面积减去填充用掉的面积。

以上两个步骤一直循环,直到所有列的面积都为1了为止。

然后结合下line论文中采用这个方法的源码分析一下,加了注释供理解参考。

/* The alias sampling algorithm, which is used to sample an edge in O(1) time. */
void InitAliasTable()
{
    alias = (long long *)malloc(num_edges*sizeof(long long)); 
    prob = (double *)malloc(num_edges*sizeof(double));//概率数组 存第i列事件i占的面积百分比 

    //如果初始化失败 
    if (alias == NULL || prob == NULL)
    {
        printf("Error: memory allocation failed!\n");
        exit(1);
    }

    double *norm_prob = (double*)malloc(num_edges*sizeof(double)); //归一化的概率数组 存放 i列事件i占的面积百分比
    long long *large_block = (long long*)malloc(num_edges*sizeof(long long));//面积大于1的数组 
    long long *small_block = (long long*)malloc(num_edges*sizeof(long long));//面积小于1的数组 

    //如果初始化失败 
    if (norm_prob == NULL || large_block == NULL || small_block == NULL)
    {
        printf("Error: memory allocation failed!\n");
        exit(1);
    }

    double sum = 0;
    long long cur_small_block, cur_large_block;
    long long num_small_block = 0, num_large_block = 0;

    for (long long k = 0; k != num_edges; k++) sum += edge_weight[k];//统计所有边的权重和 

    for (long long k = 0; k != num_edges; k++) norm_prob[k] = edge_weight[k] * num_edges / sum;//得到第i列中事件i本身所占的百分比 每一列面积为1 总面积为1*num_edges
    //分为两组,占比大于1和小于1的 
    for (long long k = num_edges - 1; k >= 0; k--)
    {
        if (norm_prob[k]<1)
            small_block[num_small_block++] = k;//占比小于1 
        else
            large_block[num_large_block++] = k;//占比大于1 
    }


    while (num_small_block && num_large_block)//直到每一列的占比都为1 
    {
        cur_small_block = small_block[--num_small_block];//当前小边的序号 
        cur_large_block = large_block[--num_large_block];//当前大边的序号 
        prob[cur_small_block] = norm_prob[cur_small_block];//把归一化占比赋给Prob 
        alias[cur_small_block] = cur_large_block;//用面积大于1的去填充面积小于1的  alias中存,不是事件i的序号,即用来填充的事件的序号 
        norm_prob[cur_large_block] = norm_prob[cur_large_block] + norm_prob[cur_small_block] - 1;//得到减去用来填充的剩下的面积

        if (norm_prob[cur_large_block] < 1)//如果剩下的面积小于1 则归到小块面积 
            small_block[num_small_block++] = cur_large_block;
        else
            large_block[num_large_block++] = cur_large_block;
    }


    while (num_large_block) prob[large_block[--num_large_block]] = 1;
    while (num_small_block) prob[small_block[--num_small_block]] = 1;

    free(norm_prob);
    free(small_block);
    free(large_block);
}

猜你喜欢

转载自blog.csdn.net/lily960427/article/details/78582982