Bagaimana algoritma pengurutan ini Θ (n³) dan bukan Θ (n²), terburuk?


52

Saya baru saja mulai mengambil kursus tentang Struktur Data dan Algoritma dan asisten pengajar saya memberi kami kode semu berikut untuk mengurutkan array bilangan bulat:

void F3() {
    for (int i = 1; i < n; i++) {
        if (A[i-1] > A[i]) {
            swap(i-1, i)
            i = 0
        }
    }
}

Mungkin tidak jelas, tetapi di sini adalah ukuran array yang kami coba untuk mengurutkan.nA

Bagaimanapun, asisten pengajar menjelaskan kepada kelas bahwa algoritma ini dalam waktu (kasus terburuk, saya percaya), tetapi tidak peduli berapa kali saya melewatinya dengan array yang diurutkan terbalik, menurut saya itu seharusnya dan bukan .Θ(n3)Θ(n2)Θ(n3)

Apakah seseorang dapat menjelaskan kepada saya mengapa ini Θ(n3) dan bukan Θ(n2) ?


Anda mungkin tertarik dengan pendekatan analisis terstruktur ; coba cari bukti sendiri!
Raphael

Cukup terapkan dan ukur untuk meyakinkan diri sendiri. Array dengan 10.000 elemen dalam urutan terbalik perlu waktu beberapa menit, dan array dengan 20.000 elemen dalam urutan terbalik harus memakan waktu sekitar delapan kali lebih lama.
gnasher729

@ gnasher729 Anda tidak salah, tetapi solusi saya berbeda: jika Anda mencoba membuktikan Anda terikat, Anda akan selalu gagal, yang akan memberi tahu Anda ada sesuatu yang salah. (Tentu saja, seseorang dapat melakukan keduanya. Merencanakan / memasang jelas lebih cepat untuk menolak hipotesis, tetapi kurang dapat diandalkan . Selama Anda melakukan beberapa jenis analisis formal / terstruktur, tidak ada salahnya dilakukan. Mengandalkan plot adalah tempat masalah dimulai.)O(n2)
Raphael

1
karena i = 0pernyataan itu
njzk2

Jawaban:


60

Algoritma ini dapat ditulis ulang seperti ini

  1. Pindai Asampai Anda menemukan inversi .
  2. Jika Anda menemukannya, tukar dan mulai lagi dari awal.
  3. Jika tidak ada, hentikan.

Sekarang ada paling banyak inversi dan Anda memerlukan pemindaian linear-time untuk menemukan masing-masing - sehingga waktu terburuknya adalah . Sebuah contoh pengajaran yang indah saat perjalanan ke pendekatan pencocokan pola banyak menyerah pada!(n2)Θ(n2)Θ(n3)

Catatan: Satu harus sedikit berhati-hati: beberapa inversi muncul lebih awal, beberapa terlambat, sehingga tidak sepele bahwa biaya bertambah seperti yang diklaim (untuk batas bawah). Anda juga perlu memperhatikan bahwa swap tidak pernah memperkenalkan inversi baru . Analisis yang lebih rinci dari kasus dengan array yang diurutkan terbalik akan menghasilkan sesuatu seperti kasus kuadrat dari rumus Gauss.

Seperti komentar tepat dari @ gnasher729, mudah untuk melihat waktu running kasus terburuk adalah dengan menganalisis waktu running ketika mengurutkan input (meskipun input ini mungkin bukan yang terburuk).Ω(n3)[1,2,,n,2n,2n1,,n+1]

Hati-hati: jangan menganggap bahwa array yang diurutkan terbalik akan menjadi input terburuk untuk semua algoritme pengurutan. Itu tergantung pada algoritma. Ada beberapa algoritma pengurutan di mana array yang diurutkan terbalik bukan yang terburuk, dan bahkan mungkin dekat dengan yang terbaik.


14
Jika Anda mengambil larik di mana bagian pertama terdiri dari angka 1 hingga n / 2 dalam urutan menaik, dan bagian kedua adalah n hingga n / 2 + 1 dalam urutan terbalik, maka jelas bahwa Anda memerlukan setidaknya n / 2 langkah-langkah untuk menemukan setiap inversi, dan akan ada sekitar (n / 2) ^ 2/2 dari mereka. Dan itu kemungkinan besar bukan yang terburuk.
gnasher729

@AnthonyRossello Ini adalah hasil standar (dalam kombinatorik permutasi). Singkatnya, hitung jumlah inversi dalam array yang diurutkan terbalik (apakah jelas itu adalah kasus terburuk?); itu jumlah Gauss.
Raphael

Kita harus ingat bahwa bagaimanapun juga, jumlah parsial dari selalu , itu hanya koefisien yang turun dengan cepat: (perhatikan koefisien ) yang cukup besar. Masalahnya adalah, tidak peduli dengan koefisien. Θ(nα)Θ(nα+1)k=0nkα1α+1nα+11α+1Θ
yo '

