Saya percaya ada cara untuk menemukan elemen kth terbesar dalam array panjang yang tidak disortir n di O (n). Atau mungkin itu "diharapkan" O (n) atau sesuatu. Bagaimana kita bisa melakukan ini?
performance
algorithm
big-o
MrDatabase
sumber
sumber
Jawaban:
Ini disebut menemukan statistik urutan ke-k . Ada algoritma acak yang sangat sederhana (disebut quickselect ) yang mengambil
O(n)
waktu rata - rata, waktuO(n^2)
terburuk, dan algoritma non-acak yang cukup rumit (disebut introseluler ) mengambilO(n)
waktu terburuk. Ada beberapa info di Wikipedia , tetapi tidak terlalu bagus.Semua yang Anda butuhkan ada di slide powerpoint ini. Hanya untuk mengekstrak algoritma dasar dari algoritma kasusO(n)
terburuk (introselect):Ini juga sangat rinci dalam buku Pengantar Algoritma oleh Cormen et al.
sumber
Jika Anda menginginkan
O(n)
algoritma yang benar , sebagai lawan dariO(kn)
atau sesuatu seperti itu, maka Anda harus menggunakan quickselect (itu pada dasarnya quicksort tempat Anda membuang partisi yang tidak Anda minati). Prof saya memiliki luncuran yang bagus, dengan analisis runtime: ( referensi )Algoritme QuickSelect dengan cepat menemukan elemen terkecil k-th dari array elemen yang tidak disortir
n
. Ini adalah Algoritma Acak , jadi kami menghitung waktu berjalan terburuk yang diharapkan .Di sini adalah algoritma.
Berapa waktu berjalan dari algoritma ini? Jika musuh membalik koin untuk kita, kita mungkin menemukan bahwa pivot selalu merupakan elemen terbesar dan
k
selalu 1, memberikan waktu berjalanTetapi jika pilihannya memang acak, waktu berjalan yang diharapkan diberikan oleh
di mana kami membuat asumsi yang tidak sepenuhnya masuk akal bahwa rekursi selalu mendarat di yang lebih besar dari
A1
atauA2
.Mari kita tebak
T(n) <= an
untuk beberapaa
. Lalu kita dapatkandan sekarang entah bagaimana kita harus mendapatkan jumlah yang menghebohkan di sebelah kanan tanda plus untuk menyerapnya
cn
di sebelah kiri. Jika kita hanya mengikatnya , kita mendapatkan secara kasar . Tapi ini terlalu besar - tidak ada ruang untuk memeras tambahan . Jadi mari kita perluas penjumlahan menggunakan rumus seri aritmatika:2(1/n) ∑i=n/2 to n an
2(1/n)(n/2)an = an
cn
di mana kita mengambil keuntungan dari n menjadi "cukup besar" untuk mengganti
floor(n/2)
faktor-faktor buruk dengan yang lebih bersih (dan lebih kecil)n/4
. Sekarang kita bisa melanjutkandisediakan
a > 16c
.Ini memberi
T(n) = O(n)
. Sudah jelasOmega(n)
, jadi kita dapatkanT(n) = Theta(n)
.sumber
k > length(A) - length(A2)
?A
ke dalamA1
dan diA2
sekitar poros, kami tahu itulength(A) == length(A1)+length(A2)+1
. Jadi,k > length(A)-length(A2)
sama dengank > length(A1)+1
, yang benar ketikak
ada di suatu tempat diA2
.Google cepat tentang itu ('kth element array array') mengembalikan ini: http://discuss.joelonsoftware.com/default.asp?interview.11.509587.17
(itu khusus untuk 3d terbesar)
dan jawaban ini:
sumber
Anda suka quicksort. Pilih elemen secara acak dan dorong semuanya lebih tinggi atau lebih rendah. Pada titik ini Anda akan tahu elemen mana yang sebenarnya Anda pilih, dan jika itu adalah elemen k yang Anda lakukan, jika tidak Anda ulangi dengan bin (lebih tinggi atau lebih rendah), bahwa elemen k akan jatuh. Secara statistik, waktu yang diperlukan untuk menemukan elemen k tumbuh dengan n, O (n).
sumber
Companion Programmer untuk Analisis Algoritma memberikan versi yang adalah O (n), meskipun penulis menyatakan bahwa faktor konstan begitu tinggi, Anda mungkin akan lebih suka naif semacam-the-daftar-kemudian-pilih metode.
Saya menjawab surat pertanyaan Anda :)
sumber
Pustaka standar C ++ memiliki fungsi panggilan yang hampir persis seperti itu
nth_element
, meskipun itu memodifikasi data Anda. Ia mengharapkan run-time linier, O (N), dan juga melakukan pengurutan parsial.sumber
Meskipun tidak terlalu yakin tentang O (n) kompleksitas, tetapi akan pasti antara O (n) dan nLog (n). Juga pasti lebih dekat ke O (n) daripada nLog (n). Fungsi ditulis dalam Java
sumber
Saya menerapkan menemukan kth minimal dalam n elemen yang tidak disortir menggunakan pemrograman dinamis, khususnya metode turnamen. Waktu eksekusi adalah O (n + klog (n)). Mekanisme yang digunakan tercantum sebagai salah satu metode pada halaman Wikipedia tentang Algoritma Pemilihan (seperti yang ditunjukkan dalam salah satu posting di atas). Anda dapat membaca tentang algoritma dan juga menemukan kode (java) di halaman blog saya Finding Kth Minimum . Selain itu logika dapat melakukan pemesanan parsial dari daftar - mengembalikan K min (atau maks) pertama dalam waktu O (klog (n)).
Meskipun kode memberikan hasil kth minimum, logika yang sama dapat digunakan untuk menemukan maksimum kth di O (klog (n)), mengabaikan pra-kerja yang dilakukan untuk membuat pohon turnamen.
sumber
Anda dapat melakukannya di O (n + kn) = O (n) (untuk k konstan) untuk waktu dan O (k) untuk ruang, dengan melacak elemen k terbesar yang pernah Anda lihat.
Untuk setiap elemen dalam array, Anda dapat memindai daftar k terbesar dan mengganti elemen terkecil dengan yang baru jika lebih besar.
Solusi tumpukan prioritas Warren lebih rapi.
sumber
O(n log k)
... masih merosot menjadi O (nlogn) jika k besar. Saya akan berpikir itu akan bekerja dengan baik untuk nilai-nilai kecil k namun ... mungkin lebih cepat daripada beberapa algoritma lain yang disebutkan di sini [???]Pilih cepat seksi dengan Python
sumber
a1 = [i for i in arr if i > arr[r]]
dana2 = [i for i in arr if i < arr[r]]
, akan mengembalikan elemen terbesar k .numpy.sort
untuknumpy array
atausorted
untuk daftar) daripada menggunakan implementasi manual ini.Temukan median array dalam waktu linier, lalu gunakan prosedur partisi persis seperti dalam quicksort untuk membagi array menjadi dua bagian, nilai di sebelah kiri rata-rata lebih rendah (<) daripada rata-rata dan ke kanan lebih besar dari (>) median , itu juga dapat dilakukan dalam waktu lineat, sekarang, pergi ke bagian array dimana elemen kth terletak, Sekarang perulangan menjadi: T (n) = T (n / 2) + cn yang memberi saya O (n) overal.
sumber
Di bawah ini adalah tautan untuk implementasi penuh dengan penjelasan yang cukup luas bagaimana algoritma untuk menemukan elemen Kth dalam algoritma yang tidak disortir bekerja. Ide dasarnya adalah mempartisi array seperti di QuickSort. Tetapi untuk menghindari kasus-kasus ekstrem (misalnya ketika elemen terkecil dipilih sebagai pivot di setiap langkah, sehingga algoritma berubah menjadi O (n ^ 2) waktu berjalan), pemilihan pivot khusus diterapkan, yang disebut algoritma median-of-median. Seluruh solusi berjalan dalam waktu O (n) dalam kondisi terburuk dan rata-rata.
Berikut ini tautan ke artikel lengkap (ini tentang menemukan elemen terkecil Kth , tetapi prinsipnya sama untuk menemukan Kth terbesar ):
Menemukan Kth Elemen Terkecil dalam Array yang Tidak Disortir
sumber
Sesuai makalah ini Menemukan item terbesar K dalam daftar n item , algoritma berikut akan memakan
O(n)
waktu dalam kasus terburuk.Analisis: Seperti yang disarankan dalam makalah asli:
Mengapa ukuran partisi diambil 5 dan bukan 3?
Sebagaimana disebutkan dalam makalah asli :
Sekarang saya telah mencoba mengimplementasikan algoritma di atas sebagai:
Demi penyelesaian, algoritma lain menggunakan Antrian Prioritas dan membutuhkan waktu
O(nlogn)
.Kedua algoritma ini dapat diuji sebagai:
Output yang diharapkan adalah:
18 18
sumber
Bagaimana dengan pendekatan yang seperti ini
Pertahankan a
buffer of length k
dan atmp_max
, mendapatkan tmp_max adalah O (k) dan dilakukan n kali jadi sepertiO(kn)
Apakah benar atau saya kehilangan sesuatu?
Meskipun tidak mengalahkan rata-rata kasus pemilihan cepat dan terburuk dari metode statistik median tetapi cukup mudah dipahami dan diimplementasikan.
sumber
beralih melalui daftar. jika nilai saat ini lebih besar dari nilai terbesar yang disimpan, simpan sebagai nilai terbesar dan benturkan 1-4 ke bawah dan 5 turun dari daftar. Jika tidak, bandingkan dengan nomor 2 dan lakukan hal yang sama. Ulangi, periksa terhadap semua 5 nilai yang tersimpan. ini harus dilakukan di O (n)
sumber
saya ingin menyarankan satu jawaban
jika kita mengambil elemen k pertama dan mengurutkannya ke daftar nilai k yang tertaut
sekarang untuk setiap nilai lain bahkan untuk kasus terburuk jika kita melakukan penyisipan sort untuk nilai nk sisanya bahkan dalam jumlah kasus terburuk perbandingan akan menjadi k * (nk) dan untuk nilai k yang akan diurutkan biarlah k * (k- 1) jadi keluar menjadi (nk-k) yaitu o (n)
Bersulang
sumber
Penjelasan dari algoritma median - of - median untuk menemukan bilangan bulat terbesar ke - k dapat ditemukan di sini: http://cs.indstate.edu/~spitla/presentation.pdf
Implementasi dalam c ++ di bawah ini:
sumber
Ada juga algoritma pemilihan Wirth , yang memiliki implementasi yang lebih sederhana daripada QuickSelect. Algoritme pemilihan Wirth lebih lambat daripada QuickSelect, tetapi dengan beberapa perbaikan, ia menjadi lebih cepat.
Lebih detail. Dengan menggunakan optimasi MODIFIND dari Vladimir Zabrodsky dan pemilihan pivot median-of-3 dan memberikan perhatian pada langkah-langkah terakhir dari bagian partisi dari algoritma, saya telah membuat algoritma berikut (yang secara imajinatif dinamai "LefSelect"):
Dalam tolok ukur yang saya lakukan di sini , LefSelect adalah 20-30% lebih cepat daripada QuickSelect.
sumber
Solusi Haskell:
Ini mengimplementasikan median solusi median dengan menggunakan metode withShape untuk menemukan ukuran partisi tanpa benar-benar menghitungnya.
sumber
Berikut ini adalah implementasi C ++ dari QuickSelect Acak. Idenya adalah untuk secara acak memilih elemen pivot. Untuk mengimplementasikan partisi acak, kami menggunakan fungsi acak, rand () untuk menghasilkan indeks antara l dan r, menukar elemen pada indeks yang dihasilkan secara acak dengan elemen terakhir, dan akhirnya memanggil proses partisi standar yang menggunakan elemen terakhir sebagai pivot.
Kompleksitas waktu kasus terburuk dari solusi di atas masih O (n2). Dalam kasus terburuk, fungsi acak selalu dapat memilih elemen sudut. Kompleksitas waktu yang diharapkan dari QuickSelect acak di atas adalah Θ (n)
sumber
Panggil polling () k kali.
sumber
Ini adalah implementasi dalam Javascript.
Jika Anda melepaskan batasan yang tidak dapat Anda ubah array, Anda dapat mencegah penggunaan memori tambahan menggunakan dua indeks untuk mengidentifikasi "partisi saat ini" (dalam gaya quicksort klasik - http://www.nczonline.net/blog/2012/ 11/27 / computer-science-in-javascript-quicksort / ).
Jika Anda ingin menguji kinerjanya, Anda dapat menggunakan variasi ini:
Sisa kode ini hanya untuk membuat beberapa taman bermain:
Sekarang, jalankan tes Anda beberapa kali. Karena Math.random () itu akan menghasilkan setiap kali hasil yang berbeda:
Jika Anda mengujinya beberapa kali, Anda bahkan dapat melihat secara empiris bahwa jumlah iterasi adalah, rata-rata, O (n) ~ = konstan * n dan nilai k tidak mempengaruhi algoritma.
sumber
Saya datang dengan algoritma ini dan tampaknya O (n):
Katakanlah k = 3 dan kami ingin menemukan item terbesar ke-3 dalam array. Saya akan membuat tiga variabel dan membandingkan setiap item dari array dengan minimum dari ketiga variabel ini. Jika item array lebih besar dari minimum kami, kami akan mengganti variabel min dengan nilai item. Kami melanjutkan hal yang sama hingga akhir array. Minimum dari tiga variabel kami adalah item terbesar ke-3 dalam array.
Dan, untuk menemukan item terbesar K kita membutuhkan variabel K.
Contoh: (k = 3)
Dapatkah seseorang tolong tinjau ini dan beri tahu saya apa yang saya lewatkan?
sumber
Berikut ini adalah implementasi dari algoritma eladv yang disarankan (Saya juga taruh implementasi ini dengan pivot acak):
sumber
itu mirip dengan strategi quickSort, di mana kita memilih pivot yang sewenang-wenang, dan membawa elemen yang lebih kecil ke kiri, dan yang lebih besar ke kanan
sumber
Pergi ke Akhir dari tautan ini: ...........
http://www.geeksforgeeks.org/kth-smallestlargest-element-unsorted-array-set-3-worst-case-linear-time/
sumber
Anda dapat menemukan elemen terkecil k dalam waktu O (n) dan ruang konstan. Jika kita menganggap array hanya untuk bilangan bulat.
Pendekatannya adalah melakukan pencarian biner pada rentang nilai Array. Jika kita memiliki nilai min_ dan nilai maks_ keduanya dalam rentang integer, kita dapat melakukan pencarian biner pada rentang itu. Kita dapat menulis fungsi komparator yang akan memberi tahu kita jika ada nilai kth-terkecil atau lebih kecil dari kth-terkecil atau lebih besar dari kth-terkecil. Lakukan pencarian biner hingga Anda mencapai angka terkecil ke-k
Ini kode untuk itu
Solusi kelas:
sumber
Ada juga satu algoritma, yang mengungguli algoritma quickselect. Ini disebut algoritma Floyd-Rivets (FR) .
Artikel asli: https://doi.org/10.1145/360680.360694
Versi yang dapat diunduh: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.309.7108&rep=rep1&type=pdf
Artikel Wikipedia https://en.wikipedia.org/wiki/Floyd%E2%80%93Rivest_algorithm
Saya mencoba menerapkan quickselect dan algoritma FR di C ++. Saya juga membandingkannya dengan implementasi standar library C ++ std :: nth_element (yang pada dasarnya adalah introelect hybrid quickselect dan heapselect). Hasilnya adalah quickselect dan nth_element berjalan rata-rata, tetapi algoritma FR berlari kira-kira. dua kali lebih cepat dibandingkan dengan mereka.
Kode contoh yang saya gunakan untuk algoritma FR:
sumber
Apa yang akan saya lakukan adalah ini:
Anda bisa menyimpan pointer ke elemen pertama dan terakhir dalam daftar yang ditautkan. Mereka hanya berubah ketika pembaruan daftar dibuat.
Memperbarui:
sumber
Pertama kita dapat membangun BST dari array yang tidak disortir yang membutuhkan waktu O (n) dan dari BST kita dapat menemukan elemen terkecil k dalam O (log (n)) yang secara keseluruhan menghitung ke urutan O (n).
sumber