算法:计算椭圆内的伪随机点

时间:2011-04-03 10:55:46

标签: algorithm math geometry

对于我正在制作的简单粒子系统,我需要给出一个宽度和高度的椭圆,计算一个位于该椭圆中的随机点X,Y。

现在我不是数学方面最好的,所以我想在这里询问是否有人能指出我正确的方向。

也许正确的方法是在宽度范围内选择随机浮点数,将其取为X并从中计算Y值?

4 个答案:

答案 0 :(得分:23)

  1. 在半径为1的圆内生成一个随机点。这可以通过在区间phi中采用随机角度[0, 2*pi)和随机值rho来实现。区间[0, 1)和计算

    x = sqrt(rho) * cos(phi)
    y = sqrt(rho) * sin(phi)
    

    公式中的平方根确保了圆内的均匀分布。

  2. xy缩放到椭圆的尺寸

    x = x * width/2.0
    y = y * height/2.0
    

答案 1 :(得分:12)

使用rejection sampling:在椭圆周围的矩形中选择一个随机点。通过检查(x-x0)^ 2 / a ^ 2 +(y-y0)^ 2 / b ^ 2-1的符号来测试该点是否在椭圆内。如果该点不在内部,请重复此操作。 (这假设椭圆与坐标轴对齐。类似的解决方案在一般情况下有效,但当然更复杂。)

答案 2 :(得分:1)

您可以使用极坐标进行笛卡尔坐标转换:

x = cos(angle) * radius
y = sin(angle) * radius

稍作修改

x = cos(angle) * width
y = sin(angle) * height

您没有指定语言,但这是使用Processing的快速演示:

float ellipseWidth = 150,ellipseHeight = 100;
float angle,radius,x,y;
void setup(){
  size(400,400);
  smooth();
  noStroke();
  ellipseMode(CENTER);
  background(0);
  fill(255);
}

void draw(){
  //choose a random angle on the ellipse
  angle = random(TWO_PI);
  //convert from polar to cartesian, using both width and height as radii
  x = cos(angle) * ellipseWidth;
  y = sin(angle) * random(ellipseHeight);//random 'lengths' vertically
  //draw
  translate(200,200);//move to centre
  ellipse(x,y,5,5);  
}

您可以看到它运行here

points in ellipse

HTH

答案 3 :(得分:1)

通过仔细考虑极性形式的定义,可以在不使用拒绝采样的情况下生成椭圆内的点。从wikipedia开始,椭圆的极坐标由

给出

Polar radius of ellipse

直观地说,我们应该在半径越大的地方更频繁地采样极角θ。在数学上更多,我们的随机变量θ的PDF应该是p(θ)dθ= dA / A,其中dA是角度θ与宽度dθ的单个区段的面积。使用极角区域方程dA = 1/2 r 2 dθ,椭圆区域为πab,则PDF变为

Theta PDF

要从此PDF中随机抽样,一种直接方法是inverse CDF technique。这需要计算累积密度函数(CDF),然后反转此函数。使用Wolfram Alpha获得不定积分,然后反转它得到

的逆CDF

Inverse CDF

其中u在0和1之间运行。因此,为了对随机角度θ进行采样,您只需在0和1之间生成一个均匀的随机数u,并将其替换为逆CDF的等式。

要获得随机半径,可以使用适用于圆的相同技术(例如,参见Generate a random point within a circle (uniformly))。

以下是一些实现此算法的示例Python代码:

import numpy
import matplotlib.pyplot as plt
import random

# Returns theta in [-pi/2, 3pi/2]
def generate_theta(a, b):
    u = random.random() / 4.0
    theta = numpy.arctan(b/a * numpy.tan(2*numpy.pi*u))

    v = random.random()
    if v < 0.25:
        return theta
    elif v < 0.5:
        return numpy.pi - theta
    elif v < 0.75:
        return numpy.pi + theta
    else:
        return -theta

def radius(a, b, theta):
    return a * b / numpy.sqrt((b*numpy.cos(theta))**2 + (a*numpy.sin(theta))**2)

def random_point(a, b):
    random_theta = generate_theta(a, b)
    max_radius = radius(a, b, random_theta)
    random_radius = max_radius * numpy.sqrt(random.random())

    return numpy.array([
        random_radius * numpy.cos(random_theta),
        random_radius * numpy.sin(random_theta)
    ])

a = 2
b = 1

points = numpy.array([random_point(a, b) for _ in range(2000)])

plt.scatter(points[:,0], points[:,1])
plt.show()

Randomly generated points within ellipse with axes 2 and 1