隨機算法
by HNO2
Credit by boook, qazwsxedcrfvtg14
大綱
• 什麼是隨機算法
• 數學工具複習
• 隨機算法例子
• 隨機算法實作
什麼是隨機算法
什麼是隨機算法?
• 是一種使用隨機函數或隨機數字的演算法。
• 相對於確定性演算法:給定特定輸入,每次執行時演算法的所 有步驟都會一樣,輸出也會一樣。
為什麼、何時要用隨機
• 隨機算法通常簡單很多,
實作起來比較方便
• 有些困難的問題
(例如 NP-complete 問題)
用隨機算法可以在一定時間 和錯誤率內,得到不錯的答案
用隨機的後果
• 影響正確率 ─ Monte Carlo Algorithms
• 可能程式不總是正確
→ 讓程式錯誤的機率特別低
• 影響執行時間 ─ Las Vegas Algorithms
• 執行時間不固定
→ 讓程式期望執行的時間不長
舉個栗子
• 你有一個長度為 N 的 01 字串,其中只有一個位置是 1。你想 要知道那個 1 在字串的哪個位置。
• 兩種隨機算法:
• 隨機挑選一個位置並輸出(Monte Carlo Algorithm)
• 每次隨機挑選字串中的一個位置,如果不是 1,那就再挑一次,直到挑 對為止(Las Vegas Algorithm)
• 以上兩種作法當然都不如直接掃過整個字串,等一下會給大家看 隨機算法比較好的例子。
數學工具複習
數學工具
• 既然會用到隨機,自然和機率脫離不了關係。
• 怎麼計算機率和期望值對分析隨機算法很重要!
計算隨機正確率
計算期望值
• 期望值 = (每次可能結果 × 出現機率) 的總和
• 例如有一顆六面的骰子,上面分別寫著 1~6 點,那投一顆骰子出現點 數的期望值是 ⅙ * (1 + 2 + 3 + 4 + 5 + 6) = 3.5
• 期望值可以線性相加
• 例如一次投 10 顆骰子,他們點數總和的期望值會是 3.5 * 10 = 35
隨機算法例子
例題 – 多項式相乘的結果
• 給你兩個 N 次多項式 A(x),B(x) 和一個 2N 次多項式 C(x)
,請問 A × B = C 是否成立?
例題 – 多項式相乘的結果
• 一個簡單的想法:每次代不同的 x=k 值進去,如果 A(k)B(k)≠C(k) 就判定相乘結果錯誤。
如果帶很多個值進去都一樣就判定相乘結果正確。
• 在正確答案是 Yes 的時候不會錯,但是在正確答案是 No 的時 候可能會錯
• 如果答案是 No 的話,答錯的機率會是多少呢?
例題 – 多項式相乘的結果
• 事實上可以證明,A(x)B(x)≠C(x) 卻有 A(k)B(k) = C(k) 的 k 不會超過 2N 個!
• 證明
• 因此我們可以選定一個大範圍的數字(例如 [0, 1000N)),從 裡面隨機挑幾個數字檢查,讓錯誤率很低
• 挑一個數字錯的機率最多只有 1/500
例題 – 矩陣相乘的結果
• 給定三個大小為 N 的 N × N 矩陣 A、B、C,請問 A × B = C 是否成立?(N <= 1000)
• 直接做是 O(N3),能不能更快一點?
例題 – 矩陣相乘的結果
• 讓 R 是一個隨機的 N × 1 的矩陣,那麼只需要檢查
• ((A×B))=(C)
((A×B)×R)=(C×R) (A×(B×R))=(C×R)
• 可以證明如果 R 的值域大小是 d,那 A×B≠C 卻有 (A×(B×R))=(C×R) 的機率最多是 1/d。證明
• 每次檢查時間複雜度是 O(N2)
例題 – 成雙成對
• 給出一個 N 個數字的序列 a1, a2, …, aN。
現在有 Q 個詢問,每次詢問會給出 l, r,請問 al, …, ar 中是否每種數字都出現偶數次?
• N <= 1,000,000
• Q <= 1,000,000
例題 – 成雙成對
• XOR 好性質:
• X ⊕ Y ⊕ Z ⊕ Y ⊕ X ⊕ Z = 0
• XOR 不夠好性質:
• 1 ⊕ 2 ⊕ 3 = 0
• 也就是說,如果答案是 Yes 的話 XOR 起來一定是 0,反之不 一定成立
• 對於序列中每一種數字,隨機映射到 [0, 2k) 的一個正整數
• 對於每一筆詢問,檢查區間 XOR 是不是 0
• 每次錯誤率 1 / 2k 證明
例題 – 成堆成堆
• 給出一個 N 個數字的序列 a1, a2, …, aN。
現在有 Q 個詢問,每次詢問會給出 l, r,請問 al, …, ar 中是否每種數字都出現 K 的倍數次?
• N <= 1,000,000
• Q <= 1,000,000
例題 – 成堆成堆
• 設計一個 Hash function:加法
• 改成在模 p 底下做事情避免溢位,其中 p 是一個大質數
• 單筆詢問答錯機率: 1/p
• 證明和前面幾題很像,可以自己試試看!
1 1 1 1 1 1 1
x x -2x x x -2x x
1 1 2 1 3 1 2 2 2 3
x x y -2x z x y -2y y z
例題 – 長度為 8 的簡單路
• 給 N 點 M 邊的有向圖,並給出兩點 S 和 T
• 請找一條 S 到 T 且含頭尾恰經過 8 個點的簡單路徑
• 簡單路徑:路徑中不存在兩個相同的點
• 8 <= N <= 100, 1 <= M <= 2000
例題 – 長度為 8 的簡單路
• 也就是說不含頭尾一共 6 個點 → O(n^6)
• 問題在簡單路徑上,不然就可以?
• 為什麼不是簡單路徑就可以做
例題 – 長度為 8 的簡單路
• 考慮 dp[i][j] 代表是否有可能從 S 恰好走 j 步到 i。
• 初始條件為 dp[S][0] = True,最後檢查 dp[T][7] = True 即可。
例題 – 長度為 8 的簡單路
• 那如果現在點上面有顏色呢?
• 給 N 點 M 邊的有向圖,並給出兩點 S 和 T。
圖上除了 S 和 T 之外每個點 i 都被塗上了顏色 Ci。 請找一條 S 到 T 且含頭尾恰經過 8 個點的簡單路徑,
並在路上蒐集滿 6 種不同的顏色。
• 1 <= N <= 100, 1 <= M <= 2000, 0 <= Ci <= 5
例題 – 長度為 8 的簡單路
• 考慮 dp[i][bitmask] 代表是否有可能從 S 走到 i,且恰好 蒐集了 bitmask 這個集合的顏色。
• 初始條件為 dp[S][0] = True,最後檢查 dp[T][26-1] = True 即可。
• 時間複雜度 O(2kM),k 是顏色數量
例題 – 長度為 8 的簡單路
•
例題 – 長度為 8 的簡單路
例題 - 最近點對
• 給定 N 個二維平面上面的點,請找出距離最近的兩個點。
• 這裡的距離是指直線距離
• 1 <= N <= 105
例題 - 最近點對
d/2~d d/2~d d/2~d d/2~d d/2~d d/2~d
0~d/2 0~d/2 0~d/2d/2~d d/2~d
0~d/2 某個點 0~d/2d/2~d d/2~d
0~d/2 0~d/2 0~d/2d/2~d d/2~d d/2~d d/2~d d/2~d d/2~d
例題 - 最近點對
• 5.找到更近的最近點對:
• 更新 r = 新的最近點對 / 2
• 回到步驟三重來
例題 - 最近點對
例題 - 最近點對
Hash
• 目的:將一筆資料用一個函數壓縮起來,當要判斷兩筆資料是否 相同時,就判斷兩筆資料分別壓縮後的結果是否相同。
• 壓縮的函數不能有隨機成份
• 壓縮過程中會失去一些資訊,因此可能會有兩個不同的物品被 壓縮後產生一樣的結果。
Hash
• 如果有兩筆資料 A, B ,以及壓縮函數 f
• 錯誤的機率:
• f(A) != f(B),但 A = B。 => 不可能
• f(A) = f(B),但 A != B。 => 錯誤的機率
• 日常生活中的例子:SHA256、MD5 等。
Hash
• 譬如:
• f(x) = x % 2
• 所以我們會認為:
• 1 != 2, 因為 1 = f(1) != f(2) = 0
• 2 = 2, 因為 0 = f(2) = f(2) = 0
• 但是我們會認為:
• 4 = 2, 因為 0 = f(4) = f(2) = 0
f(A) = f(B),但 A != B
• 好的 hash function 要滿足「碰撞」
的機率足夠小,也就是以下機率要足夠低:
f(A) = f(B),但 A != B
Hash 的安全性
• 對於一個 Hash function f(x) 有三種安全性:
• Preimage resistance:
• 知道 f(x) 還原出 x
• Second preimage resistance:
• 在知道 x 和 f(x) 的情況下,找到另一組 y -> f(y) = f(x)
• Collision resistance:
• 找到一對 a, b 使得 f(a) = f(b)
• 不是所有應用都需要這三種安全性,視需求而定
Hash 的安全性 - 生日悖論
• 一個房間要多少人,則兩個人的生日相同的機率大於 50%?
• 23人
• 有 n 個人,每人都隨機地從 N 個特定的數中選擇出來一個數
。
• p(n) = 有兩個人選擇了同樣的數字。
• 這個機率有多大?
• 可以證明只需要大概 sqrt(N) 個人,有兩個人選到同一個數字 的機率就超過 50%。詳細說明
Rolling hash
• 一個簡單有效的 Hash function,又稱 RK 算法 (Rabin- Karp Algorithm/Rabin fingerprint)
• 核心
Rolling hash - 如果 C = 10
• S = 21334531
• M = 10000000000000000000000000000000000000000000
• H(1) = 2
• H(2) = 21
• H(3) = 213
• H(4) = 2133
• H(5) = 21334
• H(6) = 213345
• H(7) = 2133453
• H(8) = 21334531
Rolling hash
• 假設我們現在想要知道某一個區間的字串 [l,r] 的 Hash 值。
• 除了照定義以外有什麼好的方法嗎?
Rolling hash
• 可以把 Rolling Hash 想像成一個前高後低三角形
• H(1) = 2
• H(2) = 21
• H(3) = 213
• H(4) = 2133
• H(5) = 21334
Rolling hash
• 首先先預處理好字串每個前綴的 Rolling hash
Rolling hash
• 要刪除前面的字元
• S = 12345
• H(S) = 12345
• 12345 – 12000
= 345
Rolling hash
• 這樣之後就能直接算出任意一段的 Hash 值(參考程式碼)
• S = 21334
• H(1) = 2
• H(2) = 21
• H(3) = 213
• H(4) = 2133
• H(5) = 21334
• H(3~4)
= H(4) – H(2) * 100 = 33
隨機算法實作
如何產生隨機的數字
• 隨機算法的核心
• 大家都會(?)
• rand()
• srand(time(0))
• 這樣足夠安全嗎?
哪裡不安全
Note: 實際跑出來的值會因作業系統有所變化,在筆者筆電上跑出來的值分別是 500106.1 和 333232.2
不要直接使用 rand()
• random_shuffle() 可能使用 rand() 作為隨機數產生器。
rand() 的回傳範圍是 [0, RAND_MAX],而 RAND_MAX 只保證 最小是 32767!
• rand() 還有其他缺點,可以看這個影片
• 解決方法:
• rand() 很多次後將結果拼接成比較大的數字(不建議)
• 用其他亂數產生器,如 mt19937
• 追求更快效率 → 自己寫亂數
使用 mt19937
自己設計 random function
自己設計 random function
結束!
謝謝大家 OwO