Go, Gopher!

2018/04/11

Go, Gopher! là một bài toán không khó, nhưng khá thú vị ở mặt problem setting. Nội dung của nó như sau:

Bài toán đặt ra là làm sao để tạo được một hình chữ nhật hoàn chỉnh mà trong đó tất cả các ô đều có trạng thái bật, và có diện tích lớn hơn hoặc bằng $ A $ với ít hơn $ m $ lần deploy.


Lời giải

Chọn $ \lceil \frac{A}{9} \rceil $ blocks 3x3 liên tiếp nhau và lần lượt deploy gopher vào các ô trung tâm của những blocks đó. Như vậy, ta sẽ luôn tạo được một hình chữ nhật hoàn chỉnh với diện tích lớn hơn hoặc bằng $ A $. Ví dụ, với $ A = 200 $ như test case lớn của đề bài, các ô trung tâm cần để chọn fill là: (2, 2), (5, 2), (8, 2), …, (68, 2).

Câu hỏi đặt ra là: Thời gian kỳ vọng để có thể bật được hết một block trống là bao nhiêu lần deploy?

Bài toán con: Trong bọc kẹo có $ n $ loại kẹo khác nhau, mỗi loại có vô hạn viên. An mỗi lần được lấy đúng 1 viên kẹo, và xác xuất để An lấy được mỗi loại kẹo là $ \frac{1}{n} $. Số lần kỳ vọng để An có thể lấy đủ n loại kẹo là bao nhiêu?

Gọi $ X $ là số lần An phải bốc để có đủ n loại kẹo, và gọi $ X_i $ là số kẹo mà An cần phải lấy để có loại kẹo mới, sau khi đã có đủ $ i-1 $ loại kẹo. Dễ thấy $ X_i $ sẽ là một biến ngẫu nhiên hình học với $ p_i = \frac{n - (i-1)}{n} $. Vậy nên $ E(X_i) = \frac{1}{p_i} $ và theo tính tuyến tính của hàm kỳ vọng, ta có:

$$ E(X) = \sum E(X_{i}) = n (1 + \frac{1}{2} + \frac{1}{3} + ... + \frac{1}{n}) \approx n ln(n) $$

$$ Var(X) = \sum _{i=1}^{n} Var(X_{i}) \leq \sum _{i=1}^{n} E^2(X_{i}) = \sum _{i=1}^{n} \frac{1}{p^2_{i}} = n^2 \sum _{i=1}^{n} \frac{1}{i^2} \leq n^2 \sum _{i=1}^{\infty} \frac{1}{i^2} = \frac{n^2 \pi^2}{6} $$

Với $ n = 9 $, ta có $ E(X) = n ln(n) = 9 ln(9) \approx 20 $, $ std(X) = \sqrt{\frac{9^2 \pi^2}{6}} \approx 11.54 $ và với giá trị $ A = 200 $, $ m = 1000 $ trong test case lớn, số blocks cần phải deploy là $ \lceil \frac{200}{9} \rceil = 23 $. Vậy tổng số lần deploy sẽ vào khoảng $ 23 * 20 \approx 460 < m = 1000 $.


Chebyshev's denial hay là Dùng dao mổ trâu giết gà

Nhưng liệu deploy $ n ln(n) $ lần có thật sự đã đủ? Phân tích về giá trị kỳ vọng chỉ cho ta chặn dưới của số lần phải deploy, nhưng không đảm bảo rằng sau đúng $ n ln(n) $ lần thì block sẽ được filled. Một câu hỏi khá tự nhiên được đặt ra: Với bao nhiêu lần deploy thì xác suất để một block được fill là nhiều hơn 80%? Nhiều hơn 90%? Nhiều hơn 99%?

Nếu ta deploy liên tục $ n ln(n) + cn $ lần, với $ c $ là tham số, thì xác suất $ X \geq nln(n) + cn $ sẽ bé hơn xác suất $ |X - nln(n)| \geq cn $. Áp dụng bất đẳng thức Chebyshev, ta được:

$$ P(|X - nln(n)| \geq cn) = P(|X - nln(n)| \geq \frac{c \sqrt{6}}{\pi} \sqrt{\frac{n^2 \pi^2}{6}}) \leq \frac{\pi^2}{6c^2} $$

Nếu $ c = 3 $, số lần deploys khoảng $ 9 ln(9) + 3 * 9 = 46 $, xác suất một block được filled chỉ là 82%. Trong khi đó, số lần cần phải deploy cho cả bảng là $ 46 * 23 > 1000 $. Như vậy, có hai khả năng xảy ra:

  1. Thuật toán được mô tả bên trên chưa đủ tốt, còn có chỗ phải cải tiến
  2. Cái chặn có được từ bất đẳng thức Chebyshev chưa thật sự đủ chặt

Giả thuyết 1. sớm bị bác bỏ sau khi tác giả nhắm mắt submit code và AC :) Câu hỏi còn lại là: Làm sao để tìm được một cái chặn tốt hơn cho giải thuật này?


Sức mạnh của union bound

Để tìm được cái chặn tốt hơn thực ra rất đơn giản. Nghĩ khác đi một tí:

Nếu ta chọn $ k = nln(n) + cn $ thì xác suất để một ô chưa được filled sau $ k $ lần deploys là bé hơn $ n e^{- \frac{k}{n}} = n e^{-ln(n) - c} < \frac{1}{e^c} $.

Nếu chọn $ c = 3 $ như lúc nãy thì xác suất thành công sau k lần deploys lúc này đã lớn hơn 95%. Hay như ở đây còn có cái chặn $ 1 - e^{-e^{-c}} $ tốt hơn nữa.


Bài học

c số lần deploys cho 1 block xác suất thành công tổng số lần deploys
2.00000 37.77502 0.87342 868.82549
2.20000 39.57502 0.89511 910.22549
2.40000 41.37502 0.91328 951.62549
2.60000 43.17502 0.92842 993.02549
2.80000 44.97502 0.94100 1034.42549
3.00000 46.77502 0.95143 1075.82549
3.20000 48.57502 0.96006 1117.22549
3.40000 50.37502 0.96718 1158.62549
3.60000 52.17502 0.97305 1200.02549
3.80000 53.97502 0.97788 1241.42549
4.00000 55.77502 0.98185 1282.82549

Đây chỉ là worst-case scenario. Thí nghiệm của Google ở đây cho thấy bạn không thực sự cần nhiều hơn 850 lần deploys. Như “kỳ vọng”, số lần phải deploys trung bình vào khoảng 450-460.