Tulis program untuk menemukan 100 angka terbesar dari array 1 miliar angka

300

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?

userx
sumber
70
Mungkin masalahnya adalah itu bukan pertanyaan pemilahan , tetapi pertanyaan pencarian .
geomagas
11
Sebagai catatan teknis, sort mungkin bukan cara terbaik untuk menyelesaikan masalah, tapi saya rasa itu bukan kekerasan - saya bisa memikirkan cara yang jauh lebih buruk untuk melakukannya.
Bernhard Barker
88
Saya hanya memikirkan metode brute force yang bahkan lebih bodoh ... Temukan semua kombinasi yang mungkin dari 100 elemen dari array 1 miliar elemen dan lihat kombinasi mana yang memiliki jumlah terbesar.
Shashank
10
Perhatikan bahwa semua algoritme deterministik (dan benar) ada O(1)dalam kasus ini, karena tidak ada peningkatan dimensi. Pewawancara seharusnya bertanya "Bagaimana menemukan elemen m terbesar dari array n dengan n >> m?".
Bakuriu
3
Kemungkinan rangkap dari Mengambil 100 angka teratas dari seratus juta angka
Adrian McCarthy

Jawaban:

328

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 adalahO(logN)

Dalam kasus terburuk Anda mendapatkan yang lebih baik daripadabillionlog2(100)billionlog2(billion)

