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:
- input and output values are in floating-point.
- radius and x-y position of the center of the circle is passed into the class constructor.
- a point on the circumference of the circle is considered to be in the circle.
randPointreturns 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; };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/478
類似題目:
Implement Rand10() Using Rand7()
參考資料:
https://leetcode.com/problems/generate-random-point-in-a-circle/
