leetcode478. Generate Random Point in a Circle

題目要求

Given the radius and x-y positions of the center of a circle, write a function randPoint which generates a uniform random point in the circle.

Note:

1. input and output values are in floating-point.
2. radius and x-y position of the center of the circle is passed into the class constructor.
3. a point on the circumference of the circle is considered to be in the circle.
4. randPoint returns a size 2 array containing x-position and y-position of the random point, in that order.

Example 1:
Input: 
["Solution","randPoint","randPoint","randPoint"]
[[1,0,0],[],[],[]]
Output: [null,[-0.72939,-0.65505],[-0.78502,-0.28626],[-0.83119,-0.19803]]

Example 2:
Input: 
["Solution","randPoint","randPoint","randPoint"]
[[10,5,-7.5],[],[],[]]
Output: [null,[11.52438,-8.33273],[2.46992,-16.21705],[11.13430,-12.42337]]

Explanation of Input Syntax:

The input is two lists: the subroutines called and their arguments. Solution's constructor has three arguments, the radius, x-position of the center, and y-position of the center of the circle. randPoint has no arguments. Arguments are always wrapped with a list, even if there aren't any.

假設現在已知圓的圓心的x和y座標,以及該圓的半徑radius。要求寫一個隨機點生成器,要求該生成器生成的點必須在圓內,且每一個點被生成的概率爲相等的。規定圓周上的點也屬於圓內。

思路1:Rejection Sampling

該思路很簡單,即取能夠容下該圓的最小正方形,並且隨機生成該正方形內的點。如果點不在圓內,則繼續重新生成。正方形內等概率的隨機點很好生成,可以直接利用JAVA內置的隨機數生成器即可。x座標的隨機數範圍爲[x-radius, x+radius], y座標的隨機數範圍爲[y-radius, y+radius]。代碼如下:

    public double[] randPoint2() {
        double x0 = x_center - radius;
        double y0 = y_center - radius;
        while(true) {
            double xg = x0 + Math.random() * radius * 2;
            double yg = y0 + Math.random() * radius * 2;
            if (Math.pow((xg - x_center) , 2) + Math.pow((yg - y_center), 2) <= radius * radius)
                return new double[]{xg, yg};
        }
    }

思路二:極座標

假如我們能夠利用極座標的思想,先從(0, 360]度之間生成一個隨機的角度,再在[0, radius]之間生成一個隨機的長度,理論上就可以了。但是通過這種方式生成的隨機點會明顯的在靠近圓心的位置密度更大,如下圖所示(圖片來源於網絡):

clipboard.png

究其原因在於,從圓心向圓周方向的半徑擴展出去,單位長度所構成的圓環的面積並非相等的。如下圖(圖片來源於網絡):

clipboard.png

假設將圓周拆分爲等分的3部分,則最內圈的面積爲clipboard.png,中圈的面積爲clipboard.png=3A,同理外圈的面積爲5A。如果按照半徑上的每一個點都是等概率生成的話,會發現內圈因爲面積更小,導致點的密度更大。但是由此也可以看出,第i圈的面積和單位面積成線性正相關。

clipboard.png

接着要先介紹兩個概率論中的概念:概率密度函數(probability density function 簡稱pdf)累計分佈函數(Cumulative Distribution Function 簡稱 cdf)

概率密度函數是指某個隨機數生成器生成的數字在某個點附近的概率值。比如現在有一個隨機數生成器能夠等概率生成[a,b]區間的任意一個數,則其pdf(x)= 1/(b-a)

累計分佈函數對離散變量而言,所有小於等於a的值出現概率的和。還是以[a,b]區間的等概率隨機數生成器爲例,cdf(x)表示隨機數生成器生成的數位於[a,x]區間內的概率,其值爲cdf(x)=(x-a)/(b-a) a<=x<=b

可以看到cdf其實是對pdf在該區間上進行微積分計算的結果。

從這題的角度而言,既然已知隨着r向着半徑增加,在該位置上生成隨機數的概率爲線性增加,因此可以設clipboard.png,其中a爲概率值。因爲已知生成的所有點的必定位於[0,R]之上,因此cdf(R)=clipboard.png,由此可以得出clipboard.png。再將a的值帶回原式中,可以得出clipboard.png。在對pdf(r)進行積分計算可以得出clipboard.png。再次校驗可以看到cdf(R)=1確實成立。

但是,JAVA只能提供一種通用的隨機數生成器,即在一定區間內每一個數均爲等概率出現。對於這種隨機數生成器,其cdf(x)=x,即生成[0,x]之間的隨機數的概率爲r。將二者相等即可得出如下結論clipboard.png

代碼如下:

    public double[] randPoint() {
        double len= Math.sqrt(Math.random())*radius;
        double deg= Math.random()*2*Math.PI;
        double x= x_center+len*Math.cos(deg);
        double y= y_center+len*Math.sin(deg);
        return new double[]{x,y};
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章