Secara umum, jika Anda membutuhkan angka K terbesar dari satu set angka N, kompleksitasnya O(NlogK)bukan O(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-Kvariabel 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 ini 1-[(i-k)/i] = k/i.

Dengan demikian, jumlah penyisipan yang diharapkan adalah:

masukkan deskripsi gambar di sini

Dan waktu berjalan yang diharapkan dapat dinyatakan sebagai:

masukkan deskripsi gambar di sini

( kwaktu untuk menghasilkan antrian dengan kelemen pertama , lalu n-kperbandingan, dan jumlah penyisipan yang diharapkan seperti yang dijelaskan di atas, masing-masing membutuhkan log(k)/2waktu rata-rata )

Perhatikan bahwa ketika Nsangat besar dibandingkan K, ungkapan ini jauh lebih dekat ndaripada NlogK. 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.

Ron Teller
sumber
6
Ini sebenarnya hanya O (100) untuk setiap sisipan.
MrSmith42
8
@RonTeller Anda tidak dapat mencari biner daftar tertaut secara efisien, itu sebabnya antrian prioritas biasanya diimplementasikan dengan heap. Waktu penyisipan Anda seperti yang dijelaskan adalah O (n) bukan O (logn). Anda sudah benar pertama kali (antrian dipesan atau antrian prioritas) sampai Skizz membuat Anda menebak sendiri.
Dev
17
@ThomasJungblut miliar juga konstan, jadi jika itu masalahnya O (1): P
Ron Teller
9
@RonTeller: biasanya pertanyaan seperti ini dikhawatirkan seperti menemukan 10 halaman teratas dari milyaran hasil pencarian Google, atau 50 kata yang paling sering untuk kata cloud, atau 10 lagu paling populer di MTV, dll. Jadi, saya percaya, dalam keadaan normal aman untuk dipertimbangkan k konstan dan kecil dibandingkan dengan n. Padahal, orang harus selalu mengingat ini "keadaan normal".
Berteman
5
Karena Anda memiliki item 1G, sampel 1000 elemen secara acak, dan pilih 100 yang terbesar. Itu harus menghindari kasus degenerasi (diurutkan, mundur diurutkan, sebagian besar diurutkan), mengurangi jumlah sisipan.
ChuckCottrill
136

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.

jin
sumber
26
Poin yang sangat bagus. Tidak ada orang lain yang bertanya atau mengindikasikan apa pun tentang distribusi angka-angka itu - bisa membuat semua perbedaan dalam cara mendekati masalah.
NealB
13
Saya ingin jawaban ini cukup untuk memperpanjangnya. Baca angka satu kali untuk mendapatkan nilai minimum / maksimum sehingga Anda dapat mengasumsikan distribusi. Kemudian, ambil satu dari dua opsi. Jika rentangnya cukup kecil, buatlah array di mana Anda bisa langsung mengecek angka ketika angka itu muncul. Jika rentang terlalu besar, gunakan algoritma tumpukan diurutkan yang dibahas di atas .... Hanya sebuah pemikiran.
Richard_G
2
Saya setuju, mengajukan pertanyaan kembali kepada pewawancara memang membuat banyak perbedaan. Bahkan, pertanyaan seperti apakah Anda dibatasi oleh kekuatan komputasi atau tidak juga dapat membantu Anda memparalelkan solusi dengan menggunakan beberapa node komputasi.
Sumit Nigam
1
@R_G Tidak perlu melalui seluruh daftar. Cukup untuk mencicipi sebagian kecil (misalnya, satu juta) anggota acak dari daftar untuk mendapatkan statistik yang berguna.
Itamar
Bagi mereka yang tidak akan memikirkan solusi itu, saya akan merekomendasikan untuk membaca tentang jenis penghitungan en.wikipedia.org/wiki/Counting_sort . Itu sebenarnya pertanyaan wawancara yang cukup umum: dapatkah Anda mengurutkan array dengan lebih baik daripada O (nlogn). Pertanyaan ini hanya perpanjangan.
Maxime Chéramy
69

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.

Regenschein
sumber
3
Ini tidak berfungsi. mis. temukan 2 teratas dari {1, 100, 2, 99} akan memberikan {100,1} sebagai 2 teratas.
Skizz
7
Anda tidak dapat berkeliling untuk menahan antrian diurutkan. (jika Anda tidak ingin mencari lubang antrian setiap saat untuk elemen terkecil berikutnya)
MrSmith42
3
@ MrSmith42 Penyortiran sebagian, seperti tumpukan, sudah cukup. Lihat jawaban Ron Teller.
Christopher Creutzig
1
Ya, saya diam-diam berasumsi bahwa ekstrak-min-antrian diimplementasikan sebagai heap.
Regenschein
Alih-alih menggunakan antrian bundar min tumpukan 100, ini akan memiliki minimal seratus nomor di atas. Ini hanya akan membutuhkan O (log n) untuk dimasukkan dibandingkan dengan o (n) dalam hal antrian
techExplorer
33

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.

Fred Mitchell
sumber
2
Jawaban luar biasa. Semua orang memusatkan perhatian pada sisi teknis dari pertanyaan, sementara respons ini menangani bagian sosial bisnis dari pertanyaan itu.
vbocan
2
Saya tidak pernah membayangkan Anda bisa mengucapkan terima kasih dan meninggalkan wawancara dan tidak menunggu sampai selesai. Terima kasih telah membuka pikiran saya.
UrsulRosu
1
Mengapa kita tidak dapat membuat tumpukan miliar elemen dan mengekstrak 100 elemen terbesar. Dengan cara ini biaya = O (miliar) + 100 * O (log (miliar)) ??
Mohit Shah
17

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.

McDowella
sumber
10
Anda dapat menambahkan optimasi kecil tapi mungkin penting: Setelah menjalankan QuickSelect untuk mempartisi array ukuran 200, minimum 100 elemen teratas diketahui. Kemudian, ketika iterasi seluruh set data, hanya mengisi nilai 100 yang lebih rendah jika nilai saat ini lebih besar dari minimum saat ini. Implementasi sederhana dari algoritma ini dalam C ++ setara dengan libstdc ++ partial_sortdijalankan langsung pada set data 200 juta 32-bit int(dibuat melalui MT19937, terdistribusi secara merata).
dyp
1
Ide bagus - tidak memengaruhi analisis kasus terburuk tetapi terlihat layak dilakukan.
mcdowella
@ McDowella Ini patut dicoba dan saya akan melakukannya, terima kasih!
userx
8
Inilah yang dilakukan Guava 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).
Louis Wasserman
15

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.

