Apa yang menyebabkan algoritme memiliki kompleksitas O (log log n)?

Jawaban:

218

Istilah O (log log n) dapat muncul di berbagai tempat berbeda, tetapi biasanya ada dua rute utama yang akan tiba pada waktu proses ini.

Menyusut dengan Akar Pangkat Dua

Seperti yang disebutkan dalam jawaban untuk pertanyaan terkait, cara umum bagi algoritme untuk memiliki kompleksitas waktu O (log n) adalah agar algoritme tersebut bekerja dengan berulang kali memotong ukuran input dengan beberapa faktor konstan pada setiap iterasi. Jika ini kasusnya, algoritme harus berhenti setelah iterasi O (log n), karena setelah melakukan pembagian O (log n) dengan konstanta, algoritme harus mengecilkan ukuran masalah menjadi 0 atau 1. Inilah sebabnya, misalnya , pencarian biner memiliki kompleksitas O (log n).

Menariknya, ada cara serupa untuk memperkecil ukuran masalah yang menghasilkan runtime dalam bentuk O (log log n). Alih-alih membagi masukan menjadi dua di setiap lapisan, apa yang terjadi jika kita mengambil akar kuadrat dari ukuran di setiap lapisan?

Misalnya, ambil angka 65.536. Berapa kali kita harus membagi ini dengan 2 sampai kita turun menjadi 1? Jika kita melakukan ini, kita mendapatkan

  • 65.536 / 2 = 32.768
  • 32.768 / 2 = 16.384
  • 16.384 / 2 = 8.192
  • 8.192 / 2 = 4.096
  • 4.096 / 2 = 2.048
  • 2.048 / 2 = 1.024
  • 1.024 / 2 = 512
  • 512/2 = 256
  • 256/2 = 128
  • 128/2 = 64
  • 64/2 = 32
  • 32/2 = 16
  • 16/2 = 8
  • 8/2 = 4
  • 4/2 = 2
  • 2/2 = 1

Proses ini membutuhkan 16 langkah, dan 65.536 = 2 16 juga terjadi .

Tapi, jika kita mengambil akar kuadrat di setiap level, kita dapatkan

  • √65.536 = 256
  • √256 = 16
  • √16 = 4
  • √4 = 2

Perhatikan bahwa hanya perlu empat langkah untuk turun ke 2. Mengapa ini?

Pertama, penjelasan intuitif. Ada berapa digit pada bilangan n dan √n? Ada sekitar log n digit di bilangan n, dan sekitar log (√n) = log (n 1/2 ) = (1/2) log n digit di √n. Ini berarti, setiap kali Anda mengambil akar kuadrat, Anda secara kasar membagi dua angka pada bilangan tersebut. Karena Anda hanya dapat membagi dua kuantitas k O (log k) kali sebelum turun menjadi sebuah konstanta (katakanlah, 2), ini berarti Anda hanya dapat mengambil akar kuadrat O (log log n) kali sebelum Anda menurunkan angkanya ke beberapa konstanta (katakanlah, 2).

Sekarang, mari kita lakukan beberapa perhitungan untuk membuatnya lebih teliti. Le'ts menulis ulang urutan di atas dalam pangkat dua:

  • √65,536 = √2 16 = (2 16 ) 1/2 = 2 8 = 256
  • √256 = √2 8 = (2 8 ) 1/2 = 2 4 = 16
  • √16 = √2 4 = (2 4 ) 1/2 = 2 2 = 4
  • √4 = √2 2 = (2 2 ) 1/2 = 2 1 = 2

Perhatikan bahwa kita mengikuti urutan 2 16 → 2 8 → 2 4 → 2 2 → 2 1 . Pada setiap iterasi, kami memotong eksponen pangkat dua menjadi dua. Itu menarik, karena ini berhubungan kembali dengan apa yang telah kita ketahui - Anda hanya dapat membagi bilangan k dalam setengah O (log k) kali sebelum turun menjadi nol.

Jadi ambil sembarang angka n dan tuliskan sebagai n = 2 k . Setiap kali Anda mengambil akar kuadrat dari n, Anda membagi dua eksponen dalam persamaan ini. Oleh karena itu, hanya ada O (log k) akar kuadrat yang diterapkan sebelum k turun menjadi 1 atau lebih rendah (dalam hal ini n turun menjadi 2 atau lebih rendah). Karena n = 2 k , ini berarti bahwa k = log 2 n, dan oleh karena itu banyaknya akar kuadrat yang diambil adalah O (log k) = O (log log n). Oleh karena itu, jika ada algoritma yang bekerja dengan berulang kali mengurangi masalah menjadi ukuran subproblem yang merupakan akar kuadrat dari ukuran masalah aslinya, algoritma tersebut akan berhenti setelah langkah O (log log n).

Salah satu contoh dunia nyata tentang ini adalah pohon van Emde Boas(vEB-tree) struktur data. Pohon vEB adalah struktur data khusus untuk menyimpan bilangan bulat dalam rentang 0 ... N - 1. Ini berfungsi sebagai berikut: simpul akar pohon memiliki √N penunjuk di dalamnya, membagi rentang 0 ... N - 1 ke dalam ember √N yang masing-masing memiliki kisaran kira-kira √N bilangan bulat. Bucket ini kemudian masing-masing dibagi lagi menjadi bucket √ (√ N), yang masing-masing menampung kira-kira elemen √ (√ N). Untuk melintasi pohon, Anda mulai dari root, tentukan bucket mana yang Anda ikuti, lalu lanjutkan secara rekursif di subtree yang sesuai. Karena cara struktur vEB-tree, Anda dapat menentukan dalam O (1) waktu subpohon mana yang akan diturunkan, dan setelah O (log log N) langkah Anda akan mencapai bagian bawah pohon. Karenanya, pencarian dalam vEB-tree hanya membutuhkan waktu O (log log N).

Contoh lainnya adalah algoritma titik terdekat Hopcroft-Fortune . Algoritma ini mencoba menemukan dua titik terdekat dalam kumpulan titik 2D. Ini bekerja dengan membuat kisi-kisi ember dan mendistribusikan titik-titik ke dalam ember itu. Jika pada titik mana pun dalam algoritme ditemukan bucket yang memiliki lebih dari √N poin di dalamnya, algoritme akan memproses bucket tersebut secara rekursif. Kedalaman maksimum rekursi adalah O (log log n), dan dengan menggunakan analisis pohon rekursi dapat ditunjukkan bahwa setiap lapisan dalam pohon melakukan kerja O (n). Oleh karena itu, total runtime dari algoritma ini adalah O (n log log n).

O (log n) Algoritma pada Input Kecil

Ada beberapa algoritma lain yang mencapai runtime O (log log n) dengan menggunakan algoritma seperti pencarian biner pada objek berukuran O (log n). Sebagai contoh, struktur data x-fast trie melakukan pencarian biner pada layer pada ketinggian pohon O (log U), sehingga runtime untuk beberapa operasinya adalah O (log log U). Trie y-fast terkait mendapatkan beberapa runtime O (log log U) dengan mempertahankan BST yang seimbang dari masing-masing node O (log U), memungkinkan pencarian di pohon tersebut untuk berjalan dalam waktu O (log log U). The tango pohon dan terkait pohon multisplay struktur data berakhir dengan O (log log n) jangka dalam analisis mereka karena mereka mempertahankan pohon-pohon yang mengandung O (log n) item masing-masing.

Contoh Lainnya

