Berapa banyak salinan yang dibutuhkan untuk memperbesar array?

8

Saya membaca analisis tentang array dinamis (dari manual algoritma Skiena).
Yaitu ketika kita memiliki struktur array dan setiap kali kita kehabisan ruang, kita mengalokasikan array baru dua kali lipat ukuran aslinya.

Ini menjelaskan pemborosan yang terjadi ketika array harus diubah ukurannya.
Dikatakan bahwa (n / 2) +1 hingga n akan dipindahkan paling banyak satu kali atau tidak sama sekali. Ini jelas.
Kemudian dengan menjelaskan bahwa separuh elemen bergerak satu kali, seperempat elemen dua kali, dan seterusnya, jumlah total gerakan M diberikan oleh:

masukkan deskripsi gambar di sini

Bagi saya ini menambah lebih banyak salinan daripada yang sebenarnya terjadi.

Misalnya

jika kita memiliki yang berikut ini:

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 ||  ||  ||  ||  ||  ||  ||  ||  |   
+--++--++--++--++--++--++--++--++--++--++--++--++--++--++--++--+   

Kami memiliki elemen x disalin 4 kali, elemen c disalin 4 kali, elemen b disalin 4 kali dan elemen disalin 5 kali sehingga totalnya adalah 4 + 4 + 4 + 5 = 17 salinan / gerakan.

Tetapi menurut rumus kita harus memiliki 1 * (16/2) + 2 * (16/4) + 3 * (16/8) + 4 * (16/16) = 8 + 8 + 6 + 4 = 26 salinan dari elemen untuk pembesaran array menjadi 16 elemen.

Apakah ini beberapa kesalahan atau tujuan dari formula ini adalah untuk memberikan perkiraan batas atas kasar? Atau apakah saya salah memahami sesuatu di sini?

pengguna10326
sumber
Faktor lain: di dunia nyata, elemen yang dialokasikan kosong akan di-zero-out (dalam bahasa tingkat tinggi seperti Java atau C #). Ini mensyaratkan penulisan (tetapi bukan membaca), yang tampaknya menelan biaya setengah dari salinan.
dbkk
1
Jumlah Anda tidak benar; bdisalin 3 kali, masing-masing cdua kali, dan masing-masing satu xkali. 15 salinan.
Donal Fellows

Jawaban:

5

Pertama, b dipindahkan 3 kali dan a dipindahkan 4 kali, yang memberikan total 4 + 4 + 3 + 4 = 15 salinan.

Saya pikir rumus harus diisi dengan n = 8: 1 * (8/2) (x disalin sekali) + 2 * (8/4) (c disalin dua kali) + 3 * (8/8) (b disalin tiga kali) = 11. Dengan kata lain, rumus tampaknya tidak memiliki istilah "+ log 2 n +1" selain jumlah itu sendiri.

Apa yang menurut saya merupakan cara yang lebih alami untuk menghitung jumlah gerakan adalah dengan menghitung jumlah elemen yang dipindahkan per salinan:

jumlah dari i = 1 ke i = langit-langit (log 2 n): 2 i-1

Dalam kasus Anda, n = 16, jadi langit-langit (log 2 16) = 4 dan jumlah di atas adalah: 2 0 +2 1 +2 2 +2 3 = 1 + 2 + 4 + 8 = 15.

Saya akan melihat apakah saya dapat menemukan manual algoritma Skiena ini untuk melihat apakah saya sudah benar.

Pembaruan: Saya menemukan bagian dalam manual algoritma Skiena. Sepertinya ada istilah yang hilang dalam jumlah yang dia gunakan di sana. Namun, kesimpulannya benar:

M = jumlah dari i = 1 ke i = langit-langit (log 2 n): 2 i-1 = jumlah dari i = 0 sampai i = langit-langit (log 2 n) - 1: 2 i = 2 langit-langit (log 2 n) - 1 + 1 <= (2 log 2 n + 1 - 1 + 1 ) = 2 * n

(Saya berharap saya bisa memformat formula ini dengan cara yang lebih baik untuk Anda)

Poin utama dari paragraf ini tampaknya adalah untuk memberikan contoh analisis diamortisasi . Metode seperti metode potensial akan membuat argumen yang lebih baik (kurang ad hoc) mengapa array dinamis berkinerja sangat baik, tetapi metode ini agak maju.

Jika Anda yakin ada kesalahan dalam buku ini, Anda dapat mempertimbangkan untuk menghubungi penulis tentang hal ini (tentu saja dengan cara yang konstruktif - buku ini memiliki banyak halaman, dan sulit untuk menyelesaikan semua hal terakhir dengan benar, dan selalu ada kemungkinan buku itu benar dan kami berdua salah). Saya belum menemukan yang satu ini di errata.

Alex ten Brink
sumber
Saya sedikit memformat formula :-)
Péter Török
Terima kasih, ini terlihat jauh lebih baik sekarang - Saya sudah terbiasa dengan pemformatan LaTeX, dan saya pikir itu tidak mungkin di Programmers.SE.
Alex ten Brink
@Alex: +1 terima kasih untuk ini. Saya bertanya-tanya mengapa Anda berpikir bahwa dalam OP n seharusnya 8 dan bukan 16. Saya tidak mengerti.
user10326
Karena maka istilah i * n / 2 ^ i masuk akal: jika i = 1, maka Anda berbicara tentang 1 * n / 2, yang akan sesuai dengan setengah input yang disalin sekali. Dalam contohnya, ada empat posisi x yang bisa disalin sekali, dan 8/2 = 4, jadi n = 8 akan lebih masuk akal. Jika n = 16, maka 16/2 = 8 elemen seharusnya disalin sekali, yang tidak cocok dengan contoh.
Alex ten Brink
2