array={...the billion numbers...} 
result[100];

pivot=QuickSelect(array,billion-101);//O(N)

for(i=0;i<billion;i++)//O(N)
   if(array[i]>=pivot)
      result.add(array[i]);

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)

One Man Crew
sumber
8
Anda sedang mengerjakan seluruh daftar tiga kali. 1 bio. bilangan bulat kira-kira 4GB, apa yang akan Anda lakukan jika Anda tidak dapat memasukkannya ke dalam memori? pilihan cepat adalah pilihan terburuk yang mungkin dalam kasus ini. Iterasi sekali dan menjaga tumpukan 100 item teratas adalah IMHO solusi terbaik di O (n) (perhatikan bahwa Anda dapat memotong O (log n) dari tumpukan tumpukan karena n dalam tumpukan adalah 100 = konstan = sangat kecil ).
Thomas Jungblut
3
Meskipun masih O(N), melakukan dua QuickSelects dan pemindaian linier lainnya jauh lebih mahal daripada yang dibutuhkan.
Kevin
Ini adalah kode PSEUDO, semua solusi di sini akan membutuhkan lebih banyak waktu (O (NLOG (N) atau 100 * O (N))
One Man Crew
1
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).
Bernhard Barker
Ada masalah bahwa ini diharapkan waktu berjalan, dan bukan kasus terburuk, tetapi dengan menggunakan strategi pemilihan poros yang layak (mis. Pilih 21 elemen secara acak, dan pilih median 21 elemen tersebut sebagai poros), maka jumlah perbandingan dapat dijamin dengan probabilitas tinggi paling banyak (2 + c) n untuk konstanta kecil sewenang-wenang c.
One Man Crew
10

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

array = input array of length n
r = Quickselect(array,n-100)
result = array of length 100
for(i = 1 to n)
  if(array[i]>r)
     add array[i] to result

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.

mrip
sumber
4
Paragraf terakhir Anda adalah poin utama: dengan satu miliar angka, tidak mungkin menyimpan semua data dalam memori atau untuk bertukar elemen. (Setidaknya begitulah cara saya menafsirkan masalah, mengingat itu adalah pertanyaan wawancara.)
Ted Hopp
14
Dalam pertanyaan algoritmik apa pun, jika membaca data merupakan masalah, itu harus disebutkan dalam pertanyaan. Pertanyaannya menyatakan "diberikan array" tidak "diberikan array pada disk yang tidak sesuai dalam memori dan tidak dapat dimanipulasi sesuai dengan model von neuman yang merupakan standar dalam analisis algoritma". Hari-hari ini Anda bisa mendapatkan laptop dengan ram 8gigs. Saya tidak yakin dari mana ide memegang satu miliar angka dalam memori tidak layak berasal. Saya memiliki beberapa miliar angka dalam memori di workstation saya sekarang.
mrip
FYI runtime kasus terburuk dari quickselect adalah O (n ^ 2) (lihat en.wikipedia.org/wiki/Quickselect ), dan juga memodifikasi urutan elemen dalam array input. Dimungkinkan untuk memiliki solusi O (n) kasus terburuk, dengan konstanta yang sangat besar ( en.wikipedia.org/wiki/Median_of_medians ).
Poin
Kasus terburuk dari quickselect secara eksponensial tidak mungkin terjadi, yang berarti bahwa untuk tujuan praktis ini tidak relevan. Mudah untuk memilih cepat sehingga dengan probabilitas tinggi jumlah perbandingan adalah (2 + c) n + o (n) untuk kecil sewenang-wenang c.
mrip
"Faktanya tetap bahwa pemilihan cepat akan menemukan solusi lebih cepat daripada menggunakan antrian ukuran 100" - Tidak. Solusi heap membutuhkan perbandingan N + Klog (N) versus rata-rata 2N untuk quickselect dan 2,95 untuk Median of Median. Ini jelas lebih cepat untuk K. yang diberikan.
Neil G
5

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.

