Tìm kiếm một cài đặt thực hiện với dấu chân bộ nhớ nhỏ


9

Tôi đang tìm kiếm để thực hiện các kiểu dữ liệu thiết lập. Đó là, chúng ta phải

  • duy trì tập con động (có kích thước ) từ vũ trụ U = \ {0, 1, 2, 3, \ dot, u - 1 \} có kích thước u vớiSnU={0,1,2,3,,u1}u
  • các hoạt động insert(x)(thêm một phần tử xvào S ) và find(x)(kiểm tra xem phần tử có phải xlà thành viên của S ) hay không.

Tôi không quan tâm đến các hoạt động khác. Để định hướng, trong các ứng dụng tôi đang làm việc với chúng tôi có u1010 .

Tôi biết các triển khai cung cấp cả hai hoạt động trong thời gian O(1) , vì vậy tôi lo lắng chủ yếu về kích thước của cấu trúc dữ liệu. Tôi mong đợi hàng tỷ mục nhưng muốn tránh hoán đổi càng nhiều càng tốt.

Tôi sẵn sàng hy sinh thời gian chạy nếu cần thiết. Thời gian khấu hao của O(logn) là những gì tôi có thể thừa nhận; thời gian chạy dự kiến ​​hoặc thời gian chạy trong ω(logn) không được chấp nhận.

Một ý tưởng tôi có là nếu S có thể được biểu diễn dưới dạng kết hợp các phạm vi [xmin, xmax], thì chúng ta sẽ có thể tiết kiệm kích thước lưu trữ với giá giảm một số hiệu suất. Ngoài ra, một số mẫu dữ liệu khác là có thể, như [0, 2, 4, 6].

Bạn có thể vui lòng chỉ cho tôi các cấu trúc dữ liệu có thể làm điều gì đó như thế không?



Làm thế nào để số của các yếu tố vào hình ảnh? Tức là, điều gì xảy ra nếu một phần tử được chèn và đã có ? nnn
vonbrand

@vonbrand - nkích thước của tập S. Nó có thể tăng với mọi inserthoặc nó có thể giữ nguyên nếu phần tử xđã có trong tập hợp.
HEKTO

3
Bạn có thể chấp nhận một xác suất nhỏ của dương tính giả? Nếu vậy, một bộ lọc nở có thể là lý tưởng: en.wikipedia.org/wiki/Bloom_filter
Joe

1
@AlekseyYakovlev, tỷ lệ dương tính giả của bộ lọc nở không liên quan gì đến kích thước vũ trụ (chỉ với số lượng hàm băm , kích thước của cấu trúc dữ liệu và số lượng vật phẩm ), nhưng nếu thực sự là gần (nói cho một hằng số nhỏ ), bạn sẽ khó ép để làm tốt hơn so với một vector chút đơn giản, tôi nghĩ rằng, với chỉ tổng bit của không gian. m n n u u = n c c c n nkmnnuu=ncccn
Joe

Câu trả lời:


8

Câu trả lời của Joe cực kỳ tốt và cung cấp cho bạn tất cả các từ khóa quan trọng.

Bạn nên lưu ý rằng nghiên cứu cấu trúc dữ liệu cô đọng vẫn còn ở giai đoạn đầu và nhiều kết quả chủ yếu là lý thuyết. Nhiều cấu trúc dữ liệu được đề xuất khá phức tạp để thực hiện, nhưng hầu hết sự phức tạp là do thực tế là bạn cần duy trì độ phức tạp tiệm cận cả về kích thước vũ trụ và số lượng phần tử được lưu trữ. Nếu một trong hai thứ này tương đối ổn định, thì rất nhiều sự phức tạp sẽ biến mất.

Nếu bộ sưu tập là bán tĩnh (nghĩa là, chèn rất hiếm hoặc ít nhất là âm lượng thấp), thì chắc chắn nên xem xét cấu trúc dữ liệu tĩnh dễ thực hiện (sdarray của Sadakane là một lựa chọn tốt) kết hợp với cập nhật bộ nhớ cache. Về cơ bản, bạn ghi lại các cập nhật trong cấu trúc dữ liệu truyền thống (ví dụ: B-tree, trie, bảng băm) và cập nhật hàng loạt cấu trúc dữ liệu "chính". Đây là một kỹ thuật rất phổ biến trong truy xuất thông tin, vì các chỉ mục đảo ngược có nhiều lợi thế để tìm kiếm nhưng khó cập nhật tại chỗ. Nếu đây là trường hợp, xin vui lòng cho tôi biết trong một nhận xét và tôi sẽ sửa đổi câu trả lời này để cung cấp cho bạn một số gợi ý.

Nếu chèn thường xuyên hơn, thì tôi đề nghị băm ngắn gọn. Ý tưởng cơ bản đủ đơn giản để giải thích ở đây, vì vậy tôi sẽ làm như vậy.

Vì vậy, kết quả lý thuyết thông tin cơ bản là nếu bạn lưu trữ phần tử từ vũ trụ của các mục và không có thông tin nào khác (ví dụ: không có mối tương quan giữa các phần tử) thì bạn cần bit để lưu trữ nó. (Tất cả các logarit là cơ sở-2 trừ khi có quy định khác.) Bạn cần nhiều bit này. Không có cách nào xung quanh nó.unulog(un)+O(1)

Bây giờ một số thuật ngữ:

  • Nếu bạn có cấu trúc dữ liệu có thể lưu trữ dữ liệu và hỗ trợ các hoạt động của bạn trong bit, chúng tôi gọi đây là cấu trúc dữ liệu ẩn .log(un)+O(1)
  • Nếu bạn có cấu trúc dữ liệu có thể lưu trữ dữ liệu và hỗ trợ các hoạt động của bạn trong bit của không gian, chúng tôi gọi đây là cấu trúc dữ liệu nhỏ gọn . Lưu ý rằng trong thực tế điều này có nghĩa là tổng chi phí tương đối (so với mức tối thiểu theo lý thuyết) nằm trong một hằng số. Nó có thể là 5% trên không, hoặc 10% trên không, hoặc 10 lần trên không.log(un)+O(log(un))=(1+O(1))log(un)
  • Nếu bạn có cấu trúc dữ liệu có thể lưu trữ dữ liệu và hỗ trợ các hoạt động của bạn trong bit của không gian, chúng tôi gọi đây là cấu trúc dữ liệu cô đọng .log(un)+o(log(un))=(1+o(1))log(un)

Sự khác biệt giữa cô đọng và nhỏ gọn là sự khác biệt giữa little-oh và big-oh. Bỏ qua những thứ có giá trị tuyệt đối trong giây lát ...

  • g(n)=O(f(n)) có nghĩa là tồn tại hằng số và một số sao cho tất cả , .cn0n>n0g(n)<cf(n)
  • g(n)=o(f(n)) có nghĩa là với tất cả các hằng số tồn tại một số sao cho tất cả , .cn0n>n0g(n)<cf(n)

Một cách không chính thức, big-oh và little-oh đều là "trong một yếu tố không đổi", nhưng với big-oh, hằng số được chọn cho bạn (bởi nhà thiết kế thuật toán, nhà sản xuất CPU, định luật vật lý hoặc bất cứ điều gì), nhưng với rất ít -oh bạn chọn hằng số cho mình và nó có thể nhỏ như bạn muốn . Nói cách khác, với các cấu trúc dữ liệu cô đọng, chi phí tương đối sẽ nhỏ tùy ý khi kích thước của vấn đề tăng lên.

Tất nhiên, quy mô của vấn đề có thể phải lớn để nhận ra chi phí tương đối mà bạn muốn, nhưng bạn không thể có mọi thứ.

OK, với điều đó dưới vành đai của chúng tôi, hãy đặt một số con số cho vấn đề. Giả sử các khóa là số nguyên -bit (vì vậy kích thước vũ trụ là ) và chúng tôi muốn lưu trữ số nguyên này. Giả sử rằng chúng ta có thể sắp xếp một cách kỳ diệu một bảng băm lý tưởng hóa với đầy đủ công suất và không lãng phí, do đó chúng ta cần chính xác các khe băm .n2n2m2m

Một thao tác tra cứu sẽ băm khóa -bit, che đi các bit để tìm các vị trí băm, sau đó kiểm tra xem giá trị trong bảng có khớp với khóa không. Càng xa càng tốt.nm

Một bảng băm như vậy sử dụng bit . Chúng ta có thể làm tốt hơn thế này không?n2m

