Saya baru-baru ini menghadiri sebuah wawancara di mana saya diminta "menulis sebuah program untuk menemukan 100 angka terbesar dari array 1 miliar angka."
Saya hanya bisa memberikan solusi brute force yang mengurutkan array dalam kompleksitas waktu O (nlogn) dan mengambil 100 angka terakhir.
Arrays.sort(array);
Pewawancara mencari kompleksitas waktu yang lebih baik, saya mencoba beberapa solusi lain tetapi gagal menjawabnya. Apakah ada solusi kompleksitas waktu yang lebih baik?
O(1)
dalam kasus ini, karena tidak ada peningkatan dimensi. Pewawancara seharusnya bertanya "Bagaimana menemukan elemen m terbesar dari array n dengan n >> m?".Jawaban:
Anda dapat menyimpan antrian prioritas dari 100 angka terbesar, beralih melalui miliaran angka, setiap kali Anda menemukan angka lebih besar dari angka terkecil dalam antrian (kepala antrian), hapus kepala antrian dan tambahkan nomor baru ke antrian.
EDIT: seperti yang dicatat Dev, dengan antrian prioritas diimplementasikan dengan heap, kompleksitas penyisipan ke antrian adalah
O(logN)
Dalam kasus terburuk Anda mendapatkan yang lebih baik daripada
billionlog2(100)
billion
log2(billion)
Secara umum, jika Anda membutuhkan angka K terbesar dari satu set angka N, kompleksitasnya
O(NlogK)
bukanO(NlogN)
, ini bisa sangat signifikan ketika K sangat kecil dibandingkan dengan N.EDIT2:
Waktu yang diharapkan dari algoritma ini cukup menarik, karena di setiap iterasi sebuah penyisipan mungkin atau mungkin tidak terjadi. Probabilitas nomor ke-i yang akan dimasukkan ke dalam antrian adalah probabilitas dari variabel acak yang lebih besar daripada setidaknya
i-K
variabel acak dari distribusi yang sama (angka k pertama secara otomatis ditambahkan ke antrian). Kita dapat menggunakan statistik pesanan (lihat tautan ) untuk menghitung probabilitas ini. Misalnya, mari kita asumsikan angka-angka dipilih secara acak secara seragam dari{0, 1}
, nilai yang diharapkan dari nomor (iK) nomor th (dari angka i) adalah(i-k)/i
, dan peluang variabel acak menjadi lebih besar dari nilai ini1-[(i-k)/i] = k/i
.Dengan demikian, jumlah penyisipan yang diharapkan adalah:
Dan waktu berjalan yang diharapkan dapat dinyatakan sebagai:
(
k
waktu untuk menghasilkan antrian dengank
elemen pertama , lalun-k
perbandingan, dan jumlah penyisipan yang diharapkan seperti yang dijelaskan di atas, masing-masing membutuhkanlog(k)/2
waktu rata-rata )Perhatikan bahwa ketika
N
sangat besar dibandingkanK
, ungkapan ini jauh lebih dekatn
daripadaNlogK
. Ini agak intuitif, seperti dalam kasus pertanyaan, bahkan setelah 10.000 iterasi (yang sangat kecil dibandingkan dengan satu miliar), peluang nomor untuk dimasukkan ke antrian sangat kecil.sumber
k
konstan dan kecil dibandingkan dengann
. Padahal, orang harus selalu mengingat ini "keadaan normal".Jika ini diminta dalam sebuah wawancara, saya pikir pewawancara mungkin ingin melihat proses penyelesaian masalah Anda, bukan hanya pengetahuan Anda tentang algoritma.
Deskripsi ini cukup umum sehingga mungkin Anda bisa menanyakan kisaran atau arti angka-angka ini untuk memperjelas masalahnya. Melakukan hal ini dapat mengesankan pewawancara. Jika, misalnya, angka-angka ini mewakili usia orang dalam suatu negara (misalnya Cina), maka itu adalah masalah yang jauh lebih mudah. Dengan asumsi yang masuk akal bahwa tidak ada yang hidup lebih tua dari 200, Anda dapat menggunakan array int ukuran 200 (mungkin 201) untuk menghitung jumlah orang dengan usia yang sama hanya dalam satu iterasi. Di sini indeks berarti usia. Setelah ini sepotong kue untuk menemukan 100 jumlah terbesar. Ngomong-ngomong, algo ini disebut penghitungan .
Bagaimanapun, membuat pertanyaan lebih spesifik dan jelas lebih baik untuk Anda dalam sebuah wawancara.
sumber
Anda dapat mengulangi angka yang mengambil O (n)
Setiap kali Anda menemukan nilai lebih besar dari minimum saat ini, tambahkan nilai baru ke antrian melingkar dengan ukuran 100.
Minimum antrian melingkar itu adalah nilai perbandingan baru Anda. Terus tambahkan ke antrian itu. Jika penuh, ekstrak minimum dari antrian.
sumber
Saya menyadari bahwa ini ditandai dengan 'algoritma', tetapi akan membuang beberapa opsi lain, karena mungkin juga harus ditandai 'wawancara'.
Apa sumber angka 1 miliar? Jika ini adalah database maka 'pilih nilai dari urutan tabel dengan nilai batas 100' akan melakukan pekerjaan dengan cukup baik - mungkin ada perbedaan dialek.
Apakah ini satu kali, atau sesuatu yang akan diulang? Jika diulang, seberapa sering? Jika hanya satu kali dan datanya ada di file, maka 'cat srcfile | sortir (opsi sesuai kebutuhan) | Head -100 'akan membuat Anda dengan cepat melakukan pekerjaan produktif yang dibayar untuk Anda saat komputer menangani tugas sepele ini.
Jika diulangi, Anda akan menyarankan memilih pendekatan yang layak untuk mendapatkan jawaban awal dan menyimpan / menyimpan hasilnya sehingga Anda dapat terus melaporkan 100 teratas.
Akhirnya, ada pertimbangan ini. Apakah Anda mencari pekerjaan entry level dan wawancara dengan manajer culun atau rekan kerja di masa depan? Jika demikian, maka Anda dapat membuang segala macam pendekatan yang menggambarkan pro dan kontra teknis relatif. Jika Anda mencari pekerjaan yang lebih manajerial, maka dekati seperti layaknya seorang manajer, yang peduli dengan biaya pengembangan dan pemeliharaan solusi, dan katakan "terima kasih banyak" dan tinggalkan jika itu adalah pewawancara ingin fokus pada hal-hal sepele CS . Dia dan Anda tidak akan memiliki banyak potensi kemajuan di sana.
Semoga beruntung di wawancara selanjutnya.
sumber
Reaksi langsung saya untuk ini adalah menggunakan heap, tetapi ada cara untuk menggunakan QuickSelect tanpa menyimpan semua nilai input pada satu waktu.
Buat array ukuran 200 dan isi dengan 200 nilai input pertama. Jalankan QuickSelect dan buang 100 yang rendah, meninggalkan Anda dengan 100 tempat gratis. Baca di 100 nilai input berikutnya dan jalankan QuickSelect lagi. Lanjutkan sampai Anda telah menjalankan seluruh input dalam batch 100.
Pada akhirnya Anda memiliki 100 nilai teratas. Untuk nilai N Anda telah menjalankan QuickSelect sekitar N / 100 kali. Setiap pilihan Quickselect sekitar 200 kali beberapa konstan, sehingga total biaya 2N kali beberapa konstan. Ini terlihat linier dalam ukuran input untuk saya, terlepas dari ukuran parameter yang saya perkirakan menjadi 100 dalam penjelasan ini.
sumber
partial_sort
dijalankan langsung pada set data 200 juta 32-bitint
(dibuat melalui MT19937, terdistribusi secara merata).Ordering.greatestOf(Iterable, int)
. Ini benar-benar linear-waktu dan single-pass, dan ini adalah algoritma yang sangat lucu. FWIW, kami juga memiliki beberapa tolok ukur yang sebenarnya: faktor konstannya lebih lambat dibandingkan antrian prioritas tradisional dalam kasus rata-rata, tetapi implementasi ini jauh lebih tahan terhadap input "kasus terburuk" (misalnya input yang naik secara ketat).Anda dapat menggunakan algoritme pilih cepat untuk menemukan nomor di indeks (berdasarkan pesanan) [miliar-101] dan kemudian beralih di atas angka-angka dan untuk menemukan angka yang lebih besar dari angka itu.
Algoritma ini Waktu adalah: 2 XO (N) = O (N) (Kinerja kasus rata-rata)
Opsi kedua seperti yang disarankan Thomas Jungblut adalah:
Gunakan Heap membangun heap MAX akan mengambil O (N), maka angka-angka max 100 teratas akan berada di atas Heap, yang Anda butuhkan adalah mengeluarkannya dari heap (100 XO (Log (N)).
Algoritma ini Waktu adalah: O (N) + 100 XO (Log (N)) = O (N)
sumber
O(N)
, melakukan dua QuickSelects dan pemindaian linier lainnya jauh lebih mahal daripada yang dibutuhkan.100*O(N)
(jika itu sintaks yang valid) =O(100*N)
=O(N)
(diakui 100 mungkin variabel, jika demikian, ini tidak sepenuhnya benar). Oh, dan Quickselect memiliki kinerja kasus terburuk O (N ^ 2) (aduh). Dan jika itu tidak sesuai dengan memori, Anda akan memuat ulang data dari disk dua kali, yang jauh lebih buruk daripada sekali (ini adalah hambatannya).Meskipun solusi quickselect lainnya telah diturunkan, faktanya tetap bahwa quickselect akan menemukan solusi lebih cepat daripada menggunakan antrian ukuran 100. Quickselect memiliki waktu berjalan 2n + o (n) yang diharapkan, dalam hal perbandingan. Implementasinya sangat sederhana
Ini akan membutuhkan perbandingan 3n + o (n) rata-rata. Selain itu, dapat dibuat lebih efisien menggunakan fakta bahwa quickselect akan meninggalkan 100 item terbesar dalam array di 100 lokasi paling kanan. Jadi pada kenyataannya, waktu berjalan dapat ditingkatkan menjadi 2n + o (n).
Ada masalah bahwa ini diharapkan waktu berjalan, dan bukan kasus terburuk, tetapi dengan menggunakan strategi pemilihan pivot yang layak (mis. Pilih 21 elemen secara acak, dan pilih median 21 elemen tersebut sebagai pivot), maka jumlah perbandingan dapat dijamin dengan probabilitas tinggi paling banyak (2 + c) n untuk konstanta kecil sewenang-wenang c.
Bahkan, dengan menggunakan strategi pengambilan sampel yang dioptimalkan (misalnya sampel sqrt (n) elemen secara acak, dan pilih persentil ke-99), waktu berjalan dapat diturunkan ke (1 + c) n + o (n) untuk c (dengan asumsi K, jumlah elemen yang dipilih adalah o (n)).
Di sisi lain, menggunakan antrian ukuran 100 akan membutuhkan perbandingan O (log (100) n), dan basis log 2 dari 100 kira-kira sama dengan 6,6.
Jika kita memikirkan masalah ini dalam arti yang lebih abstrak dalam memilih elemen K terbesar dari array ukuran N, di mana K = o (N) tetapi keduanya K dan N pergi hingga tak terbatas, maka waktu berjalan versi quickselect akan menjadi O (N) dan versi antriannya adalah O (N log K), jadi dalam hal ini pemilihan cepat juga lebih baik secara asimptotik.
Dalam komentar, disebutkan bahwa solusi antrian akan berjalan dalam waktu yang diharapkan N + K log N pada input acak. Tentu saja, asumsi input acak tidak pernah valid kecuali jika pertanyaan menyatakannya secara eksplisit. Solusi antrian dapat dibuat untuk melintasi array dalam urutan acak, tetapi ini akan menimbulkan biaya tambahan panggilan N ke generator nomor acak serta membolehkan seluruh array input atau mengalokasikan array baru panjang N yang berisi indeks acak.
Jika masalahnya tidak memungkinkan Anda untuk bergerak di sekitar elemen-elemen dalam array asli, dan biaya mengalokasikan memori tinggi sehingga menduplikasi array bukanlah suatu pilihan, itu masalah yang berbeda. Tetapi hanya dalam hal menjalankan waktu, ini adalah solusi terbaik.
sumber
ambil 100 angka pertama dari miliar dan urutkan mereka. sekarang hanya beralih melalui miliar, jika nomor sumber lebih tinggi dari yang terkecil dari 100, masukkan dalam urutan. Yang akhirnya Anda dapatkan adalah sesuatu yang jauh lebih dekat dengan O (n) di atas ukuran set.
sumber
Dua pilihan:
(1) Heap (priorityQueue)
Pertahankan tumpukan min dengan ukuran 100. Lintasi array. Setelah elemen lebih kecil dari elemen pertama di tumpukan, gantilah.
(2) Model pengurangan peta.
Ini sangat mirip dengan contoh jumlah kata dalam hadoop. Pekerjaan peta: hitung frekuensi atau waktu setiap elemen yang muncul. Kurangi: Dapatkan elemen K atas.
Biasanya, saya akan memberikan dua jawaban kepada perekrut. Beri mereka apa pun yang mereka suka. Tentu saja, peta mengurangi kode akan menjadi tenaga kerja-beberapa karena Anda harus tahu setiap parameter yang tepat. Tidak ada ruginya mempraktikkannya. Semoga berhasil.
sumber
Solusi yang sangat mudah adalah dengan mengulangi array 100 kali. Yang mana
O(n)
.Setiap kali Anda mengeluarkan angka terbesar (dan mengubah nilainya ke nilai minimum, sehingga Anda tidak melihatnya di iterasi berikutnya, atau melacak indeks dari jawaban sebelumnya (dengan melacak indeks, array asli dapat memiliki kelipatan dari nomor yang sama)). Setelah 100 iterasi, Anda memiliki 100 angka terbesar.
sumber
Terinspirasi oleh jawaban teller @ron, berikut adalah program barebones C untuk melakukan apa yang Anda inginkan.
Pada mesin saya (core i3 dengan SSD cepat) dibutuhkan 25 detik, dan 1724 macam. Saya membuat file biner
dd if=/dev/urandom/ count=1000000000 bs=1
untuk menjalankan ini.Jelas, ada masalah kinerja dengan hanya membaca 4 byte pada suatu waktu - dari disk, tapi ini demi contoh. Di sisi positifnya, sangat sedikit memori yang dibutuhkan.
sumber
Solusi paling sederhana adalah memindai miliaran angka array besar dan tahan 100 nilai terbesar yang ditemukan sejauh ini dalam buffer array kecil tanpa penyortiran dan ingat nilai terkecil buffer ini. Pertama saya pikir metode ini diusulkan oleh fordpfect tetapi dalam komentar dia mengatakan bahwa dia mengasumsikan struktur data nomor 100 sedang dilaksanakan sebagai heap. Setiap kali nomor baru ditemukan yang lebih besar maka minimum dalam buffer ditimpa oleh nilai baru yang ditemukan dan buffer dicari untuk minimum saat ini lagi. Jika angka-angka dalam miliar array angka didistribusikan secara acak sebagian besar waktu nilai dari array besar dibandingkan dengan minimum array kecil dan dibuang. Hanya untuk fraksi angka yang sangat kecil nilai harus dimasukkan ke dalam array kecil. Jadi perbedaan memanipulasi struktur data yang memegang angka-angka kecil dapat diabaikan. Untuk sejumlah kecil elemen sulit untuk menentukan apakah penggunaan antrian prioritas sebenarnya lebih cepat daripada menggunakan pendekatan naif saya.
Saya ingin memperkirakan jumlah sisipan dalam buffer array elemen 100 kecil ketika array elemen 10 ^ 9 dipindai. Program memindai 1000 elemen pertama dari array besar ini dan harus memasukkan paling banyak 1000 elemen dalam buffer. Buffer berisi 100 elemen dari 1000 elemen yang dipindai, yaitu 0,1 dari elemen yang dipindai. Jadi kita mengasumsikan bahwa probabilitas bahwa nilai dari array besar lebih besar dari minimum buffer saat ini adalah sekitar 0,1. Elemen seperti itu harus dimasukkan dalam buffer. Sekarang program memindai 10 ^ 4 elemen berikutnya dari array besar. Karena minimum buffer akan meningkat setiap kali elemen baru dimasukkan. Kami memperkirakan bahwa rasio elemen yang lebih besar dari minimum kami saat ini adalah sekitar 0,1 sehingga ada 0,1 * 10 ^ 4 = 1000 elemen yang akan disisipkan. Sebenarnya jumlah elemen yang diharapkan yang dimasukkan ke buffer akan lebih kecil. Setelah pemindaian ini 10 ^ 4 elemen fraksi dari angka dalam buffer akan menjadi sekitar 0,01 dari elemen yang dipindai sejauh ini. Jadi ketika memindai 10 ^ 5 angka berikutnya kita mengasumsikan bahwa tidak lebih dari 0,01 * 10 ^ 5 = 1000 akan dimasukkan ke dalam buffer. Melanjutkan argumentasi ini kami telah menyisipkan sekitar 7000 nilai setelah memindai 1000 + 10 ^ 4 + 10 ^ 5 + ... + 10 ^ 9 ~ 10 ^ 9 elemen array besar. Jadi ketika memindai array dengan 10 ^ 9 elemen ukuran acak kami berharap tidak lebih dari 10 ^ 4 (= 7000 dibulatkan) penyisipan dalam buffer. Setelah setiap penyisipan ke buffer, minimum baru harus ditemukan. Jika buffer adalah array sederhana, kita perlu 100 perbandingan untuk menemukan minimum baru. Jika buffer adalah struktur data lain (seperti heap) kita perlu setidaknya 1 perbandingan untuk menemukan minimum. Untuk membandingkan elemen-elemen array besar kita perlu perbandingan 10 ^ 9. Jadi semuanya membutuhkan sekitar 10 ^ 9 + 100 * 10 ^ 4 = 1,001 * 10 ^ 9 perbandingan ketika menggunakan array sebagai buffer dan setidaknya 1.000 * 10 ^ 9 perbandingan ketika menggunakan tipe lain dari struktur data (seperti heap) . Jadi menggunakan heap hanya membawa keuntungan sebesar 0,1% jika kinerja ditentukan oleh jumlah perbandingan. Tapi apa perbedaan waktu eksekusi antara memasukkan elemen ke dalam tumpukan 100 elemen dan mengganti elemen dalam array elemen 100 dan menemukan minimum baru? 000 * 10 ^ 9 perbandingan saat menggunakan tipe lain dari struktur data (seperti heap). Jadi menggunakan heap hanya membawa keuntungan sebesar 0,1% jika kinerja ditentukan oleh jumlah perbandingan. Tapi apa perbedaan waktu eksekusi antara memasukkan elemen ke dalam tumpukan 100 elemen dan mengganti elemen dalam array elemen 100 dan menemukan minimum baru? 000 * 10 ^ 9 perbandingan saat menggunakan tipe lain dari struktur data (seperti heap). Jadi menggunakan heap hanya membawa keuntungan sebesar 0,1% jika kinerja ditentukan oleh jumlah perbandingan. Tapi apa perbedaan waktu eksekusi antara memasukkan elemen ke dalam tumpukan 100 elemen dan mengganti elemen dalam array elemen 100 dan menemukan minimum baru?
Pada tingkat teoretis: Berapa banyak perbandingan yang diperlukan untuk memasukkan tumpukan. Saya tahu itu O (log (n)) tetapi seberapa besar faktor konstannya? saya
Di tingkat mesin: Apa dampak caching dan prediksi cabang pada waktu eksekusi heap insert dan pencarian linear dalam array.
Di tingkat implementasi: Biaya tambahan apa yang disembunyikan dalam struktur tumpukan data yang disediakan oleh perpustakaan atau kompiler?
Saya pikir ini adalah beberapa pertanyaan yang harus dijawab sebelum seseorang dapat mencoba memperkirakan perbedaan nyata antara kinerja tumpukan elemen 100 atau array elemen 100. Jadi masuk akal untuk melakukan percobaan dan mengukur kinerja nyata.
sumber
Algoritma x elemen terbesar dari n:
Aku akan memanggil kembali nilai LIST . Ini adalah sekumpulan elemen x (menurut saya daftar yang harus ditautkan)
Jadi, apa skenario terburuknya?
x log (x) + (nx) (log (x) +1) = nlog (x) + n - x
Jadi itu adalah O (n) waktu untuk kasus terburuk. +1 adalah memeriksa apakah nomor lebih besar dari yang terkecil di LIST. Waktu yang diharapkan untuk kasus rata-rata akan tergantung pada distribusi matematika dari n elemen tersebut.
Kemungkinan peningkatan
Algoritma ini dapat sedikit ditingkatkan untuk skenario terburuk tetapi IMHO (saya tidak dapat membuktikan klaim ini) yang akan menurunkan perilaku rata-rata. Perilaku asimptotik akan sama.
Peningkatan dalam algoritme ini adalah bahwa kami tidak akan memeriksa apakah elemen lebih besar dari terkecil. Untuk setiap elemen kami akan mencoba memasukkannya dan jika lebih kecil dari yang terkecil kami akan mengabaikannya. Meskipun itu terdengar tidak masuk akal jika kita hanya menganggap skenario terburuk yang akan kita miliki
x log (x) + (nx) log (x) = nlog (x)
operasi.
Untuk kasus penggunaan ini saya tidak melihat peningkatan lebih lanjut. Namun Anda harus bertanya pada diri sendiri - bagaimana jika saya harus melakukan ini lebih dari log (n) kali dan untuk x-es yang berbeda? Jelas kita akan mengurutkan array itu dalam O (n log (n)) dan mengambil elemen x kita kapan pun kita membutuhkannya.
sumber
Pertanyaan ini akan dijawab dengan kompleksitas N log (100) (bukan N log N) dengan hanya satu baris kode C ++.
Jawaban akhir akan menjadi vektor di mana 100 elemen pertama dijamin menjadi 100 jumlah terbesar dari array Anda, sedangkan elemen yang tersisa tidak diurutkan
C ++ STL (library standar) cukup berguna untuk masalah seperti ini.
Catatan: Saya tidak mengatakan bahwa ini adalah solusi optimal, tetapi itu akan menyelamatkan wawancara Anda.
sumber
Solusi sederhana akan menggunakan antrian prioritas, menambahkan 100 nomor pertama ke antrian dan melacak nomor terkecil dalam antrian, kemudian mengulangi melalui miliar angka lainnya, dan setiap kali kami menemukan satu yang lebih besar dari jumlah terbesar dalam antrian prioritas, kami menghapus nomor terkecil, menambahkan nomor baru, dan lagi melacak nomor terkecil dalam antrian.
Jika angka-angka itu dalam urutan acak, ini akan bekerja dengan indah karena ketika kita beralih melalui satu miliar angka acak, akan sangat jarang bahwa angka berikutnya adalah di antara 100 terbesar sejauh ini. Tetapi jumlahnya mungkin tidak acak. Jika array sudah diurutkan dalam urutan menaik maka kami akan selalu memasukkan elemen ke antrian prioritas.
Jadi kita pilih katakan 100.000 angka acak dari array terlebih dahulu. Untuk menghindari akses acak yang mungkin lambat, kami menambahkan katakan 400 grup acak dengan 250 angka berurutan. Dengan pemilihan acak itu, kita dapat yakin bahwa sangat sedikit dari angka yang tersisa berada di atas seratus, sehingga waktu pelaksanaan akan sangat dekat dengan loop sederhana yang membandingkan satu miliar angka dengan beberapa nilai maksimum.
sumber
Menemukan 100 teratas dari satu miliar angka paling baik dilakukan dengan menggunakan min-heap dari 100 elemen.
Pertama perdana min-heap dengan 100 angka pertama ditemui. min-heap akan menyimpan yang terkecil dari 100 angka pertama di root (atas).
Sekarang saat Anda melanjutkan sisa angka hanya membandingkannya dengan root (terkecil dari 100).
Jika nomor baru yang ditemui lebih besar dari root min-heap, ganti root dengan angka itu jika tidak, abaikan.
Sebagai bagian dari penyisipan nomor baru di min-heap, angka terkecil di heap akan datang ke atas (root).
Setelah kita melewati semua angka, kita akan memiliki 100 angka terbesar di tumpukan-min.
sumber
Saya telah menulis solusi sederhana dengan Python jika ada yang tertarik. Ia menggunakan
bisect
modul dan daftar pengembalian sementara yang terus disortir. Ini mirip dengan implementasi antrian prioritas.Penggunaan dengan 100.000.000 elemen dan input kasus terburuk yang merupakan daftar yang diurutkan:
Butuh sekitar 40 detik untuk menghitung ini untuk 100.000.000 elemen jadi saya takut melakukannya untuk 1 miliar. Agar adil, saya memberinya input kasus terburuk (ironisnya array yang sudah diurutkan).
sumber
Saya melihat banyak diskusi O (N), jadi saya mengusulkan sesuatu yang berbeda hanya untuk latihan pemikiran.
Adakah informasi yang diketahui tentang sifat angka-angka ini? Jika sifatnya acak, maka jangan melangkah lebih jauh dan lihat jawaban lainnya. Anda tidak akan mendapatkan hasil yang lebih baik daripada mereka.
Namun! Lihat apakah mekanisme daftar-populasi apa pun mengisi daftar itu dalam urutan tertentu. Apakah mereka dalam pola yang terdefinisi dengan baik di mana Anda dapat mengetahui dengan pasti bahwa besaran angka terbesar akan ditemukan di wilayah tertentu dari daftar atau pada interval tertentu? Mungkin ada pola untuk itu. Jika demikian, misalnya jika mereka dijamin berada dalam semacam distribusi normal dengan punuk karakteristik di tengah, selalu memiliki tren berulang di antara himpunan bagian yang ditetapkan, memiliki lonjakan yang berkepanjangan pada suatu waktu T di tengah data ditetapkan seperti mungkin insiden insider trading atau kegagalan peralatan, atau mungkin hanya memiliki "lonjakan" setiap angka ke-N seperti dalam analisis kekuatan setelah bencana, Anda dapat mengurangi jumlah catatan yang harus Anda periksa secara signifikan.
Ada beberapa makanan untuk dipikirkan pula. Mungkin ini akan membantu Anda memberikan pewawancara masa depan jawaban yang bijaksana. Saya tahu saya akan terkesan jika seseorang bertanya kepada saya pertanyaan seperti itu dalam menanggapi masalah seperti ini - itu akan memberitahu saya bahwa mereka berpikir untuk optimasi. Cukup ketahuilah bahwa tidak selalu ada kemungkinan untuk mengoptimalkan.
sumber
Buat daftar kosong 100 slot kosong
Untuk setiap nomor dalam daftar input:
Jika angkanya lebih kecil dari yang pertama, lewati
Kalau tidak gantikan dengan nomor ini
Kemudian, dorong nomor tersebut melalui swap yang berdekatan; sampai lebih kecil dari yang berikutnya
Kembalikan daftar
Catatan: jika
log(input-list.size) + c < 100
, maka cara optimal adalah mengurutkan daftar input, kemudian bagi 100 item pertama.sumber
Kompleksitasnya adalah O (N)
Pertama buat array 100 ints inisialisasi elemen pertama array ini sebagai elemen pertama dari nilai N, melacak indeks elemen saat ini dengan variabel lain, sebut saja CurrentBig
Iterate melalui nilai-nilai N.
ketika selesai, cetak array M dari CurrentBig 100 kali modulo 100 :-) Untuk siswa: pastikan baris terakhir kode tidak membuat data yang benar tepat sebelum kode keluar
sumber
Algoritma O (n) lain -
Algoritma menemukan 100 terbesar dengan eliminasi
pertimbangkan semua juta angka dalam representasi biner mereka. Mulai dari yang paling signifikan. Menemukan apakah MSB adalah 1 dapat dilakukan dengan perkalian operasi boolean dengan angka yang sesuai. Jika ada lebih dari 100 1 dalam jutaan ini hilangkan angka lainnya dengan nol. Sekarang dari angka yang tersisa lanjutkan dengan bit paling signifikan berikutnya. simpan hitungan jumlah angka yang tersisa setelah eliminasi dan lanjutkan selama jumlah ini lebih besar dari 100.
Operasi boolean utama dapat dilakukan secara pararel pada GPU
sumber
Saya akan mencari tahu siapa yang punya waktu untuk menempatkan satu miliar angka ke dalam array dan memecatnya. Harus bekerja untuk pemerintah. Setidaknya jika Anda memiliki daftar tertaut, Anda dapat memasukkan nomor ke tengah tanpa memindahkan setengah miliar untuk membuat ruang. Btree yang lebih baik memungkinkan pencarian biner. Setiap perbandingan menghilangkan setengah dari total Anda. Algoritma hash akan memungkinkan Anda untuk mengisi struktur data seperti kotak-kotak tetapi tidak begitu baik untuk data yang jarang. Karena ini adalah taruhan terbaik Anda adalah memiliki array solusi 100 integer dan melacak nomor terendah dalam array solusi Anda sehingga Anda dapat menggantinya ketika Anda menemukan nomor yang lebih tinggi di array asli. Anda harus melihat setiap elemen dalam array asli dengan asumsi itu tidak diurutkan untuk memulai.
sumber
Anda dapat melakukannya
O(n)
tepat waktu. Hanya beralih melalui daftar dan melacak 100 angka terbesar yang pernah Anda lihat pada titik tertentu dan nilai minimum dalam grup itu. Ketika Anda menemukan nomor baru yang lebih besar dari yang terkecil dari sepuluh Anda, maka gantilah dan perbarui nilai min Anda yang baru dari 100 (mungkin butuh waktu konstan 100 untuk menentukan ini setiap kali Anda melakukannya, tetapi ini tidak mempengaruhi analisis keseluruhan ).sumber
Mengelola daftar terpisah adalah pekerjaan ekstra dan Anda harus memindahkan berbagai hal di seluruh daftar setiap kali Anda menemukan pengganti lain. Cukup qsort dan ambil 100 teratas.
sumber
Harap dicatat esp. langkah kedua mungkin mudah untuk dihitung secara paralel! Dan itu juga akan efisien ketika Anda membutuhkan sejuta elemen terbesar.
sumber
Ini pertanyaan dari Google atau raksasa industri lainnya. Mungkin kode berikut ini adalah jawaban yang tepat yang diharapkan oleh pewawancara Anda. Biaya waktu dan biaya ruang tergantung pada jumlah maksimum dalam array input. Untuk input array int 32-Bit, biaya ruang maksimum adalah 4 * 125M Bytes, biaya waktu adalah 5 * Miliar.
sumber
saya melakukan kode saya sendiri, tidak yakin apakah itu yang "pewawancara" itu cari
sumber
Kemungkinan peningkatan.
Jika file berisi 1 miliar nomor, membacanya bisa sangat panjang ...
Untuk meningkatkan kinerja ini, Anda dapat:
sumber
Pertama ambil 1000 elemen dan tambahkan mereka dalam tumpukan maksimal. Sekarang ambil maks 100 elemen pertama dan simpan di suatu tempat. Sekarang pilih 900 elemen berikutnya dari file dan tambahkan mereka di tumpukan bersama dengan 100 elemen tertinggi terakhir.
Terus ulangi proses ini mengambil 100 elemen dari heap dan menambahkan 900 elemen dari file.
Pilihan akhir 100 elemen akan memberi kita maksimal 100 elemen dari satu miliar angka.
sumber
Masalah: Temukan elemen terbesar dari n item di mana n >>> m
Solusi paling sederhana, yang harus jelas bagi semua orang adalah dengan hanya melakukan beberapa m dari algoritma semacam gelembung.
lalu cetak n elemen terakhir dari array.
Ini tidak memerlukan struktur data eksternal, dan menggunakan algoritma yang semua orang tahu.
Perkiraan waktu berjalan adalah O (m * n). Jawaban terbaik sejauh ini adalah O (n log (m)), jadi solusi ini tidak jauh lebih mahal untuk m kecil.
Saya tidak mengatakan ini tidak dapat diperbaiki, tetapi ini adalah solusi paling sederhana.
sumber