圓內的均勻隨機點


前言

最近遇到一個問題,需要在以一個坐標為中心的區域內生成一組均勻分布的隨機點,首先想到的就是以圓作為區域。

圓內隨機點

方法1:

根據\(x^{2}+y^{2}=R^{2}\),那么自讓想到可以先隨機生成[-R,R]間的橫坐標x,然后生成[\(-\sqrt{R^{2}-X^{2}},\sqrt{R^{2}-X^{2}}\)]范圍內的隨機數y,那么(x,y)自然也就是在圓內的隨機點了。

寫一段代碼看一看:

def random_point_in_circle(point_num, radius):
   for i in range(2,point_num+1):
       x=random.uniform(-radius,radius)
       y_max=math.sqrt(radius*radius-x*x)
       y=random.uniform(-y_max,y_max)
       plt.plot(x,y,'*',color="blue")

def main():
    pi = np.pi
    theta = np.linspace(0, pi * 2, 1000)
    R = 1
    x = np.sin(theta) * R
    y = np.cos(theta) * R

    plt.figure(figsize=(6, 6))
    plt.plot(x, y, label="cycle", color="green", linewidth=2)
    plt.title("random_points_in_circle")
    random_point_in_circle(4000, R)
    plt.legend()
    plt.show()

if __name__=="__main__":
    main()

01.png

看到這個圖應該立刻就知道哪里出錯了,當x越靠近圓的邊緣的話,y的范圍就會越小,所以兩邊邊緣的點會非常密集,不能算"均勻分布"。

方法2:

然后就會想到能否利用面積這個概念呢?因為上一個方法出錯在邊緣處,即y的范圍會隨着x的范圍的變化而發生變化,所以如果在一個矩形區域內生成隨機點,就會是均勻分布的;然后如果在圓內就保留下來這個點:

def random_point_in_circle(point_num, radius):
   for i in range(2,point_num+1):
       while True:
           x=random.uniform(-radius,radius)
           y=random.uniform(-radius,radius)
           if(x**2)+(y**2)<(radius**2):
               break
       plt.plot(x,y,'*',color="blue")

def main():
    pi = np.pi
    theta = np.linspace(0, pi * 2, 1000)
    R = 1
    x = np.sin(theta) * R
    y = np.cos(theta) * R

    plt.figure(figsize=(6, 6))
    plt.plot(x, y, label="cycle", color="green", linewidth=2)
    plt.title("random_points_in_circle")
    random_point_in_circle(4000, R)
    plt.legend()
    plt.show()

if __name__=="__main__":
    main()

效果很OK:

image.png

但是這種方法的缺點就是會有較大的開銷,想想看我們是按矩形范圍內產生的點,最后會在圓內的點的概率只有\(\frac{\pi R^{2}}{(2R)^{2}}=\frac{\pi}{4}\)

方法3:

那么我們能否考慮用極坐標呢,可以消除y的范圍對x的范圍敏感的問題。利用\(x=R*cos(\theta)​\)\(y=R*sin(\theta)​\),先隨機生成[\(0,2\pi​\)]內的\(\theta​\),然后隨機生成[0,R]內的r:

def random_point_in_circle(point_num, radius):
   for i in range(2,point_num+1):
       theta=random.random()*2*np.pi
       r=random.uniform(0,radius)
       x=r*math.cos(theta)
       y=r*math.sin(theta)
       plt.plot(x,y,'*',color="blue")

def main():
    pi = np.pi
    theta = np.linspace(0, pi * 2, 1000)
    R = 1
    x = np.sin(theta) * R
    y = np.cos(theta) * R

    plt.figure(figsize=(6, 6))
    plt.plot(x, y, label="cycle", color="green", linewidth=2)
    plt.title("random_points_in_circle")
    random_point_in_circle(4000, R)  # 修改此處來顯示不同算法的效果
    plt.legend()
    plt.show()

if __name__=="__main__":
    main()

1545459496083.png

邊緣的點會比較稀疏的原因是這樣的,由於r是在[0,R]之間等概率產生的,所以可以認為同一個r的生成的隨機點是相同的,但是圓的半徑會變大,同樣數量的點就會顯得稀疏了。

方法4:

在這里我們先引入一條定理:令\(R=r^{2}\),R在[0,1]上是均勻分布,\(\theta\)在[\(0,2\pi\)]上是均勻分布,且R與\(\theta\)相互獨立,令$$x=rcos(\theta)=\sqrt{R}cos(\theta)$$

\[y=r*sin(\theta)=\sqrt{R}*sin(\theta) \]

那么我們有(x,y)是均勻分布。

如果要證明(x,y)是均勻分布,由對稱性我們只需要證明(x,y)在第一象限為均勻分布即可,即需要證明(x,y)的聯合概率密度\(f(x,y)=\frac{1}{S}=\frac{1}{\pi /4}\)

首先我們知道連續性隨機向量變換的聯合分布的一個定理:

設(X,Y)是聯合概率密度為\(f(x,y)\)的連續性隨機向量,\(g_{1}(x,y),g_{2}(x,y)\) \(\xi=g_{1}(X,Y), \eta=g_{2}(X,Y)\)。如果對任何非負連續的二元函數\(h(\mu,\upsilon)\)成立,則有:

\[\iint h[g_{1}(x,y),g_{2}(x,y)]f(x,y)dx dy=\iint h(\mu,\upsilon)p(\mu,\upsilon)d\mu d\upsilon \]

image.png

放上代碼:

def random_point(car_num,radius):
    for i in range(1, car_num + 1):
        theta = random.random() * 2 * np.pi
        r = random.uniform(0, radius)
        x = math.cos(theta) * (r ** 0.5)
        y = math.sin(theta) * (r ** 0.5)
        plt.plot(x, y, '*', color="blue")

def main():
    pi = np.pi
    theta = np.linspace(0, pi * 2, 1000)
    R = 1
    x = np.sin(theta) * R
    y = np.cos(theta) * R

    plt.figure(figsize=(6, 6))
    plt.plot(x, y, label="cycle", color="green", linewidth=2)
    plt.title("random_points_in_circle")
    random_point(4000, R)  # 修改此處來顯示不同算法的效果
    plt.legend()
    plt.show()

if __name__=="__main__":
    main()

結果如下:

image.png

應該是比較滿意的了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM