1. 程式人生 > >[LeetCode] Generate Random Point in a Circle 生成圓中的隨機點

[LeetCode] 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 - a) ^ 2 + (y - b) ^ 2 = r ^ 2,這裡的 (a, b) 是圓心位置,r為半徑。那麼我們想如何生成圓中的任意位置呢,如果用這種方式來生成,那麼我們先隨機出一個x,那麼隨機出y的時候還要考慮其是否在圓中間,比較麻煩。繼續回到高中時代,模糊的記憶中飄來了三個字,極座標。是的,圓還可以用極座標的形式來表示,我們只需隨機出一個角度theta,再隨機出一個小於半徑的長度,這樣我們就可以得到圓中的座標位置了,哦耶~ 那麼先來生成theta吧,我們知道一圈是360度,即2pi,所以我們隨機出一個 [0, 1] 中的小數,再乘以2pi,就可以了。然後就是隨機小於半徑的長度,這裡有個問題需要注意一下,我們並不是直接隨機出一個 [0, 1] 中的小數再乘以半徑r,而是要對隨機出的[0, 1] 中的小數取個平方根再乘以半徑r。這是為啥呢,簡單來說,是為了保證等概率。如果不用平方根的話,那麼表示圓的時候 (len * cos(theta)) ^ 2 + (len * sin(theta) ^ 2,這裡就相當於對隨機出的[0, 1] 中的小數平方了,那麼其就不是等概率的了,因為兩個小於1的小數相乘了,其會更加靠近0,這就是為啥我們要平方一下的原因。最後在求點位置的時候要加上圓心的偏移即可,參見程式碼如下:

解法一:

class Solution {
public:
    Solution(double radius, double x_center, double y_center) {
        r = radius; centerX = x_center; centerY = y_center;
    }
    
    vector<double> randPoint() {
        double theta = 2 * M_PI * ((double)rand() / RAND_MAX);
        double len = sqrt((double)rand() / RAND_MAX) * r;
        return {centerX + len * cos(theta), centerY + len * sin(theta)};
    }

private:
    double r, centerX, centerY;
};

我們也可以不用極座標來做,由於之前剛做過Implement Rand10() Using Rand7(),對其中的拒絕取樣Rejection Sampling還有印象,所以我們也可以用其來做。這其實就是拒絕取樣的經典應用,在一個正方形中有均勻分佈的點,讓我們隨機出其內切圓中的一個點,那麼就是我們隨機出x和y之後,然後算其平方和,如果小於等於r平方,說明其在圓內,我們可以返回其座標,記得加上圓心偏移,否則我們重新進行取樣。關於拒絕取樣的方法可以參見我之前那篇部落格Implement Rand10() Using Rand7(),參見程式碼如下:

解法二:

class Solution {
public:
    Solution(double radius, double x_center, double y_center) {
        r = radius; centerX = x_center; centerY = y_center;
    }
    
    vector<double> randPoint() {
        while (true) {
            double x = (2 * (double)rand() / RAND_MAX - 1.0) * r;
            double y = (2 * (double)rand() / RAND_MAX - 1.0) * r;
            if (x * x + y * y <= r * r) return {centerX + x, centerY + y};
        }
    }

private:
    double r, centerX, centerY;
};

類似題目:

參考資料: