Cần bao nhiêu bản sao để phóng to một mảng?


8

Tôi đang đọc một phân tích về mảng động (từ hướng dẫn thuật toán của Skiena).
Tức là khi chúng ta có một cấu trúc mảng và mỗi khi chúng ta hết dung lượng, chúng ta sẽ phân bổ một mảng mới có kích thước gấp đôi kích thước ban đầu.

Nó mô tả chất thải xảy ra khi mảng phải được thay đổi kích thước.
Nó nói rằng (n / 2) +1 đến n sẽ được di chuyển nhiều nhất một lần hoặc không. Điều này rõ ràng.
Sau đó, bằng cách mô tả rằng một nửa các phần tử di chuyển một lần, một phần tư các phần tử hai lần, v.v., tổng số chuyển động M được đưa ra bởi:

nhập mô tả hình ảnh ở đây

Điều này dường như với tôi rằng nó thêm nhiều bản sao hơn thực tế xảy ra.

Ví dụ

nếu chúng ta có những điều sau đây:

array of 1 element
+--+
|a |
+--+

double the array (2 elements)  
+--++--+  
|a ||b |  
+--++--+  

double the array (4 elements)  
+--++--++--++--+  
|a ||b ||c ||c |  
+--++--++--++--+  

double the array (8 elements)  
+--++--++--++--++--++--++--++--+    
|a ||b ||c ||c ||x ||x ||x ||x |  
+--++--++--++--++--++--++--++--+    

double the array (16 elements)  
+--++--++--++--++--++--++--++--++--++--++--++--++--++--++--++--+    
|a ||b ||c ||c ||x ||x ||x ||x ||  ||  ||  ||  ||  ||  ||  ||  |   
+--++--++--++--++--++--++--++--++--++--++--++--++--++--++--++--+   

Chúng ta có phần tử x được sao chép 4 lần, phần tử c được sao chép 4 lần, phần tử b được sao chép 4 lần và một phần tử được sao chép 5 lần nên tổng cộng là 4 + 4 + 4 + 5 = 17 bản sao / chuyển động.

Nhưng theo công thức, chúng ta nên có 1 * (16/2) + 2 * (16/4) + 3 * (16/8) + 4 * (16/16) = 8 + 8 + 6 + 4 = 26 bản sao của các phần tử để mở rộng mảng thành 16 phần tử.

Đây có phải là một số sai lầm hoặc mục đích của công thức là cung cấp một xấp xỉ giới hạn trên thô? Hay tôi đang hiểu sai một cái gì đó ở đây?


Một yếu tố khác: trong thế giới thực, các phần tử được phân bổ trống sẽ bị loại bỏ (trong một ngôn ngữ cấp cao như Java hoặc C #). Điều này đòi hỏi phải viết (nhưng không đọc), có vẻ như chỉ tốn một nửa so với bản sao.
dbkk

1
Số tiền của bạn không đúng; bđược sao chép 3 lần, mỗi clần hai lần và mỗi xlần một lần. 15 bản.
Donal Fellows

Câu trả lời:


5

Đầu tiên, b được di chuyển 3 lần và a được di chuyển 4 lần, cho tổng số 4 + 4 + 3 + 4 = 15 bản sao.

Tôi nghĩ rằng công thức nên được điền với n = 8: 1 * (8/2) (x được sao chép một lần) + 2 * (8/4) (c được sao chép hai lần) + 3 * (8/8) (b được sao chép ba lần) = 11. Nói cách khác, công thức dường như thiếu một thuật ngữ "+ log 2 n + 1" ngoài tổng của chính nó.

Điều có vẻ như đối với tôi là một cách tự nhiên hơn nhiều để đếm số lần di chuyển là đếm số phần tử được di chuyển trên mỗi bản sao:

tổng từ i = 1 đến i = trần (log 2 n): 2 i-1

Trong trường hợp của bạn, n = 16, do đó trần (log 2 16) = 4 và tổng trên là: 2 0 +2 1 +2 2 +2 3 = 1 + 2 + 4 + 8 = 15.

Tôi sẽ xem liệu tôi có thể tìm thấy hướng dẫn sử dụng thuật toán của Skiena này để xem liệu tôi đã hiểu đúng chưa.

Cập nhật: Tôi tìm thấy một phần trong hướng dẫn thuật toán của Skiena. Dường như có một thuật ngữ bị thiếu trong số tiền anh ta sử dụng ở đó. Tuy nhiên, kết luận là đúng:

M = tổng từ i = 1 đến i = trần (log 2 n): 2 i - 1 = sum từ i = 0 đến i = trần (log 2 n) - 1: 2 i = 2 trần (log 2 n) - 1 + 1 <= (2 log 2 n + 1 - 1 + 1 ) = 2 * n

(Tôi ước tôi có thể định dạng các công thức này theo cách đẹp hơn cho bạn)

Điểm chính của đoạn này dường như là đưa ra một ví dụ về phân tích khấu hao . Các phương thức như phương thức tiềm năng sẽ tạo ra một đối số tốt hơn (ít quảng cáo hơn) tại sao các mảng động hoạt động rất tốt, nhưng phương pháp này có phần tiên tiến.

Nếu bạn tin rằng có một lỗi trong cuốn sách này, bạn có thể xem xét liên hệ với tác giả về điều này (theo cách xây dựng - cuốn sách có rất nhiều trang, và thật khó để có được mọi điều cuối cùng chính xác, và luôn luôn có một cơ hội là cuốn sách đúng và cả hai chúng tôi đều hiểu sai). Tôi đã không tìm thấy cái này đặc biệt trên errata.


Tôi định dạng các công thức một chút :-)
Péter Török

