Saya memiliki komputer dengan 1 MB RAM dan tidak ada penyimpanan lokal lainnya. Saya harus menggunakannya untuk menerima 1 juta angka desimal 8 digit melalui koneksi TCP, mengurutkannya, dan kemudian mengirimkan daftar yang diurutkan melalui koneksi TCP lain.
Daftar angka mungkin berisi duplikat, yang tidak boleh saya buang. Kode akan ditempatkan di ROM, jadi saya tidak perlu mengurangi ukuran kode saya dari 1 MB. Saya sudah memiliki kode untuk menggerakkan port Ethernet dan menangani koneksi TCP / IP, dan memerlukan 2 KB untuk data statusnya, termasuk buffer 1 KB di mana kode akan membaca dan menulis data. Apakah ada solusi untuk masalah ini?
Sumber Tanya Jawab:
Jawaban:
Ada satu trik yang agak licik yang tidak disebutkan di sini sejauh ini. Kami berasumsi bahwa Anda tidak memiliki cara ekstra untuk menyimpan data, tetapi itu tidak sepenuhnya benar.
Salah satu cara mengatasi masalah Anda adalah melakukan hal mengerikan berikut, yang tidak boleh dilakukan oleh siapa pun dalam keadaan apa pun: Gunakan lalu lintas jaringan untuk menyimpan data. Dan tidak, maksud saya bukan NAS.
Anda dapat mengurutkan angka hanya dengan beberapa byte RAM dengan cara berikut:
COUNTER
danVALUE
.0
;I
, kenaikanCOUNTER
dan setelVALUE
kemax(VALUE, I)
;I
router. HapusI
dan ulangi.Setelah
COUNTER
mencapai1000000
, Anda memiliki semua nilai yang disimpan dalam aliran permintaan ICMP, danVALUE
sekarang berisi integer maksimum. Pilih beberapathreshold T >> 1000000
. SetelCOUNTER
ke nol. Setiap kali Anda menerima paket ICMP, naikkanCOUNTER
dan kirim bilangan bulat yang adaI
kembali dalam permintaan gema lain, kecualiI=VALUE
, dalam hal ini mentransmisikannya ke tujuan bilangan bulat yang diurutkan. SekaliCOUNTER=T
, kurangiVALUE
dengan1
, resetCOUNTER
ke nol dan ulangi. SetelahVALUE
mencapai nol, Anda harus mentransmisikan semua bilangan bulat secara berurutan dari yang terbesar ke yang terkecil ke tujuan, dan hanya menggunakan sekitar 47 bit RAM untuk dua variabel persisten (dan berapa pun jumlah kecil yang Anda perlukan untuk nilai sementara).Saya tahu ini mengerikan, dan saya tahu mungkin ada berbagai masalah praktis, tetapi saya pikir itu mungkin membuat sebagian dari Anda tertawa atau setidaknya membuat Anda ngeri.
sumber
Inilah beberapa kode C ++ yang berfungsi untuk menyelesaikan masalah.
Bukti bahwa batasan memori terpenuhi:Editor: Tidak ada bukti persyaratan memori maksimum yang ditawarkan oleh penulis baik dalam posting ini atau di blog-nya. Karena jumlah bit yang diperlukan untuk mengkodekan nilai tergantung pada nilai yang sebelumnya dikodekan, bukti seperti itu kemungkinan non-sepele. Penulis mencatat bahwa ukuran penyandian terbesar yang bisa ia temukan secara empiris adalah
1011732
, dan memilih ukuran penyangga secara1013000
sewenang-wenang.Bersama-sama, dua array ini membutuhkan 1045000 byte penyimpanan. Yang meninggalkan 1048576 - 1045000 - 2 × 1024 = 1528 byte untuk variabel yang tersisa dan ruang stack.
Ini berjalan dalam waktu sekitar 23 detik pada Xeon W3520 saya. Anda dapat memverifikasi bahwa program bekerja menggunakan skrip Python berikut, dengan asumsi nama program
sort1mb.exe
.Penjelasan terperinci dari algoritme dapat ditemukan dalam serangkaian posting berikut:
sumber
(n+m)!/(n!m!)
) jadi Anda pasti benar. Mungkin ini perkiraan saya bahwa delta b bit membutuhkan b bit untuk disimpan - jelas delta 0 tidak mengambil 0 bit untuk disimpan.Silakan lihat jawaban yang benar pertama atau jawaban selanjutnya dengan pengkodean aritmatika . Di bawah ini Anda mungkin menemukan kesenangan, tetapi bukan solusi anti peluru 100%.
Ini tugas yang cukup menarik dan ini solusi lain. Saya harap seseorang akan menemukan hasilnya bermanfaat (atau paling tidak menarik).
Tahap 1: Struktur data awal, pendekatan kompresi kasar, hasil dasar
Mari kita lakukan beberapa perhitungan sederhana: kita memiliki 1M (1048576 byte) RAM yang awalnya tersedia untuk menyimpan angka desimal 10 ^ 6 8 digit. [0; 99999999]. Jadi untuk menyimpan satu angka, 27 bit diperlukan (dengan asumsi bahwa nomor yang tidak ditandatangani akan digunakan). Jadi, untuk menyimpan aliran mentah ~ 3.5M RAM akan dibutuhkan. Seseorang sudah mengatakan itu tampaknya tidak layak, tetapi saya akan mengatakan tugas itu dapat diselesaikan jika inputnya "cukup baik". Pada dasarnya, idenya adalah untuk mengompresi data input dengan faktor kompresi 0,29 atau lebih tinggi dan melakukan pengurutan dengan cara yang benar.
Mari kita selesaikan masalah kompresi terlebih dahulu. Ada beberapa tes yang relevan sudah tersedia:
http://www.theeggeadventure.com/wikimedia/index.php/Java_Data_Compression
Sepertinya LZMA ( Algoritma rantai Lempel-Ziv-Markov ) adalah pilihan yang baik untuk melanjutkan. Saya sudah menyiapkan PoC sederhana, tetapi masih ada beberapa detail yang perlu disorot:
Harap dicatat, kode terlampir adalah POC , tidak dapat digunakan sebagai solusi akhir, itu hanya menunjukkan gagasan untuk menggunakan beberapa buffer yang lebih kecil untuk menyimpan angka yang telah dipilih dengan cara yang optimal (mungkin dikompresi). LZMA tidak diusulkan sebagai solusi akhir. Ini digunakan sebagai cara tercepat untuk memperkenalkan kompresi pada PoC ini.
Lihat kode PoC di bawah ini (harap dicatat hanya demo, untuk mengompilasinya diperlukan LZMA-Java ):
Dengan angka acak menghasilkan sebagai berikut:
Untuk urutan menaik sederhana (satu ember digunakan) itu menghasilkan:
EDIT
Kesimpulan:
Tahap 2: Peningkatan kompresi, kesimpulan akhir
Seperti yang telah disebutkan di bagian sebelumnya, teknik kompresi yang cocok dapat digunakan. Jadi mari kita singkirkan LZMA demi pendekatan yang lebih sederhana dan lebih baik (jika mungkin). Ada banyak solusi bagus termasuk coding Aritmatika , pohon Radix dll.
Bagaimanapun, skema pengkodean yang sederhana namun bermanfaat akan lebih ilustratif daripada perpustakaan eksternal lainnya, menyediakan beberapa algoritma yang bagus. Solusi sebenarnya cukup mudah: karena ada ember dengan data yang diurutkan sebagian, delta dapat digunakan sebagai ganti angka.
Tes input acak menunjukkan hasil yang sedikit lebih baik:
Kode sampel
Harap dicatat, pendekatan ini:
Kode lengkap dapat ditemukan di sini , implementasi BinaryInput dan BinaryOutput dapat ditemukan di sini
Kesimpulan akhir
Tidak ada kesimpulan akhir :) Terkadang ide yang bagus untuk naik satu tingkat dan meninjau tugas dari sudut pandang meta-level .
Menyenangkan menghabiskan waktu dengan tugas ini. BTW, ada banyak jawaban menarik di bawah ini. Terima kasih atas perhatian dan kode senang Anda.
sumber
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.
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}.
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.
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:
Jadi saya harus men-shunt ke kanan:
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:
Setiap grup sublist menyusut atau tetap sama selama perubahan pemetaan header sublist:
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
sumber
3 * 3 * 3 = 27 < 32
. Anda menggabungkannyacombined_subheader = subheader1 + 3 * subheader2 + 9 * subheader3
.Jawaban Gilmanov sangat salah dalam anggapannya. Itu mulai berspekulasi berdasarkan pada ukuran tak berguna dari sejuta bilangan bulat berturut-turut . Itu berarti tidak ada celah. Kesenjangan acak itu, betapapun kecilnya, benar-benar membuatnya menjadi ide yang buruk.
Cobalah sendiri. Dapatkan 1 juta bilangan bulat 27 bit acak, sortir, kompres dengan 7-Zip , xz, LZMA apa pun yang Anda inginkan. Hasilnya lebih dari 1,5 MB. Premis di atas adalah kompresi nomor urut. Bahkan encoding delta itu adalah lebih dari 1,1 MB . Dan tidak masalah ini menggunakan lebih dari 100 MB RAM untuk kompresi. Jadi, bahkan bilangan bulat yang dikompresi tidak cocok dengan masalah dan apalagi menjalankan penggunaan RAM .
Ini membuat saya sedih bagaimana orang-orang hanya memilih cukup grafis dan rasionalisasi.
Sekarang kompres ints.bin dengan LZMA ...
sumber
Saya pikir salah satu cara untuk memikirkan hal ini adalah dari sudut pandang kombinatorik: ada berapa banyak kemungkinan kombinasi urutan nomor yang disortir? Jika kita memberikan kombinasi 0,0,0, ...., 0 kode 0, dan 0,0,0, ..., 1 kode 1, dan 99999999, 99999999, ... 99999999 kode N, apa itu N? Dengan kata lain, seberapa besar ruang hasil?
Nah, salah satu cara untuk memikirkan hal ini adalah dengan memperhatikan bahwa ini adalah peninggalan masalah menemukan jumlah jalur monotonik dalam kisi N x M, di mana N = 1.000.000 dan M = 100.000.000. Dengan kata lain, jika Anda memiliki kisi yang lebar 1.000.000 dan tinggi 100.000.000, berapa banyak jalur terpendek dari kiri bawah ke kanan atas? Jalur terpendek tentu saja mengharuskan Anda hanya bergerak ke kanan atau ke atas (jika Anda bergerak ke bawah atau ke kiri Anda akan membatalkan kemajuan yang telah dicapai sebelumnya). Untuk melihat bagaimana ini merupakan masalah dari masalah penyortiran nomor kami, perhatikan hal berikut:
Anda dapat membayangkan kaki horizontal apa pun di jalur kami sebagai angka dalam pemesanan kami, di mana lokasi Y dari kaki tersebut mewakili nilai.
Jadi jika jalur hanya bergerak ke kanan sampai ke ujung, kemudian melompat ke atas, itu setara dengan pemesanan 0,0,0, ..., 0. jika itu dimulai dengan melompat ke atas dan kemudian bergerak ke kanan 1.000.000 kali, itu setara dengan 99999999,99999999, ..., 99999999. Sebuah jalan di mana ia bergerak ke kanan sekali, lalu naik sekali, lalu ke kanan , lalu naik sekali, dll hingga akhir (lalu harus melompat ke atas), setara dengan 0,1,2,3, ..., 999999.
Untungnya bagi kami masalah ini telah dipecahkan, grid seperti itu memiliki (N + M) Pilih jalur (M):
(1.000.000 + 100.000.000) Pilih (100.000.000) ~ = 2.27 * 10 ^ 2436455
Dengan demikian N sama dengan 2.27 * 10 ^ 2436455, dan kode 0 mewakili 0,0,0, ..., 0 dan kode 2,27 * 10 ^ 2436455 dan beberapa perubahan mewakili 99999999,99999999, ..., 99999999.
Untuk menyimpan semua angka dari 0 hingga 2.27 * 10 ^ 2436455 Anda perlu lg2 (2.27 * 10 ^ 2436455) = 8.0937 * 10 ^ 6 bit.
1 megabyte = 8388608 bit> 8093700 bit
Jadi sepertinya kita setidaknya benar-benar memiliki cukup ruang untuk menyimpan hasilnya! Sekarang tentu saja bit yang menarik adalah melakukan pengurutan sebagai aliran angka masuk. Tidak yakin pendekatan terbaik untuk ini diberikan kita memiliki 294908 bit yang tersisa. Saya membayangkan teknik yang menarik adalah pada setiap titik mengasumsikan bahwa itu adalah seluruh pemesanan, menemukan kode untuk pemesanan itu, dan kemudian ketika Anda menerima nomor baru kembali dan memperbarui kode sebelumnya. Gelombang tangan gelombang tangan.
sumber
Saran saya di sini berutang banyak pada solusi Dan
Pertama saya menganggap solusinya harus menangani semua daftar input yang mungkin. Saya pikir jawaban populer tidak membuat asumsi ini (yang merupakan kesalahan besar IMO).
Diketahui bahwa tidak ada bentuk kompresi lossless akan mengurangi ukuran semua input.
Semua jawaban populer berasumsi mereka akan dapat menerapkan kompresi yang cukup efektif untuk memberikan mereka ruang ekstra. Bahkan, sepotong ruang ekstra yang cukup besar untuk menampung sebagian dari daftar mereka yang telah selesai sebagian dalam bentuk yang tidak terkompresi dan memungkinkan mereka untuk melakukan operasi penyortiran mereka. Ini hanya asumsi yang buruk.
Untuk solusi seperti itu, siapa pun yang memiliki pengetahuan tentang bagaimana mereka melakukan kompresi mereka akan dapat merancang beberapa data input yang tidak kompres dengan baik untuk skema ini, dan "solusi" kemungkinan besar akan pecah karena kehabisan ruang.
Sebagai gantinya saya mengambil pendekatan matematika. Output kami yang mungkin adalah semua daftar panjang LEN yang terdiri dari elemen dalam kisaran 0..MAX. Di sini LEN adalah 1.000.000 dan MAX kami adalah 100.000.000.
Untuk LEN dan MAX sewenang-wenang, jumlah bit yang diperlukan untuk menyandikan keadaan ini adalah:
Log2 (MAX Multichoose LEN)
Jadi untuk angka-angka kami, setelah kami menyelesaikan penerimaan dan penyortiran, kami akan membutuhkan setidaknya Log2 (100.000.000 MC 1.000.000) bit untuk menyimpan hasil kami dengan cara yang secara unik dapat membedakan semua kemungkinan keluaran.
Ini ~ = 988kb . Jadi kami sebenarnya memiliki cukup ruang untuk menampung hasil kami. Dari sudut pandang ini, itu mungkin.
[Berceloteh tak berguna yang dihapus sekarang karena ada contoh yang lebih baik ...]
Jawaban terbaik ada di sini .
Jawaban lain yang baik ada di sini dan pada dasarnya menggunakan jenis penyisipan sebagai fungsi untuk memperluas daftar dengan satu elemen (buffer beberapa elemen dan pra-macam, untuk memungkinkan penyisipan lebih dari satu pada satu waktu, menghemat sedikit waktu). menggunakan pengkodean keadaan kompak yang bagus juga, ember delta tujuh bit
sumber
Misalkan tugas ini mungkin. Tepat sebelum output, akan ada representasi dalam memori dari jutaan angka yang diurutkan. Ada berapa banyak representasi yang berbeda? Karena mungkin ada angka yang berulang, kita tidak bisa menggunakan nCr (pilih), tetapi ada operasi yang disebut multichoose yang berfungsi pada multiset .
Jadi secara teori itu mungkin, jika Anda bisa mendapatkan representasi waras dari angka-angka yang diurutkan. Misalnya, representasi gila mungkin memerlukan tabel pencarian 10MB atau ribuan baris kode.
Namun, jika "1M RAM" berarti satu juta byte, maka jelas tidak ada ruang yang cukup. Fakta bahwa 5% lebih banyak memori memungkinkan secara teori menunjukkan kepada saya bahwa representasi harus SANGAT efisien dan mungkin tidak waras.
sumber
(Jawaban asli saya salah, maaf untuk matematika buruk, lihat di bawah ini.)
Bagaimana dengan ini?
27 bit pertama menyimpan angka terendah yang Anda lihat, kemudian perbedaan ke angka berikutnya terlihat, disandikan sebagai berikut: 5 bit untuk menyimpan jumlah bit yang digunakan dalam menyimpan perbedaan, lalu perbedaannya. Gunakan 00000 untuk menunjukkan bahwa Anda melihat nomor itu lagi.
Ini berfungsi karena semakin banyak angka yang dimasukkan, perbedaan rata-rata di antara angka-angka turun, jadi Anda menggunakan lebih sedikit bit untuk menyimpan perbedaan ketika Anda menambahkan lebih banyak angka. Saya percaya ini disebut daftar delta.
Kasus terburuk yang dapat saya pikirkan adalah semua angka diberi spasi secara merata (100), misalkan dengan asumsi 0 adalah angka pertama:
Reddit untuk menyelamatkan!
Jika yang harus Anda lakukan adalah mengurutkannya, masalah ini akan mudah. Dibutuhkan 122k (1 juta bit) untuk menyimpan angka yang telah Anda lihat (bit ke-0 jika 0 terlihat, bit ke-23 pada jika 2300 terlihat, dll.
Anda membaca angka-angka, menyimpannya di bidang bit, dan kemudian menggeser bit keluar sambil tetap menghitung.
TAPI, Anda harus ingat berapa banyak yang Anda lihat. Saya terinspirasi oleh jawaban sublist di atas untuk menghasilkan skema ini:
Alih-alih menggunakan satu bit, gunakan 2 atau 27 bit:
Saya pikir ini berfungsi: jika tidak ada duplikat, Anda memiliki daftar 244k. Dalam kasus terburuk Anda melihat setiap angka dua kali (jika Anda melihat satu angka tiga kali, itu mempersingkat sisa daftar untuk Anda), itu berarti Anda telah melihat 50.000 lebih dari sekali, dan Anda telah melihat 950.000 item 0 atau 1 kali.
50.000 * 27 + 950.000 * 2 = 396,7rb.
Anda dapat membuat peningkatan lebih lanjut jika Anda menggunakan penyandian berikut:
0 berarti Anda tidak melihat angka 10 berarti Anda melihatnya sekali 11 adalah cara Anda menghitung
Yang rata-rata akan menghasilkan penyimpanan 280,7k.
EDIT: Matematika hari Minggu pagi saya salah.
Kasus terburuk adalah kita melihat angka 500.000 dua kali, sehingga matematika menjadi:
500.000 * 27 + 500.000 * 2 = 1.77M
Pengkodean alternatif menghasilkan penyimpanan rata-rata
500.000 * 27 + 500.000 = 1,70M
: (
sumber
Ada satu solusi untuk masalah ini di semua input yang mungkin. Curang.
sumber
Saya akan mencoba Pohon Radix . Jika Anda bisa menyimpan data dalam pohon, Anda bisa melakukan lintasan in-order untuk mengirimkan data.
Saya tidak yakin Anda bisa memasukkan ini ke dalam 1MB, tapi saya pikir ini patut dicoba.
sumber
Komputer jenis apa yang Anda gunakan? Mungkin tidak memiliki penyimpanan lokal "normal" lainnya, tetapi apakah ada RAM video, misalnya? 1 megapiksel x 32 bit per piksel (katakanlah) cukup dekat dengan ukuran input data yang Anda butuhkan.
(Saya sebagian besar bertanya dalam memori Acorn RISC PC lama , yang dapat 'meminjam' VRAM untuk memperluas RAM sistem yang tersedia, jika Anda memilih mode layar resolusi rendah atau rendah warna!). Ini agak berguna pada mesin dengan hanya beberapa MB RAM normal.
sumber
Representasi pohon radix akan mendekati menangani masalah ini, karena pohon radix mengambil keuntungan dari "kompresi awalan". Tetapi sulit untuk membayangkan representasi pohon radix yang dapat mewakili satu node dalam satu byte - dua mungkin tentang batas.
Tapi, terlepas dari bagaimana data diwakili, setelah diurutkan itu dapat disimpan dalam bentuk awalan-terkompresi, di mana angka 10, 11, dan 12 akan diwakili oleh, katakanlah 001b, 001b, 001b, menunjukkan peningkatan 1 dari nomor sebelumnya. Mungkin, 10101b akan mewakili peningkatan 5, 1101001b peningkatan 9, dll.
sumber
Ada 10 ^ 6 nilai dalam kisaran 10 ^ 8, jadi ada satu nilai rata-rata per seratus poin kode. Menyimpan jarak dari titik N ke (N +1). Nilai duplikat memiliki lompatan 0. Ini berarti bahwa loncatan membutuhkan rata-rata hanya di bawah 7 bit untuk disimpan, jadi satu juta dari mereka akan dengan senang hati masuk ke dalam 8 juta bit penyimpanan kami.
Melompati ini perlu dikodekan ke dalam bitstream, katakanlah dengan pengkodean Huffman. Penyisipan adalah dengan mengulangi bitstream dan menulis ulang setelah nilai baru. Keluaran dengan mengulangi dan menuliskan nilai-nilai yang tersirat. Untuk kepraktisan, mungkin ingin dilakukan sebagai, katakanlah, 10 ^ 4 daftar yang mencakup 10 ^ 4 poin kode (dan rata-rata 100 nilai) masing-masing.
Pohon Huffman yang baik untuk data acak dapat dibangun apriori dengan mengasumsikan distribusi Poisson (rata-rata = varians = 100) pada panjang lompatan, tetapi statistik nyata dapat disimpan pada input dan digunakan untuk menghasilkan pohon optimal untuk berurusan dengan kasus patologis.
sumber
Cara lain untuk menipu: Anda dapat menggunakan penyimpanan non-lokal (jaringan) sebagai gantinya (pertanyaan Anda tidak menghalangi ini) dan memanggil layanan jaringan yang dapat menggunakan mergesort berbasis disk langsung (atau hanya cukup RAM untuk mengurutkan dalam memori, karena Anda hanya perlu menerima nomor 1M), tanpa perlu solusi (diakui sangat cerdik) yang sudah diberikan.
Ini mungkin curang, tetapi tidak jelas apakah Anda mencari solusi untuk masalah dunia nyata, atau teka-teki yang mengundang pembengkokan aturan ... jika yang terakhir, maka cheat sederhana mungkin mendapatkan hasil yang lebih baik daripada kompleks tetapi solusi "asli" (yang seperti yang ditunjukkan orang lain, hanya dapat bekerja untuk input yang dapat dikompresi).
sumber
Saya pikir solusinya adalah menggabungkan teknik dari video encoding, yaitu transformasi cosinus diskrit. Dalam video digital, alih-alih merekam perubahan kecerahan atau warna video sebagai nilai reguler seperti 110 112 115 116, masing-masing dikurangkan dari yang terakhir (mirip dengan pengodean panjang lari). 110 112 115 116 menjadi 110 2 3 1. Nilai-nilai, 2 3 1 membutuhkan lebih sedikit bit dari aslinya.
Jadi katakanlah kita membuat daftar nilai input saat mereka tiba di soket. Kami menyimpan di setiap elemen, bukan nilai, tetapi offset dari yang sebelumnya. Kami mengurutkan seiring berjalannya waktu, sehingga offset hanya akan menjadi positif. Tapi offset bisa 8 digit lebar desimal yang cocok dalam 3 byte. Setiap elemen tidak boleh 3 byte, jadi kita harus mengemasnya. Kita dapat menggunakan bit teratas dari setiap byte sebagai "terus bit", menunjukkan bahwa byte berikutnya adalah bagian dari angka dan 7 bit yang lebih rendah dari setiap byte perlu digabungkan. nol valid untuk duplikat.
Ketika daftar terisi, angka-angka harus semakin dekat, artinya rata-rata hanya 1 byte digunakan untuk menentukan jarak ke nilai berikutnya. 7 bit nilai dan 1 bit ofset jika nyaman, tetapi mungkin ada sweet spot yang membutuhkan kurang dari 8 bit untuk nilai "terus".
Lagi pula, saya melakukan beberapa percobaan. Saya menggunakan generator angka acak dan saya bisa memasukkan satu juta angka desimal diurutkan 8 digit menjadi sekitar 1279000 byte. Ruang rata-rata antara setiap angka secara konsisten 99 ...
sumber
Kita bisa bermain dengan tumpukan jaringan untuk mengirim nomor dalam urutan diurutkan sebelum kita memiliki semua nomor. Jika Anda mengirim data 1M, TCP / IP akan memecahnya menjadi paket 1500 byte dan mengalirkannya ke target. Setiap paket akan diberi nomor urut.
Kita bisa melakukan ini dengan tangan. Tepat sebelum kita mengisi RAM kita, kita dapat mengurutkan apa yang kita miliki dan mengirimkan daftar ke target kita tetapi meninggalkan lubang di urutan kami di sekitar masing-masing nomor. Kemudian proses 2 1/2 angka dengan cara yang sama menggunakan lubang-lubang di urutan.
Tumpukan jaringan di ujung jauh akan mengumpulkan aliran data yang dihasilkan dalam urutan urutan sebelum menyerahkannya ke aplikasi.
Itu menggunakan jaringan untuk melakukan semacam penggabungan. Ini adalah retasan total, tetapi saya terinspirasi oleh retasan jaringan lain yang tercantum sebelumnya.
sumber
Pendekatan Google (buruk), dari utas HN. Simpan hitungan gaya-RLE.
Masalah mereka tampaknya tidak mencakup duplikat, tetapi katakanlah mereka menggunakan "0: 1" untuk duplikat.
Masalah besar # 1: penyisipan untuk integer 1M akan memakan waktu lama .
Masalah besar # 2: seperti semua solusi pengkodean delta biasa, beberapa distribusi tidak dapat ditutup dengan cara ini. Misalnya, 1m bilangan bulat dengan jarak 0:99 (mis. Masing-masing +99). Sekarang pikirkan hal yang sama tetapi dengan jarak acak di kisaran 0:99 . (Catatan: 99999999/1000000 = 99,99)
Pendekatan Google tidak layak (lambat) dan salah. Tetapi untuk pertahanan mereka, masalah mereka mungkin sedikit berbeda.
sumber
Untuk mewakili array yang diurutkan kita bisa menyimpan elemen pertama dan perbedaan antara elemen yang berdekatan. Dengan cara ini kita peduli dengan pengkodean 10 ^ 6 elemen yang dapat meringkas paling banyak 10 ^ 8 Mari kita sebut ini D . Untuk mengkodekan elemen D, seseorang dapat menggunakan kode Huffman . Kamus untuk kode Huffman dapat dibuat di mana saja dan larik diperbarui setiap kali item baru dimasukkan dalam larik yang diurutkan (susunan penyisipan). Perhatikan bahwa ketika kamus berubah karena item baru seluruh array harus diperbarui agar sesuai dengan pengkodean baru.
Jumlah rata-rata bit untuk pengkodean setiap elemen D dimaksimalkan jika kita memiliki jumlah yang sama dari setiap elemen unik. Katakan elemen d1 , d2 , ..., dN dalam D masing-masing muncul F kali. Dalam hal itu (dalam kasus terburuk kita memiliki 0 dan 10 ^ 8 dalam urutan input) yang kita miliki
sum (1 <= i <= N ) F . di = 10 ^ 8
dimana
jumlah (1 <= i <= N ) F = 10 ^ 6, atau F = 10 ^ 6 / N dan frekuensi yang dinormalisasi adalah p = F / 10 ^ = 1 / N
Jumlah rata-rata bit adalah -log2 (1 / P ) = log2 ( N ). Dalam keadaan ini kita harus menemukan kasus yang memaksimalkan N . Ini terjadi jika kita memiliki angka berurutan untuk di mulai dari 0, atau, di = i -1
10 ^ 8 = sum (1 <= i <= N ) F . di = jumlah (1 <= i <= N ) (10 ^ 6 / N ) (i-1) = (10 ^ 6 / N ) N ( N -1) / 2
yaitu
N <= 201. Dan untuk kasus ini jumlah rata-rata bit adalah log2 (201) = 7.6511 yang berarti kita akan membutuhkan sekitar 1 byte per elemen input untuk menyimpan array yang diurutkan. Perhatikan bahwa ini tidak berarti D secara umum tidak dapat memiliki lebih dari 201 elemen. Ini hanya menabur bahwa jika elemen D didistribusikan secara seragam, itu tidak dapat memiliki lebih dari 201 nilai unik.
sumber
Saya akan mengeksploitasi perilaku pengiriman ulang TCP.
Ini mengasumsikan semacam manfaat dari ember atau beberapa lintasan.
Mungkin dengan menyortir bets / ember dan menggabungkannya. -> pohon radix
Gunakan teknik ini untuk menerima dan mengurutkan 80% pertama kemudian membaca 20% terakhir, verifikasi bahwa 20% terakhir tidak mengandung angka yang akan mendarat di 20% pertama dari angka terendah. Kemudian kirim 20% angka terendah, hapus dari memori, terima 20% sisanya dari angka baru dan gabungkan. **
sumber
Berikut ini adalah solusi umum untuk masalah seperti ini:
Prosedur umum
Pendekatan yang diambil adalah sebagai berikut. Algoritma ini beroperasi pada satu buffer kata-kata 32-bit. Ia melakukan prosedur berikut dalam satu lingkaran:
Kami mulai dengan buffer yang diisi dengan data terkompresi dari iterasi terakhir. Buffer terlihat seperti ini
|compressed sorted|empty|
Hitung jumlah maksimum angka yang dapat disimpan dalam buffer ini, baik terkompresi dan tidak terkompresi. Membagi buffer menjadi dua bagian ini, dimulai dengan ruang untuk data terkompresi, diakhiri dengan data yang tidak terkompresi. Buffer tampak seperti
|compressed sorted|empty|empty|
Isi bagian yang tidak dikompres dengan angka yang akan disortir. Buffer tampak seperti
|compressed sorted|empty|uncompressed unsorted|
Sortir nomor baru dengan jenis di tempat. Buffer tampak seperti
|compressed sorted|empty|uncompressed sorted|
Sejajarkan kanan data yang sudah dikompresi dari iterasi sebelumnya di bagian terkompresi. Pada titik ini buffer dipartisi
|empty|compressed sorted|uncompressed sorted|
Melakukan streaming dekompresi-rekompresi pada bagian terkompresi, menggabungkan data yang diurutkan di bagian terkompresi. Bagian terkompresi lama dikonsumsi saat bagian terkompresi baru tumbuh. Buffer tampak seperti
|compressed sorted|empty|
Prosedur ini dilakukan sampai semua nomor telah diurutkan.
Kompresi
Algoritme ini tentu saja hanya berfungsi jika dimungkinkan untuk menghitung ukuran terkompresi terakhir dari penyortiran penyortiran baru sebelum benar-benar mengetahui apa yang sebenarnya akan dikompresi. Di samping itu, algoritma kompresi harus cukup baik untuk menyelesaikan masalah yang sebenarnya.
Pendekatan yang digunakan menggunakan tiga langkah. Pertama, algoritma akan selalu menyimpan urutan yang diurutkan, oleh karena itu kami dapat menyimpan perbedaan antara entri yang berurutan. Setiap perbedaan berada dalam kisaran [0, 99999999].
Perbedaan-perbedaan ini kemudian dikodekan sebagai bitstream unary. A 1 dalam aliran ini berarti "Tambahkan 1 ke akumulator, A 0 berarti" Keluarkan akumulator sebagai entri, dan reset ". Jadi perbedaan N akan diwakili oleh N 1 dan satu 0.
Jumlah semua perbedaan akan mendekati nilai maksimum yang didukung algoritma, dan jumlah semua perbedaan akan mendekati jumlah nilai yang dimasukkan dalam algoritma. Ini berarti kami mengharapkan aliran, pada akhirnya, berisi nilai maksimum 1 dan jumlah 0. Ini memungkinkan kami untuk menghitung probabilitas yang diharapkan dari 0 dan 1 dalam aliran. Yaitu, probabilitas 0 adalah
count/(count+maxval)
dan probabilitas 1 adalahmaxval/(count+maxval)
.Kami menggunakan probabilitas ini untuk mendefinisikan model pengkodean aritmatika pada bitstream ini. Kode aritmatika ini akan menyandikan jumlah 1 dan 0 ini dalam ruang optimal. Kita bisa menghitung ruang yang digunakan oleh model ini untuk setiap bitstream menengah sebagai:
bits = encoded * log2(1 + amount / maxval) + maxval * log2(1 + maxval / amount)
. Untuk menghitung total ruang yang dibutuhkan untuk algoritma, setelencoded
sama dengan jumlah.Untuk tidak memerlukan jumlah iterasi yang konyol, overhead kecil dapat ditambahkan ke buffer. Ini akan memastikan bahwa algoritma setidaknya akan beroperasi pada jumlah angka yang sesuai dengan overhead ini, karena sejauh ini biaya waktu terbesar dari algoritma ini adalah kompresi pengkodean aritmatika dan dekompresi setiap siklus.
Di samping itu, beberapa overhead diperlukan untuk menyimpan data pembukuan dan untuk menangani sedikit ketidakakuratan dalam pendekatan titik tetap dari algoritma pengkodean aritmatika, tetapi secara total algoritma ini dapat masuk dalam ruang 1MiB bahkan dengan buffer tambahan yang dapat berisi 8000 angka, dengan total ruang 1043916 byte.
Optimalitas
Di luar mengurangi overhead (kecil) dari algoritma itu seharusnya secara teori tidak mungkin untuk mendapatkan hasil yang lebih kecil. Untuk hanya berisi entropi dari hasil akhir, 1011717 byte akan diperlukan. Jika kita mengurangi buffer tambahan yang ditambahkan untuk efisiensi, algoritma ini menggunakan 1011916 byte untuk menyimpan hasil akhir + overhead.
sumber
Jika aliran input dapat diterima beberapa kali ini akan jauh lebih mudah (tidak ada informasi tentang itu, ide dan masalah kinerja waktu).
Kemudian, kita bisa menghitung nilai desimal. Dengan nilai yang dihitung akan mudah untuk membuat aliran output. Kompres dengan menghitung nilai. Itu tergantung apa yang akan ada di aliran input.
sumber
Jika aliran input dapat diterima beberapa kali ini akan jauh lebih mudah (tidak ada info tentang itu, ide dan masalah kinerja waktu). Kemudian, kita bisa menghitung nilai desimal. Dengan nilai yang dihitung akan mudah untuk membuat aliran output. Kompres dengan menghitung nilai. Itu tergantung apa yang akan ada di aliran input.
sumber
Penyortiran adalah masalah sekunder di sini. Seperti kata yang lain, hanya menyimpan bilangan bulat itu sulit, dan tidak dapat bekerja pada semua input , karena diperlukan 27 bit per angka.
Pandangan saya tentang ini adalah: simpan hanya perbedaan antara integer (diurutkan) berurutan, karena kemungkinan besar kecil. Kemudian gunakan skema kompresi, misalnya dengan 2 bit tambahan per nomor input, untuk mengkodekan berapa banyak bit nomor disimpan. Sesuatu seperti:
Seharusnya dimungkinkan untuk menyimpan sejumlah daftar input yang mungkin dalam batasan memori yang diberikan. Matematika tentang cara memilih skema kompresi agar dapat bekerja pada jumlah input maksimum, berada di luar jangkauan saya.
Saya harap Anda dapat memanfaatkan pengetahuan khusus domain untuk input Anda untuk menemukan skema kompresi integer yang cukup baik berdasarkan ini.
Oh dan kemudian, Anda melakukan semacam penyisipan pada daftar yang diurutkan saat Anda menerima data.
sumber
Sekarang bertujuan untuk solusi yang sebenarnya, mencakup semua kemungkinan kasus input dalam kisaran 8 digit dengan hanya 1MB RAM. CATATAN: pekerjaan sedang berlangsung, besok akan dilanjutkan. Menggunakan kode aritmatika dari del int yang diurutkan, kasus terburuk untuk 1M int yang diurutkan akan menelan biaya sekitar 7 bit per entri (karena 99999999/1000000 adalah 99, dan log2 (99) hampir 7 bit).
Tetapi Anda membutuhkan integer 1m yang diurutkan untuk mendapatkan 7 atau 8 bit! Seri yang lebih pendek akan memiliki delta yang lebih besar, oleh karena itu lebih banyak bit per elemen.
Saya sedang berusaha mengambil sebanyak mungkin dan mengompres (hampir) di tempat. Batch pertama hampir 250K int akan membutuhkan sekitar 9 bit masing-masing di terbaik. Jadi hasilnya akan memakan waktu sekitar 275KB. Ulangi dengan sisa memori bebas beberapa kali. Kemudian dekompresi-gabungkan di tempat-kompres potongan terkompresi itu. Ini cukup sulit , tetapi mungkin. Kupikir.
Daftar yang digabungkan akan semakin dekat dan lebih dekat ke target 7bit per integer. Tapi saya tidak tahu berapa banyak iterasi yang dibutuhkan dari loop gabungan. Mungkin 3.
Tetapi ketidaktepatan implementasi kode aritmatika mungkin membuatnya tidak mungkin. Jika masalah ini mungkin terjadi, itu akan sangat ketat.
Ada sukarelawan?
sumber
Anda hanya perlu menyimpan perbedaan antara angka-angka dalam urutan, dan menggunakan pengodean untuk mengompres nomor-nomor urutan ini. Kami memiliki 2 ^ 23 bit. Kami akan membaginya menjadi potongan-potongan 6bit, dan biarkan bit terakhir menunjukkan apakah jumlahnya meluas ke 6 bit lainnya (5bit ditambah bilah yang memperpanjang).
Jadi, 000010 adalah 1, dan 000100 adalah 2. 000001100000 adalah 128. Sekarang, kami mempertimbangkan pemeran terburuk dalam mewakili perbedaan dalam urutan angka hingga 10.000.000. Mungkin ada 10.000.000 / 2 ^ 5 perbedaan lebih besar dari 2 ^ 5, 10.000.000 / 2 ^ 10 perbedaan lebih besar dari 2 ^ 10, dan 10.000.000 / 2 ^ 15 perbedaan lebih besar dari 2 ^ 15, dll.
Jadi, kami menambahkan berapa banyak bit yang diperlukan untuk mewakili urutan kami. Kami memiliki 1.000.000 * 6 + roundup (10.000.000 / 2 ^ 5) * 6 + roundup (10.000.000 / 2 ^ 10) * 6 + roundup (10.000.000 / 2 ^ 15) * 6 + roundup (10.000.000 / 2 ^ 20) * 4 = 7935479
2 ^ 24 = 8388608. Sejak 8388608> 7935479, kita seharusnya dengan mudah memiliki cukup memori. Kita mungkin perlu sedikit memori untuk menyimpan jumlah di mana kita memasukkan angka baru. Kami kemudian pergi melalui urutan, dan menemukan tempat untuk memasukkan nomor baru kami, mengurangi perbedaan berikutnya jika perlu, dan menggeser semuanya setelah itu benar.
sumber
Jika kami tidak tahu apa-apa tentang angka-angka itu, kami dibatasi oleh batasan-batasan berikut:
Jika asumsi ini berlaku, tidak ada cara untuk melakukan tugas Anda, karena Anda akan memerlukan setidaknya 26.575.425 bit penyimpanan (3.321.929 byte).
Apa yang bisa Anda ceritakan tentang data Anda?
sumber
Caranya adalah dengan mewakili keadaan algoritma, yang merupakan integer multi-set, sebagai aliran terkompresi dari "increment counter" = "+" dan "output counter" = "!" karakter. Misalnya, set {0,3,3,4} akan direpresentasikan sebagai "! +++ !! +!", Diikuti oleh sejumlah karakter "+". Untuk memodifikasi multi-set Anda mengalirkan karakter, menjaga jumlah yang konstan didekompresi pada satu waktu, dan membuat perubahan di tempat sebelum mengalirkannya kembali dalam bentuk terkompresi.
Detail
Kita tahu persis ada 10 ^ 6 angka di set terakhir, jadi paling banyak ada 10 ^ 6 "!" karakter. Kita juga tahu bahwa rentang kita memiliki ukuran 10 ^ 8, artinya paling banyak ada 10 ^ 8 "+" karakter. Jumlah cara kita dapat mengatur 10 ^ 6 "!" Di antara 10 ^ 8 "+" adalah
(10^8 + 10^6) choose 10^6
, dan dengan demikian menentukan beberapa pengaturan tertentu membutuhkan ~ 0,965 MiB `data. Itu akan menjadi sangat ketat.Kami dapat memperlakukan setiap karakter sebagai independen tanpa melebihi kuota kami. Tepatnya ada 100 kali lebih banyak "+" karakter daripada "!" karakter, yang menyederhanakan peluang 100: 1 dari setiap karakter menjadi "+" jika kita lupa bahwa mereka bergantung. Peluang 100: 101 sesuai dengan ~ 0,08 bit per karakter , untuk total yang hampir identik ~ 0,965 MiB (mengabaikan ketergantungan hanya membutuhkan biaya ~ 12 bit dalam kasus ini!).
Teknik paling sederhana untuk menyimpan karakter independen dengan probabilitas yang diketahui sebelumnya adalah pengkodean Huffman . Perhatikan bahwa kita memerlukan pohon besar yang tidak praktis (Sebuah pohon jagoan untuk blok 10 karakter memiliki biaya rata-rata per blok sekitar 2,4 bit, dengan total ~ 2,9 Mib. Pohon huffman untuk blok 20 karakter memiliki biaya rata-rata per blok sekitar 3 bit, yang merupakan total ~ 1,8 MiB. Kita mungkin akan membutuhkan satu blok ukuran pada urutan seratus, menyiratkan lebih banyak node di pohon kita daripada semua peralatan komputer yang pernah ada dapat menyimpan. ). Namun, ROM secara teknis "bebas" sesuai dengan masalah dan solusi praktis yang memanfaatkan keteraturan dalam pohon akan terlihat pada dasarnya sama.
Kode semu
sumber
Kami memiliki 1 MB - 3 KB RAM = 2 ^ 23 - 3 * 2 ^ 13 bit = 8388608 - 24576 = 8364032 bit tersedia.
Kami diberi 10 ^ 6 angka dalam kisaran 10 ^ 8. Ini memberikan jarak rata-rata ~ 100 <2 ^ 7 = 128
Pertama-tama mari kita pertimbangkan masalah yang lebih sederhana dari angka-angka dengan jarak yang cukup merata ketika semua celah <128. Ini mudah. Simpan saja angka pertama dan celah 7-bit:
(27 bit) + 10 ^ 6 Nomor 7-bit gap = dibutuhkan 7000027 bit
Catatan angka yang diulang memiliki celah 0.
Tetapi bagaimana jika kita memiliki celah lebih besar dari 127?
OK, katakanlah ukuran celah <127 diwakili secara langsung, tetapi ukuran celah 127 diikuti oleh pengodean 8-bit terus menerus untuk panjang celah aktual:
dll.
Perhatikan representasi nomor ini menjelaskan panjangnya sendiri sehingga kami tahu kapan nomor gap berikutnya dimulai.
Dengan celah kecil <127, ini masih membutuhkan 7000027 bit.
Mungkin ada hingga (10 ^ 8) / (2 ^ 7) = 781250 23-bit gap number, membutuhkan tambahan 16 * 781.250 = 12.500.000 bit yang terlalu banyak. Kita membutuhkan representasi kesenjangan yang lebih kompak dan perlahan-lahan meningkat.
Ukuran celah rata-rata adalah 100 jadi jika kita menyusun ulang mereka sebagai [100, 99, 101, 98, 102, ..., 2, 198, 1, 199, 0, 200, 201, 202, ...] dan indeks ini dengan pengkodean basis biner Fibonacci biner tanpa pasangan nol (misalnya, 11011 = 8 + 5 + 2 + 1 = 16) dengan angka dibatasi oleh '00' maka saya pikir kita dapat menjaga representasi celah cukup pendek, tetapi perlu analisis lebih lanjut.
sumber
Saat menerima aliran lakukan langkah-langkah ini.
1st mengatur beberapa ukuran chunk yang masuk akal
Gagasan Kode semu:
Lanjutkan 4 langkah pertama saat menerima aliran. Langkah terakhir adalah gagal jika Anda melebihi memori atau mulai mengeluarkan hasilnya setelah semua data dikumpulkan dengan mulai mengurutkan rentang dan memuntahkan hasilnya dalam urutan dan mengompresi mereka dalam urutan yang perlu dikompresi dan mengurutkan mereka saat Anda mendapatkan mereka.
sumber