2
@yo 'Dan ini berkaitan dengan jawaban (atau pertanyaan) bagaimana?
Raphael

7

Cara berpikir alternatif tentang ini adalah apa nilai maksimum imenjadi sebelum diatur ulang. Ini, ternyata, membuatnya lebih mudah untuk alasan tentang bagaimana urutan urutan sebelumnya Amempengaruhi waktu menjalankan algoritma.

Secara khusus, perhatikan bahwa ketika imenetapkan nilai maksimal barunya, sebut saja N, array [A[0], ..., A[N-1]]diurutkan dalam urutan menaik.

Jadi apa yang terjadi ketika kita menambahkan elemen A[N]ke dalam campuran?

Matematika:

Baiklah, katakan saja itu pas di posisi . Kemudian kita membutuhkan iterasi loop (yang akan saya nyatakan ) untuk memindahkannya ke tempat iterasi , untuk memindahkannya ke tempat , dan secara umum:pNNstepsN1N+(N1)N2

stepsN(pN)=N+(N1)+(N2)++(pN+1)=12(N(N+1)pN(pN+1))

Untuk array yang diurutkan secara acak, mengambil distribusi seragam pada untuk setiap , dengan:pN{0,1,,N}N

E(stepsN(pN))=a=1NP(pN=a)stepsN(a)=a=1N1N12(N(N+1)a(a+1))=12(N(N+1)13(N+1)(N+2))=13(N21)=Θ(N2)

jumlahnya dapat ditampilkan menggunakan rumus Faulhaber atau tautan Wolfram Alpha di bagian bawah.

Untuk array yang diurutkan terbalik, untuk semua , dan kami mendapatkan:pN=0N

stepsN(pN)=12N(N+1)

tepatnya, mengambil lebih lama dari nilai .pN

Untuk larik yang sudah disortir, dan , dengan istilah tingkat rendah menjadi relevan.pN=NstepsN(pN)=0

Total waktu:

Untuk mendapatkan total waktu, kami meringkas langkah-langkah di atas semua . (Jika kami sangat berhati-hati, kami akan menjumlahkan swap serta iterasi loop, dan menjaga kondisi awal dan akhir, tetapi cukup mudah untuk melihat mereka tidak berkontribusi pada kompleksitas dalam kebanyakan kasus) .N

Dan lagi, menggunakan linearitas harapan dan Formula Faulhaber's:

Expected Total Steps=E(N=1nstepsN(pN))=N=1nE(stepsN(pN))=Θ(n3)

Tentu saja, jika karena alasan tertentu bukan (mis. Distribusi array yang kita lihat sudah sangat dekat untuk disortir), maka ini tidak selalu perlu menjadi kasus. Tetapi dibutuhkan distribusi yang sangat spesifik pada untuk mencapai ini!stepsN(pN)Θ(N2)pN

Bacaan yang relevan:


@ Raphael - terima kasih untuk perbaikan yang disarankan, saya telah menambahkan sedikit lebih detail. Variabel acak adalah (dari , rangkaian urutan ), sehingga ekspektasi secara teknis dilakukan ataspiΩAΩ
David E

Berbeda ; Yang saya maksud adalah Landau. Ω
Raphael

3

Penolakan:

Ini bukan bukti (sepertinya beberapa orang berpikir saya mempostingnya seolah-olah itu). Ini hanya eksperimen kecil yang bisa dilakukan OP untuk menyelesaikan keraguannya tentang penugasan:

tidak peduli berapa kali saya menjalaninya dengan array yang diurutkan terbalik, menurut saya seharusnya dan bukan .Θ(n2)Θ(n3)

Dengan kode sederhana seperti itu, perbedaan antara dan seharusnya tidak sulit dikenali dan dalam banyak kasus praktis ini adalah pendekatan yang berguna untuk memeriksa firasat atau menyesuaikan harapan.Θ(n2)Θ(n3)


@Raphael sudah menjawab pertanyaan Anda, tetapi hanya untuk iseng, pas keluaran program ini ke menggunakan skrip gnuplot ini melaporkan nilai eksponen dan dan menghasilkan plot berikut ( yang pertama adalah skala normal dan yang kedua adalah skala log-log):f(x)=axb+cx2.997961668332222.99223727692339

normal loglog

Saya harap ini membantu¨


2
Anda dapat menyesuaikan fungsi apa pun dengan nilai-nilai ini. Lihat juga di sini .
Raphael

3
@Raphael Jika Anda tidak ingin melakukan nitpick dengan cara ini, maka tidak, Anda tidak dapat memenuhi fungsi apa pun (misalnya Anda tidak akan dapat menyesuaikan fungsi konstan dengan akurasi yang masuk akal). Ini bukan bukti, tetapi sudah ada jawaban yang menyediakan sketsa. Adapun kegunaannya, saya dapat mengutip posting Anda sendiri yang Anda tautkan: "Saya harus setuju bahwa ini adalah pendekatan yang sangat berguna yang bahkan kadang-kadang kurang dimanfaatkan". Selain itu, OP mengatakan dia pikir itu seharusnya daripada , jadi mengapa tidak bereksperimen dan melihat apakah firasatnya benar? Lanj. Θ(n2)Θ(n3)
dtldarek

