Katakanlah Anda punya pesawat terbang, dan bahan bakarnya rendah. Kecuali jika pesawat menurunkan berat penumpang 3.000 pon, itu tidak akan dapat mencapai bandara berikutnya. Untuk menyelamatkan jumlah maksimum nyawa, kami ingin membuang orang-orang terberat dari pesawat terlebih dahulu.
Dan oh ya, ada jutaan orang di pesawat, dan kami ingin algoritma yang optimal untuk menemukan penumpang terberat, tanpa harus menyortir seluruh daftar.
Ini adalah masalah proxy untuk sesuatu yang saya coba kode dalam C ++. Saya ingin melakukan "partial_sort" pada manifes penumpang berdasarkan berat, tetapi saya tidak tahu berapa banyak elemen yang akan saya butuhkan. Saya dapat mengimplementasikan algoritma "partial_sort" saya sendiri ("partial_sort_accumulate_until"), tapi saya ingin tahu apakah ada cara yang lebih mudah untuk melakukan ini menggunakan STL standar.
Jawaban:
Salah satu caranya adalah menggunakan min heap (
std::priority_queue
dalam C ++). Begini cara Anda melakukannya, dengan asumsi Anda memilikiMinHeap
kelas. (Ya, contoh saya adalah dalam C #. Saya pikir Anda mendapatkan ide.)Menurut referensi standar, waktu berjalan harus sebanding dengan
n log k
, di manan
jumlah penumpang dank
jumlah maksimum barang di heap. Jika kita mengasumsikan bahwa berat penumpang biasanya 100 lbs atau lebih, maka tidak mungkin bahwa tumpukan akan mengandung lebih dari 30 item setiap saat.Kasus terburuk adalah jika penumpang disajikan dalam urutan dari berat terendah ke tertinggi. Itu akan mengharuskan setiap penumpang ditambahkan ke heap, dan setiap penumpang dikeluarkan dari heap. Namun, dengan satu juta penumpang dan dengan asumsi bahwa yang paling ringan berbobot 100 lbs, maka
n log k
berhasil sedikit.Jika Anda mendapatkan bobot penumpang secara acak, kinerjanya jauh lebih baik. Saya menggunakan sesuatu yang seperti ini untuk mesin rekomendasi (saya memilih 200 item teratas dari daftar beberapa juta). Saya biasanya berakhir dengan hanya 50.000 atau 70.000 item yang benar-benar ditambahkan ke heap.
Saya menduga Anda akan melihat sesuatu yang sangat mirip: mayoritas kandidat Anda akan ditolak karena mereka lebih ringan daripada orang yang paling ringan yang ada di tumpukan. Dan
Peek
merupakanO(1)
operasi.Untuk informasi lebih lanjut tentang kinerja tumpukan pilih dan pilih cepat, lihat Ketika teori bertemu praktik . Versi singkat: jika Anda memilih kurang dari 1% dari jumlah total item, maka heap pilih adalah pemenang yang jelas atas pemilihan cepat. Lebih dari 1%, lalu gunakan pilih cepat atau varian seperti Introselect .
sumber
std::priority_queue
Namun, ini tidak akan membantu untuk masalah proxy Anda:
Untuk 1.000.000 penumpang untuk menurunkan berat 3000 pon, setiap penumpang harus kehilangan (3000/1000000) = 0,003 lbs per orang. Itu bisa dicapai dengan membuang setiap baju, atau sepatu, atau bahkan mungkin kliping kuku, menyelamatkan semua orang. Ini mengasumsikan pengumpulan dan pembuangan yang efisien sebelum penurunan berat yang dibutuhkan meningkat karena pesawat menggunakan lebih banyak bahan bakar.
Sebenarnya, mereka tidak mengizinkan gunting kuku di papan lagi, jadi itu keluar.
sumber
Di bawah ini adalah implementasi sederhana dari solusi langsung. Saya tidak berpikir ada cara yang lebih cepat yaitu 100% benar.
Ini bekerja dengan mengisi set "orang mati" sampai memenuhi ambang batas. Setelah ambang batas terpenuhi, kami terus menelusuri daftar penumpang yang berusaha menemukan yang lebih berat daripada orang mati paling ringan. Ketika kami telah menemukan satu, kami menambahkan mereka ke daftar dan kemudian mulai "Menyimpan" orang-orang paling ringan dari daftar sampai kami tidak dapat menyimpan lagi.
Dalam kasus terburuk, ini akan melakukan hampir sama dengan semacam seluruh daftar. Tetapi dalam kasus terbaik ("daftar mati" diisi dengan benar dengan orang X pertama) itu akan tampil
O(n)
.sumber
total
sebelahcontinue;
Selain itu, ini adalah jawaban yang akan saya posting. Solusi super cepatDengan asumsi semua penumpang akan bekerja sama: Gunakan jaringan penyortiran paralel . (lihat juga ini )
Ini demonstrasi langsungPerbarui: Video alternatif (lompat ke 1:00)
Meminta pasangan orang untuk membandingkan-pertukaran - Anda tidak bisa lebih cepat dari ini.
sumber
n
prosesor tidak berlaku.@ Blastfurnace berada di jalur yang benar. Anda menggunakan pilihan cepat di mana pivot adalah ambang batas berat. Setiap partisi membagi satu set orang menjadi set, dan mengembalikan bobot total untuk setiap set orang. Anda terus memecahkan ember yang sesuai sampai ember Anda yang sesuai dengan berat tertinggi orang lebih dari 3000 pound, dan ember terendah Anda yang ada di set itu memiliki 1 orang (artinya, tidak dapat dibagi lebih jauh.)
Algoritma ini adalah waktu linier diamortisasi, tetapi kasus terburuk kuadratik. Saya pikir itu adalah satu-satunya algoritma waktu linier .
Berikut adalah solusi Python yang menggambarkan algoritma ini:
Keluaran:
sumber
Dengan asumsi bahwa, seperti bobot orang, Anda memiliki ide bagus tentang apa nilai maksimum dan minimum yang mungkin akan menggunakan jenis radix untuk mengurutkannya dalam O (n). Maka cukup bekerja dari ujung terberat dari daftar menuju yang paling ringan. Total waktu berjalan: O (n). Sayangnya, tidak ada implementasi semacam radix di STL, tetapi cukup mudah untuk menulis.
sumber
Mengapa Anda tidak menggunakan quicksort parsial dengan aturan aborsi yang berbeda dari "diurutkan". Anda dapat menjalankannya dan kemudian menggunakan hanya bagian yang lebih tinggi dan terus sampai berat dalam bagian yang lebih tinggi ini tidak mengandung berat yang setidaknya harus dibuang lagi, daripada Anda kembali satu langkah dalam rekursi dan mengurutkan daftar. Setelah itu Anda dapat mulai mengusir orang-orang dari ujung atas daftar yang diurutkan.
sumber
Semacam Turnamen Paralel Massively: -
Dengan asumsi tiga kursi standar setiap sisi ailse: -
Mintalah penumpang di kursi dekat untuk pindah ke kursi tengah jika mereka lebih berat daripada orang di kursi dekat.
Mintalah penumpang di kursi tengah untuk bertukar dengan penumpang di kursi lorong jika mereka lebih berat.
Minta penumpang di kursi lorong kiri untuk bertukar dengan penumpang di kursi kanan, mereka lebih berat.
Bubble mengurutkan penumpang di kursi lorong kanan. (Mengambil n langkah untuk n baris). - minta penumpang di kursi lorong kanan untuk bertukar dengan orang di depan dan -1 kali.
5 Tendang mereka hingga mencapai £ 3000.
3 langkah + n langkah ditambah 30 langkah jika Anda memiliki penumpang yang benar-benar kurus.
Untuk pesawat dua lorong - instruksinya lebih kompleks tetapi kinerjanya hampir sama.
sumber
Saya mungkin akan menggunakan
std::nth_element
untuk mempartisi 20 orang terberat dalam waktu linier. Kemudian gunakan metode yang lebih kompleks untuk menemukan dan menghantam yang terberat dari yang terberat.sumber
Anda dapat membuat satu melewati daftar untuk mendapatkan rata-rata dan standar deviasi, kemudian menggunakannya untuk memperkirakan jumlah orang yang harus pergi. Gunakan partial_sort untuk menghasilkan daftar berdasarkan nomor itu. Jika tebakannya rendah, gunakan partial_sort lagi pada sisanya dengan tebakan baru.
sumber
@James memiliki jawaban di komentar: a
std::priority_queue
jika Anda dapat menggunakan wadah apa pun, atau kombinasi daristd::make_heap
danstd::pop_heap
(danstd::push_heap
) jika Anda ingin menggunakan sesuatu seperti astd::vector
.sumber
Berikut ini adalah solusi berbasis tumpukan menggunakan modul heapq bawaan Python. Itu di Python jadi tidak menjawab pertanyaan asli, tapi itu lebih bersih (IMHO) daripada solusi Python diposting lainnya.
Jika k = jumlah penumpang untuk dilemparkan dan N = jumlah penumpang, maka kasus terbaik untuk algoritma ini adalah O (N) dan kasus terburuk untuk algoritma ini adalah Nlog (N). Kasus terburuk terjadi jika k berada di dekat N untuk waktu yang lama. Berikut adalah contoh pemeran terburuk:
Namun, dalam hal ini (membuang orang dari pesawat (dengan parasut, saya kira)) maka k harus kurang dari 3000, yaitu << "jutaan orang". Karena itu, runtime rata-rata seharusnya sekitar Nlog (k), yang linier dengan jumlah orang.
sumber