Pada level penghitungan blok bawah, tidak mungkin terjadi alokasi memori. Manajer memori menangani blok memori dan secara rutin mengalokasikan blok memori yang lebih besar daripada permintaan alokasi yang diminta.

Demikian juga, implementasi kelas array kemungkinan untuk mengumpulkan alokasi untuk memungkinkan beberapa elemen tambahan.

EDIT:

Pada refleksi lebih lanjut, salinan yang sebenarnya tidak akan terjadi saat Anda menggambarkannya juga. Prosesor biasanya memiliki perintah salin blok dan akan menggunakan instruksi assmbler tunggal untuk menyalin data array sebagai blok memori tunggal ke alamat baru.

Michael Shaw
sumber
1
Maaf, bagaimana ini terkait dengan pertanyaan saya?
user10326
Nah jika alokasi tidak perlu terjadi, maka tidak perlu menyalin elemen array ke ruang memori baru.
Michael Shaw
1
Tapi saya bertanya tentang formula.
user10326
Cukup adil, ini pertanyaan matematis dan saya memberikan jawaban pemrograman di situs pemrograman ...;)
Michael Shaw
0

Saya percaya formula yang diberikan dalam buku itu tidak benar. The imultiplier harus turun dari rumus untuk memperbaikinya.

Mari kita ambil contoh si penanya dan memanggil larik 1 elemen larik-1, larik 2 elemen - array-2, larik 4 elemen - array-4, dan seterusnya.

Jadi, sesuai dengan buku, untuk contoh khusus ini jumlah salinan diatur oleh rumus berikut:


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

Istilah pertama dari jumlah tersebut 1⋅8adalah untuk menyalin array-8'sitem ke dalam array-16.

Kami menyalin array-4'sitem (a, b, c, c)dua kali. Sekali dari array-4ke array-8. Dan ketika menyalin array-8'sitem ke array-16kita menyalin (a, b, c, c)item untuk kedua kalinya. Sesuai dengan buku, maka istilah yang kedua: 2⋅4.

Tetapi sekarang perhatikan bahwa 1⋅8istilah tersebut sudah memperhitungkan penyalinan (a, b, c, c)barang dari array-8ke array-16. Karenanya 2⋅4istilah tersebut tidak boleh menyertakan 2pengganda.

Logika yang sama berlaku untuk semua istilah lainnya. Dan mengalikan dengan imerupakan kesalahan.

Nik
sumber
maukah Anda menjelaskan lebih lanjut tentang apa yang dilakukannya dan mengapa Anda merekomendasikannya untuk menjawab pertanyaan yang diajukan? "Jawaban khusus tautan" tidak diterima di Stack Exchange
agas
Tentu. Saya akan menyalin jawaban saya dari cs.stackexchange. Masalahnya adalah bahwa programer.stackexchange tidak memungkinkan pemformatan rumus matematika yang tepat.
Nik
per bacaan saya, rumus dalam jawaban Anda di CS dapat diperkirakan dengan menggunakan format kode dengan backticks: M=1⋅8+2⋅4+3⋅2+4⋅1etc
gnat