2
Ini memberikan bukti bahwa algoritma ini adalah tetapi pertanyaannya adalah mengapa . Ia meminta penjelasan tentang fenomena, bukan konfirmasi. Θ(n3)
David Richerby

2
@ Davidvidicbyby Apakah ini berarti jawaban ini tidak berguna?
dtldarek

3
@Magicsowon Ini adalah situs tanya jawab, bukan forum. Kami sedang mencari jawaban untuk pertanyaan, bukan diskusi di sekitarnya.
David Richerby

3

Asumsikan Anda memiliki array.

array a[10] = {10,8,9,6,7,4,5,2,3,0,1}

Algoritme Anda melakukan hal berikut

Scan(1) - Swap (10,8) => {8,10,9,6,7,4,5,2,3,0,1}  //keep looking at "10"
Scan(2) - Swap (10,9) => {8,9,10,6,7,4,5,2,3,0,1}
...
Scan(10) - Swap(10,1) => {8,9,6,7,4,5,2,3,0,1,10}

Pada dasarnya itu bergerak ke ujung array elemen tertinggi, dan dalam melakukan itu mulai overs pada setiap pemindaian secara efektif melakukan O(n^2)gerakan .. hanya untuk satu elemen. Namun, ada n elemen sehingga kita harus mengulang nkali ini . Ini bukan bukti formal, tetapi membantu memahami dengan cara "tidak formal" mengapa waktu berjalan O(n^3).


4
Apa yang ditambahkan dari jawaban lain? Penjelasan tentang apa yang dilakukan algoritme sudah diberikan, dan alasan Anda untuk runtime tidak jelas. (Kasing terburuk tidak berperilaku linear!)
Raphael

2
Terkadang ada nilai dalam menjelaskan ide yang sama dalam berbagai cara (dengan formalisme; dengan contoh sederhana untuk "memompa intuisi"), terutama ketika orang yang mengajukan pertanyaan itu baru di lapangan. Jadi menurut saya apa yang ditambahkan ini adalah disajikan dengan cara yang dapat membantu intuisi.
DW

Karena saya mendapat balasan atas komentar saya dalam sebuah bendera (jangan lakukan itu!): "Kasing terburuk tidak berperilaku linear!" - Maksud saya sifat aljabar dari operator kasus terburuk. Secara kasar, Anda menggunakan WorstCase (1 + ... + n) "=" WorstCase (1) + ... + WorstCase (n) tetapi identitas ini tidak berlaku.
Raphael

1
Saya baru di bidang ini dan memberikan penjelasan dengan contoh konkret yang jelas membantu saya mendapatkan intuisi tentang masalah tersebut. Sekarang solusi yang diterima lebih masuk akal bagi saya.
vaer-k

0

Logikanya tampaknya menyortir elemen-elemen dalam array dalam urutan menaik.

Misalkan bilangan terkecil adalah di akhir array (a [n]). Agar bisa sampai ke tempat yang tepat - diperlukan operasi (n + (n-1) + (n-2) + ... 3 + 2 + 1). = O (n2).

Diperlukan satu elemen dalam array O (n2). Jadi, untuk n nements itu adalah O (n3).


5
Apa yang ditambahkan dari jawaban lain? Penjelasan tentang apa yang dilakukan algoritma sudah diberikan, dan alasan Anda untuk runtime paling tidak bagus. (Kasing terburuk tidak berperilaku linear!)
Raphael

Penjelasan yang bagus. Ini memberikan perspektif berbeda, lebih intuitif tentang masalah, tidak dijelaskan dalam jawaban lain. (Belum lagi sangat singkat dan mudah dimengerti.)
2501

1
@ 2501 Tidak, ini salah. Coba gunakan "intuisi" ini pada algoritma Dijkstra dan Anda akan mendapatkan runtime kuadratik (dalam jumlah node), yang salah.
Raphael

@ Raphael Tidak, itu benar, seperti yang dijelaskan dalam jawabannya. Penjelasan ini berfungsi untuk algoritma ini, bukan untuk orang lain. Meskipun mungkin salah bagi mereka, klaim ini tidak membuktikan bahwa itu salah untuk yang satu ini.
2501

@ Raphael Saya tidak mengerti penjelasan dalam jawaban yang diterima. Jadi, saya memecahkan masalah ini dan mencoba menjelaskannya secara sederhana tanpa persyaratan teknis .. jadi, ini untuk anggota seperti saya yang tidak dapat memahami jawaban yang diterima .. Saya senang seseorang menemukan ini berguna.
mk ..
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.