Saya tahu pertanyaannya tidak terlalu spesifik.
Yang saya inginkan adalah seseorang memberi tahu saya cara mengonversi jenis gabungan normal menjadi jenis gabungan di tempat (atau semacam gabungan dengan overhead ruang ekstra konstan).
Yang bisa saya temukan (di internet) adalah halaman yang mengatakan "terlalu rumit" atau "di luar jangkauan teks ini".
Satu-satunya cara yang diketahui untuk menggabungkan di tempat (tanpa ruang tambahan) terlalu rumit untuk direduksi menjadi program praktis. (diambil dari sini )
Bahkan jika itu terlalu kompleks, apa konsep dasar bagaimana membuat penggabungan di tempat?
Jawaban:
Knuth meninggalkan ini sebagai latihan (Vol 3, 5.2.5). Memang ada jenis penggabungan di tempat. Mereka harus diimplementasikan dengan hati-hati.
Pertama, penggabungan naif di tempat seperti yang dijelaskan di sini bukan solusi yang tepat. Ini menurunkan kinerja ke O (N 2 ) .
Idenya adalah untuk mengurutkan bagian dari array saat menggunakan sisanya sebagai area kerja untuk penggabungan.
Misalnya seperti fungsi gabungan berikut.
Dibutuhkan array
xs
, dua sub-array yang diurutkan diwakili sebagai rentang[i, m)
dan[j, n)
masing - masing. Wilayah kerja dimulai dariw
. Bandingkan dengan algoritma penggabungan standar yang diberikan di sebagian besar buku teks, yang satu ini bertukar konten antara sub-array yang diurutkan dan area kerja. Akibatnya, area kerja sebelumnya berisi elemen yang diurutkan gabungan, sedangkan elemen sebelumnya yang disimpan di area kerja dipindahkan ke dua sub-array.Namun, ada dua kendala yang harus dipenuhi:
Dengan algoritma penggabungan ini ditentukan, mudah untuk membayangkan solusi, yang dapat mengurutkan setengah dari array; Pertanyaan selanjutnya adalah, bagaimana menangani sisa bagian yang tidak disortir yang disimpan di area kerja seperti yang ditunjukkan di bawah ini:
Satu ide intuitif adalah mengurutkan rekursif setengah dari area kerja, sehingga hanya ada 1/4 elemen yang belum disortir.
Titik kunci pada tahap ini adalah bahwa kita harus menggabungkan elemen 1/4 yang disortir B dengan elemen 1/2 yang diurutkan Cepat atau lambat.
Apakah area kerja tersisa, yang hanya menampung 1/4 elemen, cukup besar untuk menggabungkan A dan B? Sayangnya tidak.
Namun, kendala kedua yang disebutkan di atas memberi kita petunjuk, bahwa kita dapat mengeksploitasinya dengan mengatur area kerja untuk tumpang tindih dengan salah satu sub-array jika kita dapat memastikan urutan penggabungan bahwa elemen yang tidak digabungkan tidak akan ditimpa.
Sebenarnya, alih-alih mengurutkan bagian kedua dari area kerja, kita dapat mengurutkan bagian pertama, dan menempatkan area kerja di antara dua array yang diurutkan seperti ini:
Pengaturan ini secara efektif mengatur area kerja yang tumpang tindih dengan sub-array A. Ide ini diusulkan di [Jyrki Katajainen, Tomi Pasanen, Jukka Teuhola. `` Praktis di tempat mergesort ''. Nordic Journal of Computing, 1996].
Jadi satu-satunya yang tersisa adalah mengulangi langkah di atas, yang mengurangi area kerja dari 1/2, 1/4, 1/8, ... Ketika area kerja menjadi cukup kecil (misalnya, hanya dua elemen yang tersisa), kita dapat beralih ke jenis penyisipan sepele untuk mengakhiri algoritma ini.
Berikut ini adalah implementasi dalam ANSI C berdasarkan makalah ini.
Di mana wmerge didefinisikan sebelumnya.
Kode sumber lengkap dapat ditemukan di sini dan penjelasan terperinci dapat ditemukan di sini
Omong-omong, versi ini bukan jenis penggabungan tercepat karena membutuhkan lebih banyak operasi swap. Menurut pengujian saya, ini lebih cepat daripada versi standar, yang mengalokasikan ruang ekstra di setiap rekursi. Tetapi lebih lambat dari versi yang dioptimalkan, yang menggandakan array asli di muka dan menggunakannya untuk penggabungan lebih lanjut.
sumber
Knuth left this as an exercise (Vol 3, 5.2.5).
mengacu pada mantan. 13. [40] Menerapkan metode internal menyortir disarankan [pada penutupan bagian ini], menghasilkan yang macam data acak di O (N) satuan waktu Mith hanya O (sqrt (N)) lokasi memori tambahan. ? ( 40 menunjukkan masalah yang cukup sulit atau panjang yang mungkin cocok sebagai proyek jangka dalam situasi kelas. )Termasuk "hasil besarnya", makalah ini menjelaskan beberapa varian jenis penggabungan in-place (PDF):
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.22.5514&rep=rep1&type=pdf
Pengurutan di tempat dengan sedikit gerakan
Jyrki Katajainen, Tomi A. Pasanen
Saya pikir ini juga relevan. Saya memiliki cetakannya yang tergeletak, diserahkan kepada saya oleh seorang kolega, tetapi saya belum membacanya. Tampaknya mencakup teori dasar, tapi saya tidak cukup akrab dengan topik untuk menilai seberapa komprehensif:
http://comjnl.oxfordjournals.org/cgi/content/abstract/38/8/681
Penggabungan Stabil Optimal
Antonios Symvonis
sumber
Ini benar-benar tidak mudah atau efisien, dan saya sarankan Anda tidak melakukannya kecuali Anda benar-benar harus (dan Anda mungkin tidak harus kecuali jika ini adalah pekerjaan rumah karena aplikasi penggabungan inplace sebagian besar bersifat teoritis). Tidak bisakah Anda menggunakan quicksort saja? Quicksort akan lebih cepat pula dengan beberapa optimasi sederhana dan memori ekstra adalah O (log N) .
Bagaimanapun, jika Anda harus melakukannya maka Anda harus melakukannya. Inilah yang saya temukan: satu dan dua . Saya tidak terbiasa dengan inge merge sort, tetapi sepertinya ide dasarnya adalah menggunakan rotasi untuk memfasilitasi penggabungan dua array tanpa menggunakan memori tambahan.
Perhatikan bahwa ini lebih lambat bahkan dari jenis penggabungan klasik yang tidak ada.
sumber
Langkah penting adalah mendapatkan penggabungan itu sendiri di tempat. Ini tidak sesulit sumber-sumber itu, tetapi Anda kehilangan sesuatu ketika mencoba.
Melihat satu langkah penggabungan:
Kita tahu bahwa diurutkan urutan kurang dari segala sesuatu yang lain, yang x kurang dari segala sesuatu yang lain di A , dan yang y kurang dari segala sesuatu yang lain di B . Dalam kasus di mana x kurang dari atau sama dengan y , Anda hanya memindahkan pointer Anda ke awal A satu. Dalam kasus di mana y kurang dari x , Anda harus mengocok y melewati seluruh A untuk diurutkan . Langkah terakhir itulah yang membuat ini mahal (kecuali dalam kasus degenerasi).
Ini umumnya lebih murah (terutama ketika array hanya benar-benar berisi kata tunggal per elemen, misalnya, pointer ke string atau struktur) untuk menukar ruang untuk waktu dan memiliki array sementara terpisah yang Anda sortir bolak-balik.
sumber
Hanya untuk referensi, berikut adalah implementasi yang bagus dari semacam gabungan tempat yang stabil . Rumit, tapi tidak terlalu buruk.
Saya akhirnya mengimplementasikan kedua jenis penggabungan in-place yang stabil dan quicksort in -place yang stabil di Jawa. Harap perhatikan kompleksitasnya adalah O (n (log n) ^ 2)
sumber
Contoh mergesort tanpa buffer di C.
Contoh mergesort adaptif (dioptimalkan).
Menambahkan kode dukungan dan modifikasi untuk mempercepat penggabungan ketika buffer tambahan dalam berbagai ukuran tersedia (masih berfungsi tanpa memori tambahan). Menggunakan penggabungan maju dan mundur, rotasi cincin, penggabungan dan penyortiran urutan kecil, dan penggabungan berulang.
sumber
Jawaban ini memiliki contoh kode , yang mengimplementasikan algoritma yang dijelaskan dalam makalah Praktis Penggabungan In-Place oleh Bing-Chao Huang dan Michael A. Langston. Saya harus mengakui bahwa saya tidak mengerti detailnya, tetapi kompleksitas langkah penggabungan yang diberikan adalah O (n).
Dari perspektif praktis, ada bukti bahwa implementasi murni di tempat tidak berkinerja lebih baik dalam skenario dunia nyata. Sebagai contoh, standar C ++ mendefinisikan std :: inplace_merge , yang seperti namanya mengimplikasikan operasi penggabungan in-place.
Dengan asumsi bahwa pustaka C ++ biasanya dioptimalkan dengan sangat baik, menarik untuk melihat bagaimana itu diimplementasikan:
1) libstdc ++ (bagian dari basis kode GCC): std :: inplace_merge
Delegasi implementasi ke __inplace_merge , yang menghindari masalah dengan mencoba mengalokasikan buffer sementara:
Kalau tidak, ia kembali ke implementasi ( __merge_without_buffer ), yang tidak memerlukan memori tambahan, tetapi tidak lagi berjalan dalam waktu O (n).
2) libc ++ (bagian dari basis kode Dentang ): std :: inplace_merge
Terlihat mirip. Ini mendelegasikan ke fungsi , yang juga mencoba untuk mengalokasikan buffer . Bergantung pada apakah elemennya cukup, ia akan memilih implementasinya. Fungsi fallback memori konstan disebut __buffered_inplace_merge .
Mungkin bahkan mundur masih O (n) waktu, tetapi intinya adalah bahwa mereka tidak menggunakan implementasi jika memori sementara tersedia.
Perhatikan bahwa standar C ++ secara eksplisit memberikan implementasi kebebasan untuk memilih pendekatan ini dengan menurunkan kompleksitas yang diperlukan dari O (n) ke O (N log N):
Tentu saja, ini tidak dapat diambil sebagai bukti bahwa ruang konstan di tempat bergabung dalam waktu O (n) tidak boleh digunakan. Di sisi lain, jika akan lebih cepat, pustaka C ++ yang dioptimalkan mungkin akan beralih ke jenis implementasi tersebut.
sumber
Ini adalah versi C saya:
sumber
Ada implementasi yang relatif sederhana dari jenis penggabungan in-place menggunakan teknik asli Kronrod tetapi dengan implementasi yang lebih sederhana. Contoh gambar yang menggambarkan teknik ini dapat ditemukan di sini: http://www.logiccoder.com/TheSortProblem/BestMergeInfo.htm .
Ada juga tautan ke analisis teoretis yang lebih rinci oleh penulis yang sama yang terkait dengan tautan ini.
sumber
Saya baru saja mencoba di tempat menggabungkan algoritma untuk menggabungkan semacam di JAWA dengan menggunakan algoritma semacam penyisipan, menggunakan langkah-langkah berikut.
1) Tersedia dua array yang diurutkan.
2) Bandingkan nilai pertama dari setiap array; dan tempatkan nilai terkecil ke dalam array pertama.
3) Tempatkan nilai yang lebih besar ke dalam array kedua dengan menggunakan jenis penyisipan (melintasi dari kiri ke kanan).
4) Kemudian bandingkan lagi nilai kedua array pertama dan nilai pertama array kedua, dan lakukan hal yang sama. Tetapi ketika bertukar terjadi ada beberapa petunjuk tentang melompat membandingkan item lebih lanjut, tetapi hanya bertukar diperlukan.
Saya telah membuat beberapa optimasi di sini; untuk menjaga perbandingan yang lebih rendah dalam jenis penyisipan.
Satu-satunya kelemahan yang saya temukan dengan solusi ini adalah perlu swapping elemen array yang lebih besar di array kedua.
misalnya)
First___Array: 3, 7, 8, 9
Array Kedua: 1, 2, 4, 5
Kemudian 7, 8, 9 membuat array kedua untuk bertukar (bergerak ke kiri oleh satu) semua elemennya dengan satu setiap kali menempatkan dirinya di yang terakhir.
Jadi asumsi di sini adalah menukar item dapat diabaikan dibandingkan dengan membandingkan dua item.
https://github.com/skanagavelu/algorithams/blob/master/src/sorting/MergeSort.java
sumber