Suatu solusi dimungkinkan hanya karena perbedaan antara 1 megabyte dan 1 juta byte. Ada sekitar 2 pangkat 8093729.5 cara berbeda untuk memilih 1 juta nomor 8-digit dengan duplikat diperbolehkan dan memesan tidak penting, sehingga mesin dengan hanya 1 juta byte RAM tidak memiliki cukup negara untuk mewakili semua kemungkinan. Tapi 1M (kurang 2k untuk TCP / IP) adalah 1022 * 1024 * 8 = 8372224 bit, jadi solusinya mungkin.
Bagian 1, solusi awal
Pendekatan ini membutuhkan sedikit lebih dari 1M, saya akan memperbaikinya agar sesuai dengan 1M nanti.
Saya akan menyimpan daftar angka yang diurutkan secara kompak dalam kisaran 0 hingga 99999999 sebagai urutan sub daftar angka 7-bit. Sublist pertama memegang angka dari 0 hingga 127, sublist kedua memegang angka dari 128 hingga 255, dll. 100000000/128 persis 781250, jadi 781250 sublists semacam itu akan diperlukan.
Setiap sublist terdiri dari header sublist 2-bit diikuti oleh badan sublist. Badan sublist membutuhkan 7 bit per entri sublist. Sublists semuanya digabung bersama, dan formatnya memungkinkan untuk menentukan di mana satu sublist berakhir dan yang berikutnya dimulai. Total penyimpanan yang diperlukan untuk daftar yang terisi penuh adalah 2 * 781250 + 7 * 1000000 = 8562500 bit, yaitu sekitar 1,021 M-byte.
4 nilai header sublist yang mungkin adalah:
00 Sublist kosong, tidak ada yang mengikuti.
01 Singleton, hanya ada satu entri dalam sublist dan dan 7 bit berikutnya tahan.
10 Sublist memiliki setidaknya 2 nomor berbeda. Entri disimpan dalam urutan non-menurun, kecuali bahwa entri terakhir kurang dari atau sama dengan yang pertama. Ini memungkinkan akhir dari sublist diidentifikasi. Misalnya, angka 2,4,6 akan disimpan sebagai (4,6,2). Angka 2,2,3,4,4 akan disimpan sebagai (2,3,4,4,2).
11 Sublist memiliki 2 atau lebih pengulangan dari satu nomor. 7 bit berikutnya memberikan angka. Kemudian datang nol atau lebih entri 7-bit dengan nilai 1, diikuti oleh entri 7-bit dengan nilai 0. Panjang badan sublist menentukan jumlah pengulangan. Misalnya, angka 12,12 akan disimpan sebagai (12,0), angka 12,12,12 akan disimpan sebagai (12,1,0), 12,12,12,12 akan menjadi (12,1) , 1,0) dan seterusnya.
Saya mulai dengan daftar kosong, membaca banyak angka dan menyimpannya sebagai bilangan bulat 32 bit, mengurutkan angka-angka baru di tempat (menggunakan heapsort, mungkin) dan kemudian menggabungkannya ke dalam daftar ringkas yang diurutkan. Ulangi sampai tidak ada lagi angka untuk dibaca, lalu jalankan daftar ringkas sekali lagi untuk menghasilkan output.
Baris di bawah ini mewakili memori tepat sebelum dimulainya operasi penggabungan daftar. "O" adalah wilayah yang menyimpan bilangan bulat 32-bit yang diurutkan. "X" adalah wilayah yang menyimpan daftar compact lama. Tanda "=" adalah ruang ekspansi untuk daftar ringkas, 7 bit untuk setiap bilangan bulat di "O". "Z" adalah overhead acak lainnya.
ZZZOOOOOOOOOOOOOOOOOOOOOOOOOO==========XXXXXXXXXXXXXXXXXXXXXXXXXX
Rutin gabungan mulai membaca di paling kiri "O" dan di paling kiri "X", dan mulai menulis di paling kiri "=". Pointer tulis tidak menangkap pointer baca daftar kompak sampai semua integer baru digabungkan, karena kedua pointer memajukan 2 bit untuk setiap sublist dan 7 bit untuk setiap entri dalam daftar kompak yang lama, dan ada cukup ruang tambahan untuk Entri 7-bit untuk nomor baru.
Bagian 2, menjejalkannya menjadi 1M
Untuk memeras solusi di atas ke dalam 1M, saya perlu membuat format daftar ringkas sedikit lebih kompak. Saya akan menyingkirkan salah satu jenis sublist, sehingga hanya akan ada 3 nilai header sublist yang berbeda. Kemudian saya dapat menggunakan "00", "01" dan "1" sebagai nilai header sublist dan menyimpan beberapa bit. Jenis sublist adalah:
Sublist kosong, tidak ada yang mengikuti.
B Singleton, hanya ada satu entri dalam sublist dan dan 7 bit berikutnya tahan.
C Sublist memiliki setidaknya 2 nomor berbeda. Entri disimpan dalam urutan non-menurun, kecuali bahwa entri terakhir kurang dari atau sama dengan yang pertama. Ini memungkinkan akhir dari sublist diidentifikasi. Misalnya, angka 2,4,6 akan disimpan sebagai (4,6,2). Angka 2,2,3,4,4 akan disimpan sebagai (2,3,4,4,2).
D Sublist terdiri dari 2 atau lebih pengulangan dari satu nomor.
3 nilai header sublist saya adalah "A", "B" dan "C", jadi saya perlu cara untuk mewakili sub-tipe D-daftar.
Misalkan saya memiliki header daftar-jenis C diikuti oleh 3 entri, seperti "C [17] [101] [58]". Ini tidak dapat menjadi bagian dari sublist tipe C yang valid seperti dijelaskan di atas, karena entri ketiga kurang dari yang kedua tetapi lebih dari yang pertama. Saya bisa menggunakan tipe konstruksi ini untuk mewakili sublist tipe-D. Dalam istilah bit, di mana pun saya memiliki "C {00 ?????} {1 ??????} {01 ?????}" "adalah sublist jenis-C yang mustahil. Saya akan menggunakan ini untuk mewakili sublist yang terdiri dari 3 atau lebih pengulangan satu nomor. Dua kata 7-bit pertama mengkodekan angka ("N" bit di bawah) dan diikuti oleh nol atau lebih {0100001} kata diikuti oleh kata {0100000}.
For example, 3 repetitions: "C{00NNNNN}{1NN0000}{0100000}", 4 repetitions: "C{00NNNNN}{1NN0000}{0100001}{0100000}", and so on.
Itu hanya meninggalkan daftar yang memiliki 2 pengulangan dari satu nomor. Saya akan mewakili mereka dengan pola daftar-jenis C lain yang tidak mungkin: "C {0 ??????} {11 ?????} {10 ?????}" ". Ada banyak ruang untuk 7 bit angka dalam 2 kata pertama, tetapi pola ini lebih panjang dari sublist yang diwakilinya, yang membuat segalanya sedikit lebih rumit. Lima tanda tanya di bagian akhir dapat dianggap bukan bagian dari pola, jadi saya punya: "C {0NNNNNN} {11N ????} 10" sebagai pola saya, dengan nomor yang akan diulang disimpan di "N "s. Itu 2 bit terlalu lama.
Saya harus meminjam 2 bit dan mengembalikannya dari 4 bit yang tidak digunakan dalam pola ini. Saat membaca, saat menjumpai "C {0NNNNNN} {11N00AB} 10", hasilkan 2 instance dari angka dalam "N", timpa "10" di bagian akhir dengan bit A dan B, dan putar kembali pointer baca dengan 2 bit. Bacaan destruktif ok untuk algoritma ini, karena setiap daftar ringkas hanya berjalan satu kali.
Saat menulis sublist dari 2 repetisi dari satu nomor, tulis "C {0NNNNNN} 11N00" dan atur penghitung bit yang dipinjam menjadi 2. Pada setiap penulisan di mana penghitung bit yang dipinjam adalah nol, ia dikurangi untuk setiap bit yang ditulis dan "10" ditulis ketika penghitung mencapai nol. Jadi 2 bit berikutnya yang ditulis akan masuk ke slot A dan B, dan kemudian "10" akan jatuh ke ujung.
Dengan 3 nilai header sublist diwakili oleh "00", "01" dan "1", saya dapat menetapkan "1" untuk jenis sublist yang paling populer. Saya akan membutuhkan tabel kecil untuk memetakan nilai-nilai header sublist ke tipe sublist, dan saya akan membutuhkan penghitung kejadian untuk setiap tipe sublist sehingga saya tahu apa pemetaan header sublist terbaik.
Representasi minimal kasus terburuk dari daftar compact yang terisi penuh terjadi ketika semua jenis sublist sama-sama populer. Dalam hal ini saya menyimpan 1 bit untuk setiap 3 sublist header, sehingga ukuran daftar adalah 2 * 781250 + 7 * 1000000 - 781250/3 = 8302083,3 bit. Membulatkan batas kata 32 bit, yaitu 8302112 bit, atau 1037764 byte.
1M dikurangi 2k untuk keadaan TCP / IP dan buffer adalah 1022 * 1024 = 1046528 byte, membuat saya 8764 byte untuk dimainkan.
Tetapi bagaimana dengan proses mengubah pemetaan header sublist? Dalam peta memori di bawah, "Z" adalah overhead acak, "=" adalah ruang kosong, "X" adalah daftar ringkas.
ZZZ=====XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Mulai membaca di "X" paling kiri dan mulai menulis di "" "paling kiri dan bekerja dengan benar. Ketika selesai, daftar ringkas akan sedikit lebih pendek dan akan berada di ujung memori yang salah:
ZZZXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=======
Jadi saya harus men-shunt ke kanan:
ZZZ=======XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Dalam proses perubahan pemetaan tajuk, hingga 1/3 dari sublist header akan berubah dari 1-bit ke 2-bit. Dalam kasus terburuk, semua ini akan menjadi yang teratas dalam daftar, jadi saya akan membutuhkan setidaknya 781250/3 bit penyimpanan gratis sebelum saya mulai, yang akan membawa saya kembali ke persyaratan memori dari versi sebelumnya dari daftar ringkas: (
Untuk menyiasatinya, saya akan membagi 781250 sublists menjadi 10 grup sublist masing-masing 78125 sublists. Setiap grup memiliki pemetaan header sublist yang independen. Menggunakan huruf A ke J untuk grup:
ZZZ=====AAAAAABBCCCCDDDDDEEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
Setiap grup sublist menyusut atau tetap sama selama perubahan pemetaan header sublist:
ZZZ=====AAAAAABBCCCCDDDDDEEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAA=====BBCCCCDDDDDEEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABB=====CCCCDDDDDEEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCC======DDDDDEEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDD======EEEFFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEE======FFFGGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEEFFF======GGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEEFFFGGGGGGGGGG=======HHIJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEEFFFGGGGGGGGGGHH=======IJJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEEFFFGGGGGGGGGGHHI=======JJJJJJJJJJJJJJJJJJJJ
ZZZAAAAAABBCCCDDDDDEEEFFFGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ=======
ZZZ=======AAAAAABBCCCDDDDDEEEFFFGGGGGGGGGGHHIJJJJJJJJJJJJJJJJJJJJ
Ekspansi sementara kasus terburuk dari sublist grup selama perubahan pemetaan adalah 78125/3 = 26042 bit, di bawah 4k. Jika saya mengizinkan 4k ditambah 1037764 byte untuk daftar kompak yang penuh, yang membuat saya 8764 - 4096 = 4668 byte untuk "Z" di peta memori.
Itu seharusnya cukup untuk 10 tabel pemetaan header sublist, 30 jumlah kejadian header sublist dan beberapa counter, pointer dan buffer kecil yang saya perlukan, dan ruang yang saya gunakan tanpa pemberitahuan, seperti ruang stack untuk fungsi, alamat pengirim, dan variabel lokal.
Bagian 3, berapa lama untuk berjalan?
Dengan daftar ringkas kosong tajuk daftar 1 bit akan digunakan untuk sublist kosong, dan ukuran awal daftar adalah 781250 bit. Dalam kasus terburuk daftar tumbuh 8 bit untuk setiap angka yang ditambahkan, jadi 32 + 8 = 40 bit ruang kosong diperlukan untuk masing-masing angka 32-bit yang ditempatkan di bagian atas daftar buffer dan kemudian disortir dan digabungkan. Dalam kasus terburuk, mengubah hasil pemetaan header sublist dalam penggunaan ruang entri 2 * 781250 + 7 * - 781250/3 bit.
Dengan kebijakan mengubah pemetaan tajuk sublist setelah setiap penggabungan kelima setelah setidaknya ada 800.000 angka dalam daftar, proses kasus terburuk akan melibatkan total sekitar 30 juta aktivitas membaca dan menulis daftar ringkas.
Sumber:
http://nick.cleaton.net/ramsortsol.html