3

如何计算圆内的随机点(有点均匀分布)?

我找到了这个答案(Udo Klein 的那个):使用 X,Y 坐标在圆内绘制点,我的 Objective C 实现如下所示:

-(CGPoint)getRandomPointInCircle:(CGPoint)cCenter radius:(float)cRadius {
    float r = cRadius * sqrtf(arc4random());
    float angle = sqrtf(2 * M_PI);
    float x = cCenter.x + r * cosf(angle);
    float y = cCenter.y + r * sinf(angle);
    CCLOG(@"--> rand point: %f,%f", x, y);
    return ccp(x,y);
}

现在有了一个中心为 5000,5000 和半径为 7100 的给定圆,它给了我这样的疯狂值:-324893408.000000,239372544.000000

请帮助一个数学傻瓜:)

/索伦

4

3 回答 3

3

你的问题是你没有把arc4random' 的输出放在一个范围内,但你也没有随机化角度。

首先,圆内随机分布的数学:

对于每个半径a,半径圆内的随机点在半径圆内的r概率api*a^2 / pi*r^2= (a/r)^2

arc4random给了我们一个均匀分布的随机数,但我们想要一个根据我们刚刚计算的分布(累积概率)有偏差的随机数。为此存在一种方法:http ://en.wikipedia.org/wiki/Inverse_transform_sampling

我们累积概率的倒数是a = sqrt(p*(r^2))(其中p在 内[0 1])。这简化为a = r * sqrt( p )。(这是你已经拥有的,恭喜!我不必要地计算了)

角度要容易得多;我们只需要在[-pi pi)or[0 pi*2)等​​内均匀分布。

float r = cRadius * sqrtf( arc4random( ) / (float) 0xFFFFFFFFul );
float angle = arc4random( ) * (float) (M_PI * 2) / (float) 0x100000000ul;

请注意,我将0xFFFFFFFFul(=2^32-1, 作为无符号长整数,以便在编译期间适合) 用于包含范围,而0x100000000ul(=2^32) 用于排除范围。这是您永远不会注意到的微小差异,但从数学上讲,这是转换分布的最正确方法。

您使用哪种随机函数取决于您,但arc4random通常在 Objective C 中推荐,因为它不需要播种(它将比以当前时间播种的分布“更随机”)。

于 2013-09-20T11:48:58.423 回答
1

最简单的方法是arc4random像其他人建议的那样使用生成两个随机数,然后将它们映射到 R 和 theta 的范围内。像:

#import <tgmath.h>
// ...
CGFloat r = ((CGFloat)arc4random())  * cRadius / ((CGFloat)UINT32_MAX);
CGFloat theta = ((CGFloat)arc4random())  * (M_PI + M_PI) / ((CGFloat)UINT32_MAX);
CGPoint randomXY = CGPointMake(cCenter.x + r * cos(theta), cCenter.y + r * sin(theta));
于 2013-09-20T11:41:03.850 回答
0

半径为 R 和中心 (x0,y0) 的圆上的点都是 (x,y),使得:

x = x0 + R*cos(θ)

y = y0 + R*sin(θ)

θ 范围从 0 到 2*PI。

如果你想要圆内的点,你想要这些点:

x = x0 + r*cos(θ)

y = y0 + r*sin(θ)

θ 范围从 0 到 2*PI,r 范围从 0 到 R。

因此,您需要做的就是在 [0,R] 中为 r 生成随机值,在 [0,2*PI] 中为 theta 生成随机值!

在目标 C 中:

-(CGPoint)getRandomPointInCircle:(CGPoint)cCenter radius:(float)cRadius {

// Since it's a 2D problem, you want to generate two different random numbers to have a
// uniform distribution. So generate a number in [0,R] and another between [0,2*PI],
// without forgetting to seed.
srand48(time(0));
float r = (float)(drand48()*cRadius);
float angle = (float)(drand48()*2*M_PI);

// Now use the equations above!
float x = cCenter.x + r * cosf(angle);
float y = cCenter.y + r * sinf(angle);


CCLOG(@"--> rand point: %f,%f", x, y);

return ccp(x,y);
}

干杯!

于 2013-09-20T11:37:13.620 回答