Khởi tạo mảng trong thời gian không đổi được khấu hao - thủ thuật này được gọi là gì?


13

Có cấu trúc dữ liệu này giao dịch hiệu năng của truy cập mảng so với nhu cầu lặp lại khi xóa nó. Bạn giữ một bộ đếm thế hệ với mỗi mục, và cũng là bộ đếm thế hệ toàn cầu. Hoạt động "rõ ràng" làm tăng bộ đếm thế hệ. Trên mỗi truy cập, bạn so sánh các bộ đếm thế hệ địa phương và toàn cầu; nếu chúng khác nhau, giá trị được coi là "sạch".

Điều này đã xuất hiện trong câu trả lời này trên Stack Overflow gần đây, nhưng tôi không nhớ liệu thủ thuật này có tên chính thức hay không. Phải không?

Một trường hợp sử dụng là thuật toán của Dijkstra nếu chỉ một tập hợp con nhỏ của các nút phải được nới lỏng và nếu điều này phải được thực hiện nhiều lần.


2
Thủ thuật thú vị, nhưng nó có một chi phí khá cao. Vì vậy, tôi tự hỏi sử dụng nào đã xóa mảng như một hoạt động phổ biến mà giá phải trả? (Câu hỏi chân thành!)
Joachim Sauer

@JoachimSauer: Đã chỉnh sửa.
krlmlr

Âm thanh rất đắt trong trường hợp chung cho cả việc sử dụng bộ nhớ và chi phí truy cập. Trường hợp sử dụng cho kỹ thuật này phải rất cụ thể.
Martin York

3
@Joachim: Nó được sử dụng để xóa nhanh bộ đệm để kết xuất - đại khái. Họ chỉ có "bit rõ ràng" trên 64kb hoặc somesuch như thế.
DeadMG

3
@ user946850 "khấu hao" có nghĩa là bạn có thể chứng minh rằng một hoạt động đắt tiền hiếm khi xảy ra trong bức tranh tổng thể rằng nó không đóng góp nhiều hơn, ví dụ: O (1)

Câu trả lời:


2

Cách tiếp cận đã nói ở trên đòi hỏi mỗi ô có thể chứa một số lượng đủ lớn để giữ số lần mà mảng có thể cần phải được khởi tạo lại, đó là một hình phạt không gian đáng kể. Nếu một vị trí có khả năng giữ ít nhất một giá trị sẽ không bao giờ được viết một cách hợp pháp, người ta có thể tránh có bất kỳ hình phạt không gian (không cố định) nào khác với chi phí thêm O(Wlg(N))hình phạt thời gian, trong đó Wsố lượng vị trí mảng khác biệt được viết giữa hoạt động xóa và Nlà kích thước của mảng. Ví dụ: giả sử một người sẽ lưu trữ các số nguyên từ -2,147,483,647 đến 2,147,483,647 (nhưng không bao giờ -2,147,483,648) và người ta muốn các mục mảng trống đọc là 0. Bắt đầu bằng cách điền vào mảng với -2,147,483,648 (gọi giá trị đóB). Khi đọc một mảng mảng cho ứng dụng, hãy báo cáo giá trị Bbằng 0. Trước khi viết khe mảng I, kiểm tra xem nó diễn ra Bvà nếu như vậy và Ilớn hơn một, lưu trữ một số không để khe I/4sau khi thực hiện một kiểm tra tương tự cho vị trí đó (và nếu nó tổ chức B, I/16, vv).

Để xóa mảng, bắt đầu Ibằng 0 hoặc 1, tùy thuộc vào cơ sở mảng (thuật toán như mô tả sẽ hoạt động cho một trong hai). Sau đó lặp lại quy trình sau: Nếu mục IB, tăng Ivà nếu làm như vậy sẽ mang lại bội số của bốn, chia cho bốn (chấm dứt nếu phép chia mang lại giá trị 1); nếu mục Inày không B, lưu trữ Bở đó và nhân Ivới bốn (nếu Ibắt đầu từ 0, nhân với bốn sẽ để lại 0, nhưng vì mục 0 sẽ trống, Isẽ được tăng lên).

Lưu ý rằng người ta có thể thay thế "bốn" hằng số ở trên bằng các số khác, với các giá trị lớn hơn thường yêu cầu gắn thẻ công việc ít hơn, nhưng các giá trị nhỏ hơn thường yêu cầu xóa công việc ít hơn; vì các vị trí mảng được gắn thẻ phải được xóa, giá trị ba hoặc bốn gần như chắc chắn là tối ưu; vì giá trị bốn chắc chắn gần với tối ưu, tốt hơn hai hoặc tám và thuận tiện hơn bất kỳ số nào khác, nó có vẻ là sự lựa chọn hợp lý nhất.


Nó là đủ để có một bộ đếm phiên bản có khả năng chứa đủ bộ đặt lại liên tiếp trước khi tất cả các ô được cập nhật với các giá trị mới. Trong thực tế, một byte có thể là đủ, hoặc thậm chí ít hơn trong các vòng lặp chặt chẽ hơn.
9000

@ 9000: Mã dựa trên hành vi như vậy có thể dễ bị hỏng, đặc biệt là lý do duy nhất để sử dụng cách tiếp cận 'giả rõ ràng' như vậy (trái ngược với việc xóa mảng) sẽ là nếu tập hợp các mục cần thiết được xóa thường nhỏ và thay đổi - một cặp điều kiện có âm mưu làm tăng khả năng một vật phẩm có thể được sử dụng, "bị xóa", và sau đó được sử dụng trong một thời gian dài tùy ý. Người ta có thể xem xét việc quét mảng và xóa vật lý bất kỳ vị trí cũ nào khi bộ đếm sắp hoàn thành, nhưng ...
supercat

1
... Nếu giá trị bọc của bộ đếm không đổi, khối lượng công việc trung bình cho mỗi hoạt động xóa mảng sẽ là O (N), với N là kích thước của mảng. Không phải là một điều như vậy có thể không hữu ích trong thực tế, vì việc triển khai O (N) được tăng lên theo hệ số 65.536 vẫn sẽ là O (N), nhưng cũng nhanh hơn 65.536 lần so với việc không cải thiện . Ngẫu nhiên, các trường hợp các cách tiếp cận này sẽ hữu ích cũng có thể được hưởng lợi từ việc sử dụng cấu trúc dữ liệu mảng thưa, có thể sử dụng không gian O (AlgN) để giữ một mảng có một mảng có kích thước N với các phần tử không trống.
supercat

1

Tôi sẽ gọi nó là "tái cấu trúc tế bào mảng lười biếng", nhưng dường như nó không có bất kỳ tên nào được thiết lập (nghĩa là tên đang được sử dụng rộng rãi).

Thuật toán là thông minh, nhưng rất chuyên ngành và áp dụng trong một khu vực rất hẹp.


1

Tôi tin rằng đó là một trường hợp đặc biệt của việc ghi nhớ , ngoại trừ trong trường hợp này, "bản ghi nhớ" ngầm "tuổi" với mỗi lần tăng của bộ đếm toàn cầu. Tôi đoán một loại "ghi nhớ ngược".

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.