Samuel Thurston
sumber
3
oops tidak melihat jawaban yang lebih mendetail daripada jawaban saya.
Samuel Thurston
Ambil 500 angka pertama dan hanya berhenti untuk menyortir (dan membuang yang rendah 400) ketika daftar terisi. (Dan tak usah dikatakan bahwa Anda kemudian hanya menambah daftar jika nomor baru> yang terendah dalam 100 yang dipilih.)
Hot Licks
4

Dua pilihan:

(1) Heap (priorityQueue)

Pertahankan tumpukan min dengan ukuran 100. Lintasi array. Setelah elemen lebih kecil dari elemen pertama di tumpukan, gantilah.

InSERT ELEMENT INTO HEAP: O(log100)
compare the first element: O(1)
There are n elements in the array, so the total would be O(nlog100), which is O(n)

(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.

Chris Su
sumber
+1 untuk MapReduce, saya tidak percaya Anda adalah satu-satunya yang menyebutkan Hadoop untuk satu miliar angka. Bagaimana jika pewawancara meminta angka 1k miliar? Anda layak mendapat suara lebih banyak menurut saya.
Silviu Burcea
@ Silviu Burcea Terima kasih banyak. Saya menghargai MapReduce juga. :)
Chris Su
Meskipun ukuran 100 adalah konstan dalam contoh ini, Anda harus benar-benar menggeneralisasi ini ke variabel terpisah yaitu. k. Karena 100 sama konstannya dengan 1 miliar, jadi mengapa Anda memberi ukuran set angka besar variabel ukuran n, dan bukan untuk set angka yang lebih kecil? Sungguh kompleksitas Anda harus O (nlogk) yang bukan O (n).
Tom Heard
1
Tetapi poin saya adalah jika Anda hanya menjawab pertanyaan, 1 miliar juga diperbaiki dalam pertanyaan, jadi mengapa menggeneralisasi 1 miliar ke n dan bukan 100 ke k. Dengan mengikuti logika Anda, kompleksitasnya seharusnya menjadi O (1) karena 1 miliar dan 100 diperbaiki dalam pertanyaan ini.
Tom Heard
1
@ TomHeard Baiklah. O (nlogk) Hanya ada satu faktor yang akan mempengaruhi hasil. Ini berarti, jika n meningkat lebih besar dan lebih besar, "tingkat hasil" akan meningkat secara linear. Atau bisa dikatakan, meski diberi angka triliun, saya masih bisa mendapatkan 100 angka terbesar. Namun, Anda tidak bisa mengatakan: Dengan bertambahnya n, k bertambah sehingga k akan memengaruhi hasilnya. Itu sebabnya saya menggunakan O (nlogk) tetapi tidak O (nlogn)
Chris Su
4

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.

James Oravec
sumber
1
Dua kelemahan - (1) Anda menghancurkan input dalam proses - ini sebaiknya dihindari. (2) Anda akan melewati array beberapa kali - jika array disimpan pada disk dan tidak dapat masuk ke memori, ini bisa dengan mudah hampir 100 kali lebih lambat dari jawaban yang diterima. (Ya, mereka berdua O (n), tapi tetap saja)
Bernhard Barker
Panggilan bagus @Dukeling, saya menambahkan kata-kata tambahan tentang cara menghindari mengubah input asli dengan melacak indeks jawaban sebelumnya. Yang mana masih cukup mudah untuk dikodekan.
James Oravec
Contoh cemerlang dari solusi O (n) yang jauh lebih lambat daripada O (n log n). log2 (1 miliar) hanya 30 ...
gnasher729
@ gnasher729 Seberapa besar konstanta yang tersembunyi di O (n log n)?
miracle173
1

Terinspirasi oleh jawaban teller @ron, berikut adalah program barebones C untuk melakukan apa yang Anda inginkan.