Giả sử rằng hàm băm là không thể đảo ngược. Sau đó, chúng tôi không phải lưu trữ toàn bộ khóa trong mỗi khe băm. Vị trí của vị trí băm cung cấp cho bạn bit của giá trị băm, vì vậy nếu bạn chỉ lưu trữ các bit còn lại của , bạn có thể xây dựng lại khóa từ hai mẩu thông tin đó (vị trí của vị trí băm và giá trị được lưu trữ ở đó). Vì vậy, bạn sẽ chỉ cần bit lưu trữ.hmnm(nm)2m

Nếu nhỏ so với , phép tính gần đúng của Stirling và một số học nhỏ (bằng chứng là một bài tập!) Cho thấy:2m2n

(nm)2m=log(2n2m)+o(log(2n2m))

Vì vậy, cấu trúc dữ liệu này là ngắn gọn.

Tuy nhiên, có hai cách bắt.

Cái bắt đầu tiên là xây dựng các hàm băm khả nghịch "tốt". May mắn thay, điều này là dễ dàng hơn nhiều so với vẻ ngoài của nó; Các nhà mật mã tạo ra các chức năng không thể đảo ngược mọi lúc, chỉ họ gọi chúng là "cyphers". Ví dụ, bạn có thể dựa vào hàm băm trên mạng Feistel, đây là cách đơn giản để xây dựng hàm băm không thể đảo ngược từ các hàm băm không thể đảo ngược.

Điều hấp dẫn thứ hai là các bảng băm thực sự không lý tưởng, nhờ nghịch lý Sinh nhật. Vì vậy, bạn muốn sử dụng một loại bảng băm tinh vi hơn, giúp bạn gần gũi hơn với công suất đầy đủ mà không bị đổ. Băm cuckoo là hoàn hảo cho điều này, vì nó cho phép bạn tùy ý gần với lý tưởng trong lý thuyết, và khá gần trong thực tế.

Băm cuckoo yêu cầu nhiều hàm băm và nó yêu cầu các giá trị trong các vị trí băm được gắn thẻ với hàm băm nào được sử dụng. Vì vậy, nếu bạn sử dụng bốn hàm băm, ví dụ, bạn cần lưu trữ thêm hai bit trong mỗi khe băm. Điều này vẫn ngắn gọn khi phát triển, vì vậy nó không phải là vấn đề trong thực tế và vẫn còn lưu trữ toàn bộ chìa khóa.m

Ồ, bạn cũng có thể muốn nhìn vào cây van Emde Boas.

THÊM

Nếu ở đâu đó xung quanh , thì xấp xỉ , vì vậy (một lần nữa) giả sử rằng không có mối tương quan nào nữa giữa các giá trị, về cơ bản bạn không thể thực hiện bất kỳ tốt hơn một vectơ bit. Bạn sẽ lưu ý rằng giải pháp băm ở trên thực sự làm suy giảm hiệu quả trường hợp đó (cuối cùng bạn lưu trữ một bit trên mỗi khe băm), nhưng rẻ hơn là chỉ sử dụng khóa làm địa chỉ thay vì sử dụng hàm băm.nu2log(un)u

Nếu rất gần với , tất cả các tài liệu cấu trúc dữ liệu cô đọng đều khuyên bạn nên đảo ngược ý nghĩa của từ điển. Lưu trữ các giá trị không xảy ra trong tập hợp. Tuy nhiên, bây giờ bạn thực sự phải hỗ trợ thao tác xóa và để duy trì hành vi cô đọng, bạn cũng cần có khả năng thu nhỏ cấu trúc dữ liệu khi nhiều phần tử được "thêm". Mở rộng bảng băm là một hoạt động được hiểu rõ, nhưng ký hợp đồng thì không.nu


Xin chào, như đoạn thứ hai trong câu trả lời của bạn - Tôi hy vọng rằng mọi cuộc gọi đến insertsẽ được kèm theo một cuộc gọi đến findvới cùng một đối số. Vì vậy, nếu findtrả về true, sau đó chúng ta chỉ cần bỏ qua insert. Vì vậy, tần suất của các findcuộc gọi nhiều hơn tần suất của các insertcuộc gọi, cũng như khi ntrở nên gần gũi u, thì insertcác cuộc gọi trở nên rất hiếm.
HEKTO

Nhưng bạn mong đợi để có được gần cuối cùng? un
Bút danh

