Menemukan elemen terkecil dari urutan tertentu hanya dengan O (k) memori O (n) waktu

11

Misalkan kita membaca urutan n angka, satu per satu. Cara menemukan elemen terkecil k hanya dengan menggunakan memori sel O(k) dan dalam waktu linier ( O(n) ). Saya pikir kita harus menabung dulu k hal urutan dan ketika mendapatkan k+1 'jangka th, menghapus istilah yang kita yakin bahwa itu tidak bisa menjadi k ' th elemen terkecil dan kemudian save k+1 'jangka th. Jadi kita harus memiliki indikator yang menunjukkan istilah tidak dapat digunakan ini di setiap langkah dan indikator ini harus diperbarui di setiap langkah dengan cepat. Saya mulai dengan "maks"; tetapi tidak dapat memperbarui dengan cepat; Berarti jika kita mempertimbangkan maks maka dalam penghapusan pertama kita kehilangan maks dan kita harus mencari maks dalam O(k) dan penyebabnya (nk)×O(k) waktu itu tidak linier. Mungkin kita harus menyimpan terlebih dahulu syarat k urutan lebih cerdas.

Bagaimana saya mengatasi masalah ini?

Shahab_HK
sumber
1
Apakah Anda tertarik pada algoritma online, atau apakah algoritma apa pun akan melakukannya?
Yuval Filmus
Jika maka Anda dapat melakukannya dengan menggunakan algoritma statistik pesanan. Jika k = o ( n ) maka Anda dapat melakukannya O ( k ) memori dan O ( n log k ) waktu menggunakan pohon seimbang tinggi. k=θ(n)k=o(n)O(k)O(nlogk)
Shreesh
Ini disebut masalah pemilihan en.wikipedia.org/wiki/Selection_algorithm
xavierm02
Ada algoritma waktu linear di tempat, yang bisa Anda gunakan untuk google, tetapi agak rumit.
Yuval Filmus
@ xavierm02 ini bukan masalah pemilihan yang identik. Karena ada batasan batas memori.
Shahab_HK

Jawaban:

16

Buat buffer ukuran . Baca dalam elemen 2 k dari array. Gunakan algoritma pemilihan waktu linier untuk mempartisi buffer sehingga elemen terkecil k adalah yang pertama; ini membutuhkan waktu O ( k ) . Sekarang baca item k lain dari array Anda ke buffer, ganti item k terbesar di buffer, partisi buffer seperti sebelumnya, dan ulangi.2k2kkO(k)kk

Ini membutuhkan waktu dan O ( k ) ruang.O(kn/k)=O(n)O(k)

jbapple
sumber
+1, ini cocok dengan asimptotik yang diminta. Yang sedang berkata, saya tidak percaya ini lebih cepat daripada melakukan algoritma seleksi linear-waktu tunggal ... kecuali ketika adalah konstanta kecil, maka itu memberikan perspektif yang menarik. Misalnya untuk k = 1 algoritma ini menghasilkan fungsi. kk=1min
orlp
1
Kadang-kadang, algoritma seleksi linear-waktu menggunakan terlalu banyak ruang. Misalnya, ini tidak cocok untuk digunakan dalam konteks streaming atau ketika larik input tidak dapat diubah.
jbapple
Itu adalah poin yang valid.
orlp
3

Anda dapat melakukannya dalam memori dan waktu O ( n log k ) dengan membentuk he-max max heap dari elemen k pertama dalam waktu O ( k ) , kemudian mengulangi sisa array dan mendorong yang baru elemen dan kemudian muncul untuk O ( log k ) untuk setiap elemen yang memberikan total waktu O ( k + n log k ) = O ( n log k ) .O(k)O(nlogk)kO(k)O(logk)O(k+nlogk)O(nlogk)

Anda dapat melakukannya dalam memori tambahan dan waktu O ( n ) dengan menggunakan algoritma pemilihan median-of-median, memilih pada k , dan mengembalikan elemen k pertama . Tanpa perubahan asimtotik, Anda dapat menggunakan introselect untuk mempercepat kasing rata-rata. Ini adalah cara kanonik untuk menyelesaikan masalah Anda.O(logn)O(n)kk

Sekarang secara teknis dan O ( k ) tidak ada bandingannya. Namun saya berpendapat bahwa O ( log n ) lebih baik dalam praktiknya, karena secara efektif konstan mengingat tidak ada sistem komputer yang memiliki lebih dari 2 64 byte memori, log 2 64 = 64 . Sementara itu k dapat tumbuh hingga sebesar n .O(logn)O(k)O(logn)264log264=64kn

orlp
sumber
O(n×logmin(k,nk))
O(min(k,nk))O(k)knmin(k,nk)n2O(min(k,nk))O(k)
@ xavierm02 Yang dikatakan, itu masih speedup bagus :)
orlp
un,k=kO(k)O(min(k,nk))CMMknkC(nk)n=k+).O(min(k,nk))O(k)
@ xavierm02 Saya tidak terbiasa dengan . Agar adil, aku secara umum cukup terbiasa dengan multidimensi besar- notasi, terutama mengingat bahwa dimensi tidak berhubungan. O n , kun,kOn,k
orlp