#include <stdlib.h>
#include <stdio.h>

#define TOTAL_NUMBERS 1000000000
#define N_TOP_NUMBERS 100

int 
compare_function(const void *first, const void *second)
{
    int a = *((int *) first);
    int b = *((int *) second);
    if (a > b){
        return 1;
    }
    if (a < b){
        return -1;
    }
    return 0;
}

int 
main(int argc, char ** argv)
{
    if(argc != 2){
        printf("please supply a path to a binary file containing 1000000000"
               "integers of this machine's wordlength and endianness\n");
        exit(1);
    }
    FILE * f = fopen(argv[1], "r");
    if(!f){
        exit(1);
    }
    int top100[N_TOP_NUMBERS] = {0};
    int sorts = 0;
    for (int i = 0; i < TOTAL_NUMBERS; i++){
        int number;
        int ok;
        ok = fread(&number, sizeof(int), 1, f);
        if(!ok){
            printf("not enough numbers!\n");
            break;
        }
        if(number > top100[0]){
            sorts++;
            top100[0] = number;
            qsort(top100, N_TOP_NUMBERS, sizeof(int), compare_function);
        }

    }
    printf("%d sorts made\n"
    "the top 100 integers in %s are:\n",
    sorts, argv[1] );
    for (int i = 0; i < N_TOP_NUMBERS; i++){
        printf("%d\n", top100[i]);
    }
    fclose(f);
    exit(0);
}

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=1untuk 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
1

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.

keajaiban173
sumber
1
Itulah yang dilakukan tumpukan.
Neil G
@ Neil G: Apa "itu"?
miracle173
1
Bagian atas tumpukan adalah elemen minimum di tumpukan, dan elemen baru ditolak dengan satu perbandingan.
Neil G
1
Saya mengerti apa yang Anda katakan, tetapi bahkan jika Anda menggunakan jumlah perbandingan absolut daripada jumlah perbandingan asimptotik, array masih jauh lebih lambat karena waktu untuk "memasukkan elemen baru, membuang minimum lama, dan menemukan minimum baru" adalah 100 daripada sekitar 7.
Neil G
1
Oke, tetapi perkiraan Anda sangat bundaran. Anda dapat langsung menghitung jumlah sisipan yang diharapkan menjadi k (digamma (n) - digamma (k)), yang kurang dari klog (n). Bagaimanapun, solusi heap dan array hanya menghabiskan satu perbandingan untuk membuang elemen. Satu-satunya perbedaan adalah jumlah perbandingan untuk elemen yang dimasukkan adalah 100 untuk solusi Anda dibandingkan dengan 14 untuk heap (meskipun kasus rata-rata mungkin jauh lebih sedikit.)
Neil G
1
 Although in this question we should search for top 100 numbers, I will 
 generalize things and write x. Still, I will treat x as constant value.

Algoritma x elemen terbesar dari n:

Aku akan memanggil kembali nilai LIST . Ini adalah sekumpulan elemen x (menurut saya daftar yang harus ditautkan)

  • Elemen x pertama diambil dari kumpulan "saat mereka datang" dan diurutkan dalam LIST (ini dilakukan dalam waktu konstan karena x diperlakukan sebagai konstanta - O (x log (x)) waktu)
  • Untuk setiap elemen yang berikutnya kita periksa apakah itu lebih besar dari elemen terkecil di LIST dan jika kita mengeluarkan yang terkecil dan memasukkan elemen saat ini ke LIST. Karena itu adalah daftar yang diurutkan, setiap elemen harus menemukan tempatnya dalam waktu logaritmik (pencarian biner) dan karena itu adalah daftar yang dipesan, penyisipan tidak menjadi masalah. Setiap langkah juga dilakukan dalam waktu konstan (O (log (x)) waktu).

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.

Rouz
sumber
1

