隨機取點
這兩天做了LeetCode上面關於Random的系列問題,雖然問題不多,但是能提供解決隨機問題的經典思路——按權重採樣和拒絕採樣。我們這裏只是討論關於隨機去點的基本問題,後面沒時間的話,可能不會深究。
Java Random API
首先,當然是最經典的Math.random(); 返回[0,1)的一個double值,區間內任意一個點被取到的概率都是等可能的。
double x = Math.random();
int i = (int)(Math.random() * 2); // 取0或者1,兩者是等可能的。
int j = (int)(Math.random() * 10); // {0, 1, 2, 3,...,9}是等可能的。
之後,是random.nextInt(int range); 返回{0, 1, ..., range-1}中任意一個數,每一個點被取到的概率是等可能的,也就是1/range。
Random rand = new Random();
int x = rand.nextInt(10); // 返回{0, 1, 2, ..., 9}中的任意數,每個數被取到的概率是1/10.
基本上,有這兩個API就足夠了。
按權重採樣
相關的經典題目是LeetCode的按權重採樣和非重疊矩形中的隨機點。大概意思是,你現在有一個均勻分佈,如Math.random()或者nextInt,你需要把它按照權重分配一個數字出現的概率。比方說下面這幅圖,首先使用rand.nextInt(10)生成一個隨機數,再利用大小的比較返回不同的取值,這樣選到1的概率就是2/10, 2的概率是6/10,3的概率是2/10。
相關代碼如下,
public class MyRandom{
Random rand = new Random();
public int getInt() {
int x = rand.nextInt(10);
if (x >= 0 && x < 2) return 1;
if (x >= 2 && x < 8) return 2;
if (x >= 8 && x < 10) return 3;
}
}
LeetCode裏面第528題,按照權重隨機取樣就是這個原理,但是不同的是,還需要結合二分查找法節省時間。
拒絕採樣
拒絕採樣的原理是非常簡單的,比方說,下面這個圖形,在整個正方形中隨機取點,如果點落在圓外,就拒絕這個點。那麼對於圓中各點來說,取到的概率是等可能的。給我們的思路是構造一個等可能的點序列,或者面,拒絕一些點就可以得到我們想要的概率。這樣講比較抽象,建議大家做一下這兩道題目: 用 Rand7() 實現 Rand10()和在圓內隨機生成點。拒絕採樣是比較簡單通用的解法,所以我比較推薦。