昨天服务器端的童靴遇到了一个问题,被我偷听到了~问题是这样的,某物品在制作时的产出概率是p,要制作n次,除了把rand循环n次之外,还有啥方法么?……
这个问题之前宝宝在给另一位服务器童靴写测试代码的时候碰到过,当时的问题是,想要在一个技能范围(例如球体)内随机的产生爆炸(一个点),怎么均匀的随机在一个球体中?
这个拿随机分布的视角看一下,尊的都是草鸡简单的问题……然而我当时还是没有给服务器童靴讲清(之前那位也没有讲清=.=),服务器童靴把我涂涂涂的草稿纸拿走了但是我涂得也是异常混乱而且貌似好多错~所以我打算好好写下来,以便以后直接用=.=
问题分析
基本思路是,随机生成一个数a,然后对这个数使用一个应设函数f(a),得到另一个同范围的数,而后者的分布将变为所需的分布。
也就是说,给定一个概率密度函数p(m)(数学函数),如何利用均匀分布的函数 rand()(程序函数) ,来得到新的给定概率密度的函数 new_rand()(程序函数)?(m 在[0,1]内)
具体数学过程以后我补补数学课再写 =.= ,依靠女人超强的第六感我猜测的结论如下:
对于一个P(m)(m在[0,n])分布生成的数a(a在[0,1]),对它使用函数f(a)(f(a)和a在[0,1]),得到令f(a)的分布密度是Q(m),那么f(a) = P(Q^(-1)(a))。
代码(这里用了一个0~1上半周的余弦的分布):
// 生成一个均匀分布的 0~1 之间的double
double rand_double()
{
return (double)rand()/(double)RAND_MAX;
}
// 转换均匀分布的 0~1 之间的double为新的 0~1 之间的分布
double trans(double old)
{
return (asin( (old-0.5)*2 ) / PI_D2 )/2 +0.5; //这个函数将会被换成其
他需要的函数
}
// 测试函数:n个样本,总共dn段步长
typedef unsigned sample_count_type;
typedef unsigned step_count_type;
void test(sample_count_type n, step_count_type dn, sample_count_type* res)
{
double t_rand;
for(step_count_type i=0; i < dn; ++i)
{
*(res + i) = 0;
}
srand((unsigned int)time(0));
while(--n != 0)
{
t_rand = trans( rand_double() );
++ *(res + (step_count_type)(t_rand * dn));
}
}
int main()
{
const step_count_type dn = 10;
sample_count_type n = 500;
sample_count_type res[dn];
test(n, dn, res);
for(step_count_type i=0; i<dn; ++i)
{
cout<<res[i]<<" ";
}
cout<<endl;
return 0;
}
我的测试结果:
10 37 68 75 79 69 70 50 28 13
脑补一下这个分布,确实是中间凸一点~
回到现实中来~上边说到的那两个问题,后者容易解决一点,对球积分求逆就可以,各种方向的任君选择;但是前者内个二项分布的问题,我搜了一圈发现这货好像真的没有求和公式或者连续近似的积分~
在下唯一想到的方法就是数值的方法了……
就是用一个数组来近似表达一个连续函数的思想,在上边的trans函数上做。也是O(1)的复杂度~
如何插值随意~如何求积分数组也有各种数值积分方案随意~
=,= 突然赶脚以上是不是应该是数值策划干的事情啊……