(Randomized Algorithm)
在實際談論隨機演算法前,我們要知道如何產生亂數
你可能學過如下圖最基本的亂數產生
//給予亂數種子
srand(time(NULL));
//生成亂數
rand()%x
電腦的隨機並不是真的隨機!
他的原理是從一個決定好的亂數種子產生亂數
他可以是電腦的記憶體、執行時間等等
最常見的亂數種子
1. time(NULL)
2. chrono::steady_clock::now().time_since_epoch().count()
那有了亂數種子之後,我們要生成亂數
最好的亂數產生,我們會希望數字的機率是均勻分布的
也就是每個數字的出現機率是相等的
以 C++ 的亂數為例
rand() 並不是一個均勻分布的隨機函數
他所產生的數字也只會在 [1,RAND_MAX] 之間
(RAND_MAX 是 32767)
//使用 time(NULL) 作為隨機種子
mt19937 rng(time(NULL);
//使用高精度時間函數作為隨機種子
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
他能夠在 \(p\) 的機率找到正確答案
例如 \(p=0.5\) 等等的數字
這個機率看似不高,但是否表示無法成為正確的算法呢
有個題目問你是否能夠構造出某個解
你確定你有 \(20\%\) 的機率隨機產生成功的答案
機率看似不高,不過真的是這樣嗎?
\(20\%\) 雖然感覺很低
不過如果我們重複嘗試同樣的事情呢
5 次都失敗的機率 => \(0.8^5 = 0.327\)
10 次都失敗的機率 => \(0.8^{10} = 0.107\)
20 次都失敗的機率 => \(0.8^{20} = 0.011\)
30 次都失敗的機率 => \(0.8^{30} = 0.001\)
也就是跑 30 次時,有 \(99.9\%\) 的機率找到正確的答案!
你有一個 \(n\) 項的陣列,與 \(q\) 次詢問
每次詢問問你在 \([l,r]\) 區間內的絕對眾數
*絕對眾數: 當一個數字 \(x\) 出現次數 \( \ge \lceil\frac n 2\rceil\) ,即為絕對眾數
如果有絕對眾數,戳一個數字有 \(50\%\) 的機率得到
因此多跑幾次就可以了
接著再檢查次數是否 \(> \lceil \frac n 2 \rceil\)
(Hash)
這種方式比較不一樣
我們會給定一種東西叫做 Hash
將某個輸入經過某個黑盒子
得到另外一種東西
之後再利用這個東西去比對答案
\(abcde\)
\(p = 31, m = 10^9+7\)
生日攻擊
23 人 => \(50\%\) 兩個同一天生日
Hash \(m = 10^9+7\)
\(10^5\) 個字串
可能會碰撞!
第一個 hash => \(m =10^9+7\)
第二個 => \(m=998244353\)
範例
今天給你一個陣列
有 \(q\) 個詢問
每次詢問一個區間內是否每個數字都出現偶數次
範例
今天給你一個陣列
有 \(q\) 個詢問
每次詢問一個區間內是否每個數字都出現偶數次
\([1,2,3]\) XOR 會錯
\(5, 9, 10\)
暴力 => \(O(qC)\)
作法
隨機賦予這些數字不同數字
然後去看整個區間 XOR 是不是 0
使用前綴 XOR 去檢查
多跑幾次這方式就會 AC 了
範例
今天給你一個陣列
有 \(q\) 個詢問
每次詢問一個區間內是否每個數字都出現三的倍數次
[1,1,2,2,2,1,3,3,3]
第一次出現的 x => x
第二次 => x
第三次 => -2x