Pertanyaan ini akan dijawab dengan kompleksitas N log (100) (bukan N log N) dengan hanya satu baris kode C ++.

 std::vector<int> myvector = ...; // Define your 1 billion numbers. 
                                 // Assumed integer just for concreteness 
 std::partial_sort (myvector.begin(), myvector.begin()+100, myvector.end());

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.

Vivian Miranda
sumber
1

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.

gnasher729
sumber
1

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.

imsaar
sumber
0

Saya telah menulis solusi sederhana dengan Python jika ada yang tertarik. Ia menggunakan bisectmodul dan daftar pengembalian sementara yang terus disortir. Ini mirip dengan implementasi antrian prioritas.

import bisect

def kLargest(A, k):
    '''returns list of k largest integers in A'''
    ret = []
    for i, a in enumerate(A):
        # For first k elements, simply construct sorted temp list
        # It is treated similarly to a priority queue
        if i < k:
            bisect.insort(ret, a) # properly inserts a into sorted list ret
        # Iterate over rest of array
        # Replace and update return array when more optimal element is found
        else:
            if a > ret[0]:
                del ret[0] # pop min element off queue
                bisect.insort(ret, a) # properly inserts a into sorted list ret
    return ret

Penggunaan dengan 100.000.000 elemen dan input kasus terburuk yang merupakan daftar yang diurutkan:

>>> from so import kLargest
>>> kLargest(range(100000000), 100)
[99999900, 99999901, 99999902, 99999903, 99999904, 99999905, 99999906, 99999907,
 99999908, 99999909, 99999910, 99999911, 99999912, 99999913, 99999914, 99999915,
 99999916, 99999917, 99999918, 99999919, 99999920, 99999921, 99999922, 99999923,
 99999924, 99999925, 99999926, 99999927, 99999928, 99999929, 99999930, 99999931,
 99999932, 99999933, 99999934, 99999935, 99999936, 99999937, 99999938, 99999939,
 99999940, 99999941, 99999942, 99999943, 99999944, 99999945, 99999946, 99999947,
 99999948, 99999949, 99999950, 99999951, 99999952, 99999953, 99999954, 99999955,
 99999956, 99999957, 99999958, 99999959, 99999960, 99999961, 99999962, 99999963,
 99999964, 99999965, 99999966, 99999967, 99999968, 99999969, 99999970, 99999971,
 99999972, 99999973, 99999974, 99999975, 99999976, 99999977, 99999978, 99999979,
 99999980, 99999981, 99999982, 99999983, 99999984, 99999985, 99999986, 99999987,
 99999988, 99999989, 99999990, 99999991, 99999992, 99999993, 99999994, 99999995,
 99999996, 99999997, 99999998, 99999999]

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).

Shashank
sumber
0

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.

djdanlib
sumber
0
Time ~ O(100 * N)
Space ~ O(100 + N)
  1. Buat daftar kosong 100 slot kosong

  2. 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

  3. Kembalikan daftar


Catatan: jika log(input-list.size) + c < 100, maka cara optimal adalah mengurutkan daftar input, kemudian bagi 100 item pertama.

Khaled.K
sumber
0

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.

if N[i] > M[CurrentBig] {

M[CurrentBig]=N[i]; ( overwrite the current value with the newly found larger number)

CurrentBig++;      ( go to the next position in the M array)

CurrentBig %= 100; ( modulo arithmetic saves you from using lists/hashes etc.)

M[CurrentBig]=N[i];    ( pick up the current value again to use it for the next Iteration of the N array)

} 

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

Angelos Karageorgiou
sumber
0

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

Panduranga Rao Sadhu
sumber
0

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.

David Allan Houser Jr
sumber
0

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 ).

James Oravec
sumber
1
Pendekatan ini hampir identik dengan jawaban yang paling banyak dan yang paling banyak dipilih untuk pertanyaan ini.
Bernhard Barker
0

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.