Cảm ơn bạn, giờ nó trông đẹp hơn nhiều - tôi đã quen với định dạng LaTeX và tôi không nghĩ điều đó có thể có trên Lập trình viên.
Alex ten Brink

@Alex: +1 cảm ơn bạn vì điều này. Tôi đã tự hỏi tại sao bạn nghĩ rằng trong OP nên là 8 chứ không phải 16. Tôi đã không nhận được điều đó.
dùng10326

Bởi vì sau đó các thuật ngữ i * n / 2 ^ i có ý nghĩa: nếu i = 1, thì bạn nói về 1 * n / 2, tương ứng với một nửa đầu vào được sao chép một lần. Trong ví dụ của anh ấy, có bốn vị trí x được sao chép một lần và 8/2 = 4, vì vậy n = 8 sẽ có ý nghĩa hơn. Nếu n = 16, thì 16/2 = 8 phần tử được cho là sẽ được sao chép một lần, đơn giản là không khớp với ví dụ.
Alex ten Brink

2

Ở các cấp số khối thấp hơn, việc phân bổ bộ nhớ thực sự không xảy ra. Các trình quản lý bộ nhớ xử lý các khối bộ nhớ và phân bổ thường xuyên các khối bộ nhớ lớn hơn yêu cầu phân bổ theo yêu cầu cấp thiết.

Tương tự như vậy, việc thực hiện một lớp mảng có khả năng làm tròn các phân bổ để cho phép một vài yếu tố bổ sung.

BIÊN TẬP:

Về sự phản ánh hơn nữa, các bản sao thực tế không có khả năng xảy ra như bạn mô tả chúng. Bộ xử lý thường có lệnh sao chép khối và sẽ sử dụng một lệnh trình giả định duy nhất để sao chép dữ liệu mảng dưới dạng một khối bộ nhớ vào địa chỉ mới.


1
Xin lỗi, điều này liên quan đến câu hỏi của tôi như thế nào?
dùng10326

Vâng, nếu việc phân bổ không cần phải xảy ra, thì không cần phải sao chép các phần tử mảng vào không gian bộ nhớ mới.
Michael Shaw

1
Nhưng tôi đang hỏi về công thức.
dùng10326

Đủ công bằng, đó là một câu hỏi toán học và tôi đang đưa ra câu trả lời lập trình trên một trang web lập trình ...;)
Michael Shaw

0

Tôi tin rằng công thức được đưa ra trong cuốn sách chỉ đơn giản là không chính xác. Số inhân phải được loại bỏ khỏi công thức để sửa nó.

Hãy lấy ví dụ của người hỏi và gọi mảng của 1 phần tử mảng-1, mảng gồm 2 phần tử - array-2, mảng gồm 4 phần tử - array-4v.v.

Vì vậy, theo cuốn sách, đối với ví dụ cụ thể này, số lượng bản sao được điều chỉnh theo công thức sau:


M = 1⋅8 + 2⋅4 + 3⋅2 + 4⋅1

Thuật ngữ đầu tiên của tổng 1⋅8là để sao chép array-8'scác mục vào array-16.

Chúng tôi sao chép các array-4'smục (a, b, c, c)hai lần. Một lần từ array-4đến array-8. Và sau đó khi sao chép array-8'scác mục để array-16chúng tôi sao chép (a, b, c, c)các mục lần thứ hai. Theo cuốn sách, do đó, thuật ngữ thứ hai : 2⋅4.

Nhưng bây giờ lưu ý rằng 1⋅8thuật ngữ đã tính đến việc sao chép các (a, b, c, c)mục từ array-8đến array-16. Do đó, 2⋅4thuật ngữ không được bao gồm 2số nhân.

Logic tương tự áp dụng cho tất cả các điều khoản khác. Và nhân lên ilà một sai lầm.


bạn có phiền giải thích thêm về những gì nó làm không và tại sao bạn lại đề nghị nó như trả lời câu hỏi được hỏi? "Câu trả lời chỉ liên kết" không được chào đón tại Stack Exchange
gnat

Chắc chắn rồi. Tôi sẽ sao chép câu trả lời của tôi từ cs.stackexchange. Vấn đề là mặc dù lập trình viên.stackexchange không cho phép định dạng công thức toán học thích hợp.
Nik

theo cách đọc của tôi, các công thức trong câu trả lời của bạn tại CS có thể được xấp xỉ một cách hợp lý bằng cách sử dụng định dạng mã với backticks: M=1⋅8+2⋅4+3⋅2+4⋅1vv
gnat
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.