隨機算法
by boook
Hash Credit by qazwsxedcrfvtg14
大綱
• When
• 為甚麼、何時要用隨機
• How
• 如何產生隨機的數字
• Hash
• 隨機選擇
• Algorithm
• 最近點對
為甚麼、何時要用隨機
• 模板很難寫
• 大家都喜歡唬爛
• AC 的效率 vs 出題者的良心
• 在題目限制很寬的時候
• 當出題者的題目出壞掉的時候
用隨機的後果
• 影響正確率
• 可能程式不總是正確
• -> 讓程式錯誤的機率特別低
• 影響執行時間
• 執行時間不固定
• -> 讓程式期望執行的時間不長
如何產生隨機的數字
• 大家都會??
• rand()
• srand(time(0))
• 這樣足夠安全嗎?
哪裡不安全
• 大家都會??
• rand()
• srand(time(0))
• 這樣足夠安全嗎?
哪裡不安全
不要直接使用 rand()
• rand() 在 windows 上預設的回傳範圍為 [0, 65535]
• 解決方法:
• rand() 很多次後將結果拼接成比較大的數字
• 用其他亂數產生器,如 mt19937
• 追求更快效率 -> 自己寫亂數
使用 mt19937
自己設計 random function
自己設計 random function
暖身-計算隨機正確率
Hash
Hash
• 目的:
• 將一個物品的很多資訊壓縮起來,當要判斷兩個物品是否相同時 就判斷兩個物品分別壓縮後的結果是否相同。
• 壓縮過程中會失去一些資訊,因此可能會有兩個不同的物品被 壓縮後產生一樣的結果。
• 如果有兩個物品 A, B ,以及壓縮函數 f
• 錯誤的機率:
• f(A) != f(B),但 A = B。 => 不可能
• f(A) = f(B),但 A != B。 => 錯誤的機率
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 的機率。
• P(f(A) = f(B),但 A != B) < 1e-9
• 當錯誤的機率「足夠小」,那麼就可以
「假裝」hash function 不會碰撞
• 有時候 1e-9 仍然不夠,需視情況調整
• 生活上的例子 (checksum)
Hash table
• 可以構造讓 hash table 變得非常慢
,但在隨機的 使用下,
insert、
delete 都是 O(1)。
Hash 的安全性
• 對於一個 hash function x -> f(x) 有三種等級的安全性:
• Preimage attack:
• 知道 f(x) 還原出 x
• Second preimage resistance:
• 在知道 f(x) 的情況下,找到另一組 y -> f(y) = f(x)
• collision resistance:
• 找到一對 a, b 使得 f(a) = f(b)
Hash 的安全性 - 生日悖論
• 一個房間要多少人,則兩個人的生日相同的機率大於 50%?
• 23人
• 有 n 個人,每人都隨機地從 N 個特定的數中選擇出來一個數。
• p(n) = 有兩個人選擇了同樣的數字。
• 這個機率有多大?
Rolling hash
• 又稱 RK 算法(Robin-Karp Algorithm)
• 核心
• 也可以用來當作亂數產生器
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
• 假設你有一個字串,且你已經知道其hash值
• 下列哪些操作可以O(1)執行?
• 加一個字元在前面
• 加一個字元在後面
• 從前面刪除一個字元
• 從後面刪除一個字元
Rolling hash
• 可以把Rolling hash想像成一個前高後低三角形
• H(1) = 2
• H(2) = 21
• H(3) = 213
• H(4) = 2133
• H(5) = 21334
Rolling hash
• 兩個hash接起來
• 一個字元也可以 算是一個hash
*𝐶
𝑏• S = 213
• P = 123
• H(S) = 213
• H(P) = 123
• H(S+P)
= 213 * 1000 + 123
Rolling hash
• 刪除前面的字元
*𝐶
𝑏• S = 12345
• H(S) = 12345
• 12345 – 12000
= 345
Rolling hash
• 刪除後面的字元
*𝐶
−𝑏模逆元
• S = 12345
• H(S) = 12345
• (12345 – 45) / 100
= 123
• 8 / 2 != 2 / 2 (mod 6)
Rolling hash
• 因為這些性質
• 我們經常會預處理好字串每個前綴的Rolling hash
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
例題 - 怎麼訂 hash 值
• 給一個矩陣大小為 N * N
• Q 次詢問兩個大小相同的子矩陣是否相同。
• N <= 5000
• Q <= 500000
• hash 可不可以推廣到二維?
二維 hash
例題 – 怎麼訂 hash 值
• 假設兩個字串相似的定義如下
• 如果存在一個字元的置換方式,使得兩個字串相等,則這兩個字串相似
• 例:
• AAA=BBB
• XYX=ABA
• ABA!=AAA
• 問1:給你字串集 A 和字串 B ,A 中有多少字串和 B 相似?
• 問2:給你一個字串 C,Q 次問兩段區間是否相似?
• Sum(|Ai|), |A|, |B| < 10^6, C, Q <= 10^6
例題 – 矩陣相成的結果
• 給定三個大小為 N 的 N × N 矩陣 A、B、C,請問 A × B = C 是否成立?
• N <= 1000
• 直接做是 O(N^3)
• 能不能更快一點?
例題 – 矩陣相成的結果
• 讓 R 是一個 N × 1 的矩陣,那麼只需要檢查
• ((A×B))=(C)
• ((A×B)×R)=(C×R)=C′
• (A×(B×R))=(C×R)=C′
• 乘法個過程中與字串 hash 做的 事情相同
例題 – 成雙成對
• 給出一個 N 個數字的序列 a_1, a_2, …, a_n。現在 有 Q 個詢問,每次詢問會給出 l, r,請問如果考慮 a_l, …, a_r 是否每種數字都出現偶數次?
• N <= 1,000,000
• Q <= 1,000,000
例題 – 成雙成對
• XOR 好性質:
• X ⊕ Y ⊕ Z ⊕ Y ⊕ X ⊕ Z = 0
• XOR 不夠好性質:
• 1 ⊕ 2 ⊕ 3 = 0
• One time pad:一次性密碼本
• N 個 ∈ [0, 2^k) 的隨機數字 XOR 起來的結果是隨機的
• 隨機將 a_i 映射成一個隨機的大數字
• 每次錯誤率 1 / (2^k),讓 k = 60
例題 – 成堆成堆
• 給出一個 N 個數字的序列 a_1, a_2, …, a_n。現在 有 Q 個詢問,每次詢問會給出 l, r,請問如果考慮 a_l, …, a_r 是否每種數字都出現 K 的倍數次?
• N <= 1,000,000
• Q <= 1,000,000
例題 – 成堆成堆
• 設計一個 hash function
• XOR 可以 加法也可以
• 只要滿足:
• 答案是 YES 的時候一定判斷得出來
• 答案是 NO 的時候有很低的機率會錯
例題 – 成堆成堆
• 設計一個 hash function:加法
• 改成在模 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 個點的簡單路徑
• 簡單路徑:路徑中不存在兩個相同的點
• 1 <= N <= 100, 1 <= M <= 2000
例題 – 長度為 8 的簡單路
• 也就是說不含頭尾一共 6 個點 => O(n^6)
• 問題在簡單路徑上,不然就可以?
• 為什麼不是簡單路徑就可以做
例題 – 長度為 8 的簡單路
• 也就是說不含頭尾一共 6 個點 => O(n^6)
• 問題在簡單路徑上,不然就可以?
• 為什麼不是簡單路徑就可以做
• Dp[point][step] = True or false
例題 – 長度為 8 的簡單路
• 那如果現在點上面有顏色呢
• 給 N 點 M 邊的有向圖,並給出兩點 S 和 T。
圖上除了 S 和 T 之外每個點 i 都被塗上了顏色 C_i。
請找一條 S 到 T 且含頭尾恰經過 8 個點的簡單路徑,
並在路上蒐集滿 6 種不同的顏色
• 1 <= N <= 100, 1 <= M <= 2000, 0 <= C_i <= 5
例題 – 長度為 8 的簡單路
• 給 N 點 M 邊的有向圖,並給出兩點 S 和 T。
圖上除了 S 和 T 之外每個點 i 都被塗上了顏色 C_i。
請找一條 S 到 T 且含頭尾恰經過 8 個點的簡單路徑,
並在路上蒐集滿 6 種不同的顏色
• 1 <= N <= 100, 1 <= M <= 2000, 0 <= C_i <= 5
• Dp[point][bit_mask] = True or False
• O(point × bit_mask) × O(轉移) = O(2^6 M)
例題 – 長度為 8 的簡單路
• 如果點有顏色多好 => 自己上色
• 隨機把每個點塗上 C_i = 𝑥 ∈ 0, 5
• 至少多少機率會答對:
• 如果只有一條符合條件的路徑
• 中間六個點的顏色都要不一樣: 6!
• 總方法數量: 66
• 隨機一次會答錯的機率: 1 − 6!
66
例題 – 長度為 8 的簡單路
隨機選擇
• How:
• 字面上的意思
• 隨機選擇一條路 => 估計答對的機率
隨機選擇 - 例題
• 給定 N 個二為平面上面的點,請問有沒有一條通過至少 k % 的直線?
• 2 <= N <= 2 × 106
• k >= 20
直觀的想法
• 直觀的想法
• 1. 隨機兩個點 𝑎, 𝑏
• 2. 檢查有多少個點在 𝑎, 𝑏 兩點構成的直線上面
• 3. 做很多次
• 重複隨機選擇 𝑎, 𝑏 兩點 50 次,錯誤率
1 − 20% × 20% 50 ≈ 12.98%
隨機少一點「維度」
• 1. 隨機一個點 𝑎
• 2. 以 𝑎 點作為圓心的極角進行排序
• 3. 做很多次
• 重複隨機選擇 𝑎 點 40 次,錯誤率
1 − 20% 40 ≈ 0.013%
錯排數 (Derangement)
• 錯排數 DN :滿足對於所有的1 ≤ 𝑖 ≤ 𝑁 ,𝐴𝑖 ≠ 𝑖 這樣的 permutation
• DN/ N! ≈ 1/𝑒 ≈ 0.367879
• 也就是說有大概 1/3 的機率隨機一個 permutation 可以隨機 到一個 Derangement。
隨機選擇 - 例題 CF330E
• 給出一張 𝑁 個點 M 條邊的無向圖,並且圖上保證每個點最多 只接了兩條邊,請構造出一張新圖滿足以下條件,若無解則輸出
− 1 :
• 新圖上與原圖有相同數量的點和相同數量的邊。
• 圖上保證每個點最多只接了兩條邊。
• 對於圖上兩個點 𝑢, 𝑣 ,若在原圖上這兩點之間存在一條邊,則在新圖上 這兩點之間必定沒有邊。
1 ≤ 𝑀 ≤ 𝑁 ≤ 105
隨便做?
• 把圖的編號隨機打亂 => 做完了!
• 估計錯誤率 => 想想看什麼樣的測資容易錯
• 題意裡面的圖為很多環和很多鍊
• 加一條邊 = 加上一個限制 (很多環)
• 很多連通塊會導致條件被獨立分開
• 圖是一個很大的環
• 拓展錯排數:滿足對任意的相鄰的兩數字差不是 1 或 N – 1 條件的排列 p_1, p_2, ..., p_n
性質
• 滿足條件的排列 / N! = 𝑒−2
• (1−𝑒−2)90 ≈ 2.07×10−6
• 可以寫個簡單的程式估計一下:滿足條件的排列 / N!
最近點對
最近點對
• 給定 N 個二維平面上面的點,請找出距離最近的兩個點。
• 1 <= N <= 10^5
• 傳說做法?旋轉排序
• 1. 把 N 個點平移到第一象限
• 2. 把 N 個點的順序隨機打亂,d = dis(𝑎1, 𝑎2)
• 3. 以 𝑟 = 𝑑/2 的大小將二維平面切成網格狀,並將這些網格也 以座標表示:點 (𝑥, 𝑦) 會落入座標為 (𝑥
𝑟 , 𝑦
𝑟)
• 不會有兩個點在同一個格子內
最近點對
• 4. 一直把點加進網格中
• 如果產生新的最近點對一定會出現在以某個點為中心的 5 × 5 宮格內
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/2 d/2~d d/2~d 0~d/2
某個點
0~d/2 d/2~d d/2~d 0~d/2 0~d/2 0~d/2 d/2~d d/2~d d/2~d d/2~d d/2~d d/2~d最近點對
• 5.找到更近的最近點對:
• 更新 r = 新的最近點對 / 2
• 回到步驟三重來 O(i+1)
最近點對
• 期望複雜度
• 令 𝑑𝑖 為考慮 𝑎1, 𝑎2, … , 𝑎𝑖 這些點的最近點對距離。
• 當加入點 𝑎𝑖+1 時,出現一個 𝑎𝑗 使得 (𝑎𝑗, 𝑎𝑖+1) 是新的最近點對的機 率為:
• 𝑃 𝑑𝑖𝑠 𝑎𝑗, 𝑎𝑖+1 < 𝑑𝑖 = 𝑖
𝐶2𝑖+1 = 𝑖×2
𝑖×(𝑖+1) = 2
𝑖+1
• 出現一個 𝑎𝑗 要付出的時間代價: O 𝑖 + 1
• 每加入一個點考慮期望複雜度為 機率 × O(代價) = 2
𝑖 + 1 × 𝑖 + 1 = 2 = 𝑂(1)
• 總共要加 N 個點 => O(N)