Chris Fox
sumber
-1 quicksort adalah O (n log n) yang persis seperti apa yang OP lakukan dan minta diperbaiki. Anda tidak perlu mengelola daftar terpisah, hanya daftar 100 angka. Saran Anda juga memiliki efek samping yang tidak diinginkan dari mengubah daftar asli, atau menyalinnya. Itu 4GiB atau lebih dari memori, hilang.
0
  1. Gunakan elemen-n untuk mendapatkan elemen ke-100 O (n)
  2. Ulangi kedua kalinya tetapi hanya sekali dan hasilkan setiap elemen yang lebih besar dari elemen spesifik ini.

Harap dicatat esp. langkah kedua mungkin mudah untuk dihitung secara paralel! Dan itu juga akan efisien ketika Anda membutuhkan sejuta elemen terbesar.

matematika
sumber
0

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.

public class TopNumber {
    public static void main(String[] args) {
        final int input[] = {2389,8922,3382,6982,5231,8934
                            ,4322,7922,6892,5224,4829,3829
                            ,6892,6872,4682,6723,8923,3492};
        //One int(4 bytes) hold 32 = 2^5 value,
        //About 4 * 125M Bytes
        //int sort[] = new int[1 << (32 - 5)];
        //Allocate small array for local test
        int sort[] = new int[1000];
        //Set all bit to 0
        for(int index = 0; index < sort.length; index++){
            sort[index] = 0;
        }
        for(int number : input){
            sort[number >>> 5] |= (1 << (number % 32));
        }
        int topNum = 0;
        outer:
        for(int index = sort.length - 1; index >= 0; index--){
            if(0 != sort[index]){
                for(int bit = 31; bit >= 0; bit--){
                    if(0 != (sort[index] & (1 << bit))){
                        System.out.println((index << 5) + bit);
                        topNum++;
                        if(topNum >= 3){
                            break outer;
                        }
                    }
                }
            }
        }
    }
}
Su Xiang
sumber
0

saya melakukan kode saya sendiri, tidak yakin apakah itu yang "pewawancara" itu cari

private static final int MAX=100;
 PriorityQueue<Integer> queue = new PriorityQueue<>(MAX);
        queue.add(array[0]);
        for (int i=1;i<array.length;i++)
        {

            if(queue.peek()<array[i])
            {
                if(queue.size() >=MAX)
                {
                    queue.poll();
                }
                queue.add(array[i]);

            }

        }
Javier
sumber
0

Kemungkinan peningkatan.

Jika file berisi 1 miliar nomor, membacanya bisa sangat panjang ...

Untuk meningkatkan kinerja ini, Anda dapat:

  • Pisahkan file menjadi n bagian, Buat n utas, buat n utas lihat masing-masing untuk 100 angka terbesar di bagiannya (menggunakan antrian prioritas), dan akhirnya dapatkan 100 angka terbesar dari semua keluaran utas.
  • Gunakan cluster untuk melakukan tugas seperti itu, dengan solusi seperti hadoop. Di sini Anda dapat membagi file lebih banyak lagi dan memiliki output yang lebih cepat untuk file angka 1 miliar (atau 10 ^ 12).
Maxime B.
sumber
0

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.

Juvenik
sumber
-1

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.

Chris Cudmore
sumber
1
Tidak ada struktur data eksternal? Bagaimana dengan array angka miliar untuk disortir? Array ukuran ini adalah overhead besar di kedua waktu untuk mengisi dan ruang untuk menyimpan. Bagaimana jika semua angka "besar" berada di ujung array yang salah? Anda akan membutuhkan pada urutan 100 miliar swap untuk "menggelembungkan" mereka ke posisi - overhead besar lainnya ... Akhirnya, M N = 100 miliar vs M Log2 (N) = 6,64 miliar yang hampir dua urutan perbedaan besarnya. Mungkin berpikir ulang yang ini. Pemindaian sekali jalan sambil mempertahankan struktur data dari jumlah terbesar akan secara signifikan melakukan pendekatan ini.
NealB