Algoritme lain mencapai runtime O (log log n) dengan cara lain. Pencarian interpolasi mengharapkan runtime O (log log n) untuk menemukan nomor dalam array yang diurutkan, tetapi analisisnya cukup terlibat. Akhirnya, analisis bekerja dengan menunjukkan bahwa jumlah iterasi sama dengan jumlah k sehingga n 2 -k ≤ 2, di mana log n adalah solusi yang tepat. Beberapa algoritme, seperti algoritme Cheriton-Tarjan MST , tiba pada waktu proses yang melibatkan O (log log n) dengan memecahkan masalah pengoptimalan terbatas yang kompleks.

Semoga ini membantu!

templatetypedef
sumber
5
@ JonathonReinhart- Ini kebetulan adalah sesuatu yang menurut saya (a) benar-benar keren, dan (b) tidak terkenal. Saya selalu senang membagikan hal-hal seperti ini! :-)
templatetypedef
1
@ Mahesha999 Stack Overflow secara aktif mendorong pengguna untuk menjawab pertanyaan mereka sendiri . :-)
templatetypedef
hanya menebak apa implikasi lain yang mungkin dimiliki "Jawab pertanyaan Anda sendiri" di bagian bawah halaman Ajukan Pertanyaan atau hanya mengaktifkan kotak teks jawaban pada halaman pertanyaan.
Mahesha999
1
Baris penting: Oleh karena itu, jika ada algoritma yang bekerja dengan berulang kali mengurangi masalah menjadi ukuran subproblem yang merupakan akar kuadrat dari ukuran masalah asli, algoritma tersebut akan berhenti setelah O (log log n) langkah.
Gun2sh
3

Salah satu cara untuk melihat faktor O (log log n) dalam kompleksitas waktu adalah dengan pembagian seperti hal-hal yang dijelaskan di jawaban lain, tetapi ada cara lain untuk melihat faktor ini, ketika kita ingin melakukan pertukaran antara waktu dan ruang / waktu dan perkiraan / waktu dan kekerasan / ... dari algoritme dan kami memiliki beberapa iterasi buatan pada algoritme kami.

Misalnya SSSP (Jalur terpendek sumber tunggal) memiliki algoritme O (n) pada grafik planar, tetapi sebelum algoritme yang rumit itu ada algoritme yang jauh lebih mudah (tetapi masih agak sulit) dengan waktu berjalan O (n log log n), dasar algoritme adalah sebagai berikut (hanya deskripsi yang sangat kasar, dan saya akan menawarkan untuk melewatkan pemahaman bagian ini dan membaca bagian lain dari jawabannya):

  1. bagilah grafik menjadi beberapa bagian dengan ukuran O (log n / (log log n)) dengan beberapa batasan.
  2. Misalkan setiap bagian yang disebutkan adalah simpul pada graf baru G 'kemudian hitung SSSP untuk G' pada waktu O (| G '| * log | G' |) ==> di sini karena | G '| = O (| G | * log log n / log n) kita dapat melihat faktor (log log n).
  3. Hitung SSSP untuk setiap bagian: sekali lagi karena kita memiliki bagian O (| G '|) dan kita dapat menghitung SSSP untuk semua bagian dalam waktu | n / logn | * | log n / log logn * log (logn / log log n).
  4. perbarui bobot, bagian ini bisa dilakukan di O (n). untuk lebih jelasnya catatan kuliah ini bagus.

Tapi maksud saya, di sini kita memilih pembagian dengan ukuran O (log n / (log log n)). Jika kita memilih divisi lain seperti O (log n / (log log n) ^ 2) yang mungkin berjalan lebih cepat dan membawa hasil lain. Maksud saya, dalam banyak kasus (seperti dalam algoritma aproksimasi atau algoritma acak, atau algoritma seperti SSSP seperti di atas), ketika kita mengulang sesuatu (subproblem, solusi yang mungkin, ...), kita memilih jumlah iterasi yang sesuai dengan perdagangan itu kita punya (waktu / ruang / kompleksitas algoritma / faktor konstan dari algoritma, ...). Jadi mungkin kita melihat hal-hal yang lebih rumit daripada "log log n" dalam algoritma kerja nyata.

Saeed Amiri
sumber