Trong thế giới thực n đang phát triển cho đến khi nó đạt đến u, tuy nhiên chúng ta không thể dự đoán nó sẽ xảy ra hay không. Cấu trúc dữ liệu sẽ hoạt động tốt cho mọi ngườin <= u
HEKTO

Đúng. Sau đó, thật công bằng khi nói rằng chúng ta không biết về một cấu trúc dữ liệu duy nhất ngắn gọn (theo nghĩa trên) và đạt được điều này trên toàn bộ phạm vi của . Tôi nghĩ rằng bạn sẽ muốn có cấu trúc dữ liệu thưa thớt khi , sau đó chuyển sang cấu trúc dữ liệu dày đặc (ví dụ: vectơ bit) khi ở xung quanh , sau đó cấu trúc dữ liệu thưa thớt với đảo ngược cảm giác khi gần với . nun<unu2nu
Bút danh

5

Có vẻ như bạn muốn một cấu trúc dữ liệu cô đọng cho vấn đề thành viên động .

Hãy nhớ lại rằng cấu trúc dữ liệu cô đọng là một cấu trúc mà yêu cầu không gian "gần" với giới hạn dưới lý thuyết thông tin, nhưng không giống như cấu trúc dữ liệu nén, vẫn cho phép truy vấn hiệu quả.

Các vấn đề thành viên là chính xác những gì bạn mô tả trong câu hỏi của bạn:

duy trì tập hợp con (có kích thước ) từ vũ trụ có kích thước với các thao tác:SnU={0,1,2,3,,u1}u

  • find(x)(kiểm tra xem phần tử có phải xlà thành viên của ).S
  • insert(x)(thêm một phần tử xvào )S
  • delete(x)(xóa một phần tử xkhỏi )S

Nếu chỉ findhoạt động được hỗ trợ, thì đây là vấn đề thành viên tĩnh . Nếu một trong hai inserthoặc deleteđược hỗ trợ, nhưng không phải cả hai, nó được gọi là bán động và nếu cả ba thao tác được hỗ trợ, thì đó được gọi là vấn đề thành viên động .

Về mặt kỹ thuật, tôi nghĩ bạn chỉ yêu cầu cấu trúc dữ liệu cho vấn đề thành viên bán động, nhưng tôi không biết bất kỳ cấu trúc dữ liệu nào tận dụng ràng buộc này và cũng đáp ứng các yêu cầu khác của bạn. Tuy nhiên, tôi có tài liệu tham khảo sau:

Trong Định lý 5.1 của bài viết Tư cách thành viên trong thời gian không đổi và không gian gần như tối thiểu , Brodnik và Munro cho kết quả như sau:

Tồn tại một cấu trúc dữ liệu yêu cầu các bit hỗ trợ các tìm kiếm trong thời gian không đổi và chèn và xóa trong thời gian khấu hao dự kiến ​​không đổi.O(B)

Trong đó là số bit tối thiểu theo lý thuyết thông tin cần có.B=log(un)

Ý tưởng cơ bản là họ đệ quy phân chia vũ trụ thành các phạm vi có kích thước được lựa chọn cẩn thận, do đó, điều này thậm chí nghe có vẻ như các kỹ thuật có thể nằm dọc theo đường mà bạn đang nghĩ đến.

Tuy nhiên, nếu bạn đang tìm kiếm thứ gì đó mà bạn thực sự có thể thực hiện, tôi không biết liệu đây có phải là lựa chọn tốt nhất của bạn không. Tôi chỉ đọc lướt qua bài báo, và cố gắng giải thích các chi tiết là vượt quá phạm vi của câu trả lời này. Họ tham số hóa giải pháp của họ, sử dụng các chiến lược khác nhau tùy thuộc vào kích thước tương đối của và . Và phiên bản động của cấu trúc dữ liệu chỉ được phác thảo trong bài báo.un


1
Bản tóm tắt giấy Brodnik & Munro không nói gì về chèn. Nhưng kết quả của họ là những gì chúng ta có thể mong đợi, phải không? Nếu n = u/2, thì không gian cần thiết là tối đa.
HEKTO

@AlekseyYakovlev Họ không thực sự đề cập đến trường hợp động trong bản tóm tắt, nhưng định lý liên quan đến trường hợp động được trích dẫn trong câu trả lời của tôi (từ phần 5).
Joe
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.