Tác giả:
Trong thế giới Tin học, Hệ nhị phân (Binary) đóng một vai trò rất quan trọng. Tương tự với trong CP khi Binary cùng với tư tưởng của nó đã tạo ra rất nhiều thuật toán khác nhau và phổ biến như Binary Search, Binary Tree, Binary Lifting,
Trong bài viết này, chúng ta sẽ tìm hiểu về Hệ tam phân (Ternary) cùng với những thuật toán được tạo ra từ tư tưởng của nó.
Trước khi đọc, các bạn cần phải biết về Hệ tam phân là gì.
Mặc dù đều là tìm kiếm nhưng khái niệm này với Binary Search là hoàn toàn khác nhau.
Ternary Search thường được sử dụng để tìm cực trị của một hàm số chứa chính xác một cực trị. Khi đó, sẽ được chia ra làm trường hợp:
Để ta có thể hình dung về Ternary Search, ta có thể xem qua bài toán sau:
Xét vị trí và trong đoạn với . Ta sẽ có trường hợp sau:
Dễ dàng thấy rằng và sẽ hoàn toàn ngược lại nhau. Nếu không xét trường hợp giữa, ta có kết luận như sau:
Để chọn và một cách tối ưu, ta có thể đặt và đặt . Dễ dàng thấy sau mỗi lần chặt, không gian tìm kiểm sẽ giảm đi .
Nếu ta lặp lại việc tìm kiếm trên lần thì không gian của đoạn sẽ chỉ bằng ban đầu. Dựa vào đó, dễ dàng thấy độ lớn của tỉ lệ thuận với độ chính xác của thuật toán.
Độ phức tạp của ý tưởng trên là . Dưới đây là code tham khảo.
Cũng như Binary Tree, mỗi node của Ternary Tree sẽ chứa node con. Dưới đây là hình ảnh minh họa một Ternary Tree với node.
Đề dễ hình dung hơn về Ternary Tree, ta có thể xem qua bài toán RMQ và sử dụng Segment Tree để giải. Nếu không nhớ RMQ là gì thì bài toán đấy như sau:
Để giải bài toán trên, ta chỉ đơn giản là sử dụng thuật toán Segment Tree bằng cách cài đặt Ternary Tree thay Binary Tree như thông thường. Cụ thể hơn, nếu node cha lưu trữ của đoạn thì node con của nó sẽ lần lượt lưu trữ của đoạn , , . Khi đó, số node của cây sẽ không vượt quá với độ cao không quá . Dựa vào đó, ta dễ dàng thấy rằng ta sẽ truy cập một node của cây trong .
Độ phức tạp của ý tưởng trên là . Dưới đây là code tham khảo.
Cài đặt Lazy Update và giải các bài toán Range Queries khác sẽ tương tự như trên.
Tư tưởng cũng sẽ như Binary Lifting, ta sẽ định nghĩa là một cái gì đấy thứ của . Tuy nhiên, ta cần phải lưu ý một chỗ rằng Ternary có tất cả chữ số là .
Đề dễ hình dung hơn về Ternary Lifting, ta có thể xem qua lại bài toán RMQ như trên. Khi cài đặt bằng Binary Lifting, ta còn được biết đến với cái tên là Sparse Table.
Ta sẽ định nghĩa là phần tử nhỏ nhất của đoạn . Dễ dàng thấy đoạn đó chứa đoạn con nhỏ hơn là:
Từ điều trên, ta có thể tính bằng cách lấy của cái trên. Và tất nhiên, với mà dữ liệu cung cấp.
Với mỗi truy vấn , ta chỉ việc phân tích thành dãy tam phân và xét trường hợp chữ số là với .
Độ phức tạp của ý tưởng trên là . Dưới đây là code tham khảo.
Cài đặt LCA và các bài toán có thể giải bằng Binary Lifting khác sẽ tương tự như trên.
Qua các thuật toán trên, dễ dàng thấy Ternary sẽ có độ phức tạp chủ yếu là . Trong khi đó, Binary lại có độ phức tạp chủ yếu là . Vậy cái nào nhanh hơn?
Ta có một chút biến đổi như sau: và .
So sánh với sẽ tương đương với so sánh và . Khi lấy tỉ lệ giữa phân số này, ta sẽ có . Từ đó suy ra và .
Để rõ hơn, dưới đây là đồ thị của hàm số và .
Qua đó, ta có thể thấy rằng với . Ta cũng suy ra được rằng Binary vẫn sẽ là lựa chọn tối ưu.
Đây là một bài viết được viết với mục đích giải trí. Tuy nhiên, nó có lẽ sẽ khai sáng đầu óc và bộ não của bạn bởi Ternary thực chất không quá phổ biến trong CP.
Hy vọng bài viết này giúp ích cho bạn một phần nào. Chúc các bạn một ngày vui vẻ.