CHƯƠNG 1 – TỔNG QUAN VỀ THUẬT TOÁN NÉN DỮ LIỆU 1. Thuật toán mã hóa bit (Naïve Bit) Thuật toán mã hóa bit sử dụng các bit trạng thái để biểu diễn dữ liệu nén. 4 bazơ đặc trưng của DNA được mã hóa bởi 2 bit (4 trạng thái). Kỹ thuật nén thẳng dữ liệu chuỗi DNA là mã hóa 4 bazơ trong một byte theo mã hóa bit.1 [2] cho thấy một ví dụ về nén mã hóa bit Hình 1.
Ví dụ mã hóa bit Mỗi kí tự ở đầu vào được thay thế bởi 2 bit sử dụng phép thay thế {A = 00, C = 01, G = 10, T = 11}. Những cấu trúc hiện tại cung cấp các phép toán bit tốt hơn, về cơ bản cho phép một mã hóa của dữ liệu chuỗi DNA với 2 bit. Mã hóa này ảnh hưởng tới khả năng đọc dữ liệu đáng kể vì cần một bảng tìm kiếm để dịch dữ liệu nén. Do biểu diễn 4 bazơ vừa đủ chính xác trong 8 bit nên nếu xảy ra thêm giá trị biên thì sẽ phá hỏng cấu trúc này.
Mã hóa sẽ trở nên phức tạp nếu thêm một hoặc nhiều phần bù ví dụ như N vào chuỗi. Một phương pháp dùng để mã hóa 5 kí tự A, C, G, T, N là đặt 3 bazơ liên tiếp vào 1 byte. 7 bit có thể mã hóa 128 trạng thái và vì 53 < 128. Tuy nhiên, việc tăng kích thước các kí tự (nhiều kí tự được thêm vào chuỗi) sẽ khiến cho việc biểu diễn kí tự trở nên khó khăn hơn.
Tỉ lệ nén của thuật toán mã hóa bit là 4:1 nếu kích thước của chuỗi kí tự đầu vào là 4 hoặc ít hơn 4:1 nếu nhiều hơn 4 kí tự [2]. Có nhiều thuật toán được xây dựng dựa trên phương thức mã hóa bit như thuật toán mã hóa trực tiếp phần khác biệt (thuật toán 2D), thuật toán này có thể xử lý các chuỗi đầu vào ở bất kỳ định dạng nào. Với 5 kí tự thông thường của 11 DNA (A, C, G, T, N), một mã hóa 7bit cho 3 kí tự liên tiếp được sử dụng. Theo cách này thì có tới 128 kí tự bổ sung sẽ được mã hóa.
Tiếp theo là Genbit compress (GBC), một công cụ nén chuỗi viết bằng ngôn ngữ java, sử dụng mã hóa độ dài (run-length encoding) thực hiện trên 2 bit (naïve 2bit) [3]. [4] cũng đưa ra một phương thức nén các nhiễm sắc thể tương đồng, mã hóa 3 bazơ sử dụng 1 byte. Tuy nhiên, trong thuật toán này kết hợp những xử lý phức tạp cho phần lặp N, sau đó nén mã hóa đạt được bằng LZ77. Một phương thức khác thuộc lớp thuật toán này được xây dựng trên cơ sở dữ liệu Oracle [5].
Và [6] kết hợp một thuật toán bổ sung cho việc tìm kiếm nhiều đoạn trong dữ liệu nén. Sau cùng là một thuật toán tập trung vào việc phân tích cách thức lưu trữ các phần lặp với những mã hóa có kích thước biến đổi, thuật toán DNABit [7]. Do tính đặc trưng của thuật toán mã hóa bit được thể hiện khá rõ nét trong 2 thuật toán mã hóa trực tiếp phần khác biệt (2D) và DNABit nên sau đây người viết luận văn sẽ trình bày chi tiết hai thuật toán này. Mã hóa trực tiếp phần khác biệt (thuật toán 2D) Với sự phát triển ngày càng mạnh về các tập dữ liệu gen khổng lồ, nhiều phương pháp nén đã và đang được phát triển để đáp ứng khối lượng lớn gen gồm nhiều chuỗi và phần bù lớn hơn (như đầu chuỗi).
Các giao thức nén phát triển riêng cho dữ liệu chuỗi thì thường có tỉ lệ nén tốt nhưng hiệu suất thấp trên tập dữ liệu lớn mà gồm nhiều dữ liệu phụ trợ (phần bù). Để so sánh thì những ứng dụng nén thông thường có thể dễ dàng nén các tệp dữ liệu lớn không đồng nhất nhưng lại bị hạn chế đối với dải dữ liệu kí tự trong dữ liệu chuỗi. Bởi vậy, thuật toán 2D được thiết kế để cung cấp một giao thức nén chuỗi nucleotit thông thường. Giao thức này có thể phân biệt dữ liệu chuỗi và dữ liệu phần bù, từ đó đưa ra sự điều chỉnh phù hợp giữa nén dữ liệu chung chung và cụ thể.
Thuật toán 2D có những mục tiêu như sau [43]: Thời gian thực hiện tuyến tính cho việc hỗ trợ các tập dữ liệu lớn: cả hai quá trình nén và giải nén đều phải hỗ trợ thực hiện đối với độ phức tạp thời gian thực hiện O(n). Hỗ trợ bao gồm cả những kí tự phụ mà không phải thành phần của tập bazơ nucleotit mong đợi: các kí tự bổ sung có thể được sử dụng để biểu diễn thông tin tự do, dữ liệu chú thích hoặc các chuỗi con đặc biệt như miền chức năng hoặc các chuỗi lặp đặc biệt. Mã hóa trực tiếp pha đơn: Pha nén yêu cầu chỉ một chiều đơn mà không có pha loại bỏ những thông tin dư thừa và không lưu trữ dữ liệu vào các tệp 12 cấu trúc phụ hoặc trung gian tạm thời. Tương tự, việc không lưu trữ dữ liệu phụ phải cho phép khôi phục một chiều đơn đối với pha giải nén.
Nén không mất dữ liệu: Chuỗi gốc phải được khôi phục hoàn toàn sau quá trình giải nén. Việc này có thể được thực hiện chỉ dựa trên chuỗi thẳng mà không quan tâm tới định dạng hay bị ngắt dòng, hoặc dựa trên bố cục từng dòng của dữ liệu chuỗi gốc. Không phân biệt loại chuỗi: Nén và giải nén không ưu tiên hay phân biệt chuỗi là DNA hay mRNA. Giải nén chuỗi polipeptit (mỗi peptit gồm 10 tới 100 amino axit): Có thể lựa chọn khôi phục chuỗi nén nucleotit trực tiếp tới một chuỗi polipeptit dựa trên khung đọc xác định.
Sử dụng được cùng với phương pháp nén khác: Một chuỗi mã hóa 2D có thể nén được bằng những ứng dụng nén khác để đưa ra khả năng nén chuỗi gốc trong tương lai. (a) Mô hình Để cho thời gian thực hiện tuyến tính, 2D sử dụng một mô hình tĩnh cho việc mã hóa dữ liệu chuỗi cùng với bất kỳ thông tin nào mà có thể được bao gồm trong đầu vào. 2D cho rằng DNA gồm {A, C, G, T} và mRNA gồm {A, C, G, U}. Đồng nhất hai tập trên, tập kí tự cho mô hình 2D gồm {A, C, G, T, U}.
Việc này giúp khai báo rõ ràng loại chuỗi. Trong trường hợp kí tự không phải nucleotit, 2D hỗ trợ tập giá trị ASCII truyền thống gồm 0 tới 127. Để hoàn thành nén, 2D cần biểu diễn nhiều bazơ sử dụng một byte đơn như khung 2-bit-mỗi-bazơ. 2D sử dụng mã hóa trực tiếp trên một bộ ba (3 bazơ nucleotit liên tiếp) vì những lý do sau.
Đầu tiên, việc này cho phép 3 bazơ nucleotit hợp lại trong một byte đơn mà không phải là nhiều byte. Thứ hai, bằng việc nén bộ ba (thay vì bộ hai) thì các kí tự không mong muốn có thể được mã hóa trực tiếp. Do đó giúp bỏ được pha loại các kí tự thừa và lưu trữ dữ liệu thừa trong cấu trúc thứ cấp. Điều này có lợi cho cả thời gian nén và giải nén.
Sau cùng, biểu diễn theo bộ ba giúp 2D giải nén các chuỗi polipeptit bằng cách biên dịch bộ ba như các codon (chuỗi liên kết 3 nucleotit của DNA hoặc RNA). (b) Mã hóa Ở mức thấp nhất, 2D sử dụng một byte được gán có dải giá trị từ -128 tới 127. Về mặt khái niệm, 7 bit của mỗi byte được sử dụng cho mã hóa và bít quan trọng nhất được sử dụng như một cờ nén. Khung nén này được mô tả ở Hình 1.
Ít nhất 7 bit được sử dụng để mã hóa dữ liệu. Bit quan trọng nhất được sử dụng như cờ để cho biết ngữ cảnh của byte là dữ liệu nén hay giải nén. Các kí tự được chuyển thành các bộ ba liên tiếp nếu mỗi thành phần là một bazơ nucleotit hợp lệ. Một bộ ba hợp lệ được gán một giá trị đơn trong dải từ 1 tới 125 và cờ nén là một tập hợp ngang hàng với giá trị gán trong khoảng -1 và - 125.
2D sẽ phân biệt dữ liệu chuỗi và các kí tự khác, nếu một giá trị không mong muốn xảy ra thì nó có thể được biên dịch như một giá trị ASCII trong dải từ 0 tới 127, sau đó giá trị này sẽ được lưu nguyên bản và không được gán cờ nén. Trong trường hợp xảy ra giá trị không mong muốn thì các thành phần khác của bộ ba hiện tại vẫn được mã hóa và giải nén độc lập dù có bazơ nucleotit hay không, việc này để duy trì khung đọc hiện tại cho việc hỗ trợ biên dịch một polipeptit chính xác. Mặc định là sự thực hiện có thể giả sử một khung đọc mong muốn bắt đầu cùng với phần bắt đầu của chuỗi. Tuy nhiên, nhiều khung đọc cũng được hỗ trợ dễ dàng bằng việc mã hóa một hoặc hai kí tự đầu tiên khi dữ liệu chưa được nén và sau đó mới bắt đầu thực hiện 2D.
Sau cùng, trong trường hợp kí tự không-biết, 2D biểu diễn nó bằng việc lưu ở dạng chưa nén với giá trị byte được gán nhỏ nhất -128. (c) Thuật toán Đoạn mã giả sau đây mô tả lõi của thuật toán nén 2D, nhận một chuỗi đầu vào và trả về mã hóa 2D dưới dạng mảng byte. begin byte list = new List char triplet = new Array int baseCount = 0 int nonCompressCount = 0 foreach character c in input string if nonCompressCount = 0 then if c is a nucleotide base then triplet at position baseCount = c baseCount = baseCount + 1 14 if baseCount = 3 then convert triplet to byte b and add b to list reset triplet baseCount = 0 else foreach character t in triplet convert t to byte b and add b to list endfor convert c to byte b and add b to list reset triplet nonCompressCount = 2 - baseCount baseCount = 0 else convert c to byte b and add b to list nonCompressCount = nonCompressCount - 1 endfor return list as byte Array end Dữ liệu giải mã được khôi phục theo dòng với độ dài chia hết cho 3. Ví dụ, nếu chuỗi trong tệp nguồn được chia thành dòng, mỗi dòng 70 kí tự thì chuỗi trong tệp khôi phục sẽ có độ dài dòng là 69, 69, 72, 69, 69, 72….
Việc này thực hiện để tăng tính nén toàn bộ mà vẫn duy trì được khả năng đọc. Tuy nhiên, nếu yêu cầu thì có thể thực hiện được phiên bản đọc từng dòng chính xác nhưng sẽ bị giảm tính nén toàn bộ.