Big-O untuk loop bersarang

8

Saya membaca posting ini di Big-O
Dikatakan bahwa kode berikut adalah O (n ^ 2):

bool ContainsDuplicates(String[] strings)
{
    for(int i = 0; i < strings.Length; i++)
    {
        for(int j = 0; j < strings.Length; j++)
        {
            if(i == j) // Don't compare with self
            {
                continue;
            }

            if(strings[i] == strings[j])
            {
                return true;
            }
        }
    }
    return false;
}

Tapi saya tidak bisa mengerti kenapa.
Lingkaran dalam melakukan sesuatu yang konstan.
Jadi ini adalah penjumlahan dari 1 .... N dari sebuah konstanta. yaitu angka konstan O (1).
Lingkaran luar adalah penjumlahan dari O (1).
Jadi saya akan membayangkan itu adalah n * O (1).

Saya pikir saya salah paham sesuatu di sini.
Saya tidak berpikir bahwa semua loop bersarang berarti O (n ^ 2), kan?

pengguna10326
sumber
3
Di mana Anda mendapatkan gagasan bahwa loop batin adalah "melakukan sesuatu yang konstan"? Itu sebuah lingkaran. Itu adalah petunjuk pertama Anda yang membutuhkan O (N).
Paul Tomblin
2
Hal ini dapat dikurangi menjadi O (N ^ 2/2) dengan memiliki loop batin mulai i+1bukan 0. Seperti yang tertulis, dalam kasus terburuk (tidak ada dupes) 1/2 perbandingannya mubazir.
Peter Rowell
1
O (N ^ 2/2) = O (N ^ 2) (Dalam kedua kasus, ketika N pergi ke tak terhingga, penggandaan N berarti empat kali lipat dari run time. Itu semua O (N ^ 2) artinya.)
David Schwartz
5
Yang perlu diperhatikan adalah bahwa dalam bahasa seperti C di mana perbandingan string O(N), kode ini akan benar-benar berada di O(N^2 * M)tempat M adalah panjang string.
Isak Savo
2
Perbandingan (dari string panjang konstan) adalah O (1). Perbandingan N adalah O (N). Perbandingan N ^ 2 adalah O (N ^ 2).
SF.

Jawaban:

18

Kesalahan Anda adalah dengan lingkaran dalam. Ia melakukan sesuatu yang konstan n kali, jadi itu adalah O ( n ). Loop luar melakukan loop dalam n kali, jadi itu adalah O ( n × n ), atau O ( n 2  ).

Secara umum, jika jumlah iterasi yang dibuat loop tergantung pada ukuran input, itu adalah O ( n ). Dan jika k loop tersebut bersarang, itu adalah O ( n k  ).

KeithB
sumber
1
mungkin notasi yang lebih mudah untuk dipahami adalah dengan mengatakan bahwa algoritme berjalan dalam O (i * j). Di sini kedua loop iterate di atas panjang f String, sehingga i = j = n di mana n adalah strings.length
Newtopian
1
@Newtopian: Anda biasanya mengasumsikan bahwa panjang senarnya sekitar konstan; itu sebenarnya pendekatan yang cukup bagus dalam praktiknya.
Donal Fellows
@ Donal memang, kita bisa, dengan pengetahuan kumpulan data yang tepat membuat asumsi seperti itu, tetapi seperti selalu ada pengecualian, analisis DNA muncul di pikiran di mana panjang string dapat beberapa karakter panjangnya untuk kodon ke multi mega byte untuk kromosom penuh. Yang mengatakan dalam kasus ini dalam contoh khusus panjang string tidak ada konsekuensi karena kita mengulangi string itu sendiri, bukan karakter. Yaitu, kecuali operator kesetaraan telah kelebihan beban ke beberapa logika funky tergantung pada panjang operan. Namun saya ragu OP bermaksud menggali sejauh ini dalam analisisnya.
Newtopian
@Newtopian: Agar adil, ContainsDuplicatesfungsi / metode dari pertanyaan tidak sangat mungkin digunakan dengan kromosom penuh.
Donal Fellows
@ Donal: mungkin tidak tidak :-)
Newtopian
4

Jika panjang string adalah n, tes jika i == jakan dieksekusi n ^ 2 kali. Urutan suatu algoritma adalah urutan bagian mana pun yang memiliki urutan tertinggi. Karena 'i' dibandingkan dengan 'j' n ^ 2 kali, urutan algoritma tidak mungkin kurang dari O(n^2).

Untuk 'n' yang sangat besar, menggandakan 'n' akan empat kali lipat waktu berjalan.

David Schwartz
sumber
Tapi bukankah tes i==jitu O (1)?
user10326
5
Tes itu sendiri adalah O (1). Loop dalam menjalankan tes 'n' kali, jadi itu O (n * 1). Loop luar mengeksekusi loop dalam 'n' kali, jadi itu O (n * n * 1). Jadi keseluruhan kode adalah O (n ^ 2). Jika Anda menjalankan O (1) langkah n ^ 2 kali, itu O (n ^ 2).
David Schwartz
1
Nit kecil: sebenarnya untuk setiap nilai N dua kali lipat itu akan empat kali lipat saat run - hanya saja tidak peduli bahwa banyak untuk N. kecil
Peter Rowell
@ Peter: Itu tidak benar. Misalnya, beralih dari n = 2 ke n = 4 mungkin tidak menggandakan waktu berjalan karena mesin 32-bit dapat menggunakan jumlah memori yang sama untuk membaca 2 byte sebagai 4. Demikian pula, untuk n kecil, overhead memasuki fungsi. mungkin signifikan, dan itu konstan. Lebih besar n mungkin memiliki prediksi cabang yang lebih baik secara rata-rata. Dan seterusnya.
David Schwartz
5
Dalam konteks ini, "bery large 'n'" berarti 'n' cenderung ke arah infinity, tetapi mengabaikan segala inefisiensi yang mungkin terjadi karena big 'n's (seperti tidak pas dalam bilangan bulat 32-bit atau 64-bit). Inti dari notasi O-besar adalah untuk menganalisis bagaimana kinerja suatu algoritma berubah seiring dengan meningkatnya 'n', asalkan tetap dalam kemampuan mesin. (Ini mungkin berdalih dalam konteks ini, tetapi secara umum, memahami apa yang termasuk dan diabaikan notasi O besar penting untuk memahami apa artinya.)
David Schwartz
2

Anda salah mengartikan arti operasi konstan.

Operasi konstan adalah operasi yang selalu dijalankan dalam jumlah waktu yang tetap tanpa bergantung pada input yang diterimanya.

i == j adalah operasi konstan karena mengeksekusi pernyataan ini dalam jumlah waktu yang tetap. Katakanlah dibutuhkan 1 unit waktu.

Tetapi operasi konstan ini dilakukan (tidak ada nilai i) * (tidak ada nilai j) kali. Katakanlah saya dan j dijalankan masing-masing 10 kali. Kemudian dengan perhitungan dibutuhkan 100 unit waktu untuk menyelesaikan i == j ketika dalam loop bersarang. Jadi itu akan bervariasi karena nilai i dan j bervariasi.

Kita dapat yakin bahwa i == j akan dilakukan dalam 1 unit waktu tetapi kita tidak tahu berapa kali i == j akan dilakukan.

Jika dilakukan n kali maka akan membutuhkan n unit waktu. Loop luar mengeksekusi loop dalam sebanyak n kali. Jadi pada dasarnya operasi i == j dilakukan n ^ 2 kali.

Semua loop bersarang berarti O (n ^ (tidak ada loop bersarang)). Di sini O berarti batas atas yang berarti kode akan dieksekusi kurang dari atau sama dengan nilai O (). Ini adalah jaminan bahwa itu tidak akan lebih lama dari itu tetapi bisa memakan waktu lebih sedikit tidak lebih besar.

codecool
sumber
0

Loop bersarang menjalankan O ( i 1 * i 2 * ... * i n ), di mana n adalah jumlah loop bersarang dan i x adalah jumlah iterasi dalam loop x . (Atau, dengan kata lain, itu adalah produk dari jumlah iterasi di masing-masing loop.)

Sepasang loop Anda mengulangi array yang sama dua kali, yang secara kebetulan adalah O (n * n) atau O (n 2 ).

Apa yang terjadi di dalam loop paling dalam diperlakukan sebagai konstan karena itu akan terjadi setiap saat. Notasi O-besar tidak benar-benar mengukur kinerja kode linier, ini lebih tentang membuat perbandingan relatif tentang bagaimana algoritma iteratif berperilaku ketika diberi n item input untuk ditangani. Operasi tertentu yang tidak benar-benar melakukan iterasi (seperti push atau pop pada stack) menjadi seperti O (1) karena mereka menangani satu elemen data.

Blrfl
sumber
1
tidak benar jika i_k tidak konstan: pikirkan tentang sweepline algos kasus terburuk mereka adalah O (n ^ 2) kasus terbaik mereka adalah O (n) (tidak termasuk jenis awal) dengan beberapa loop-dalam menjadi lebih atau tidak sama sekali tergantung pada masalah. quicksort juga diimplementasikan tanpa rekursi adalah loop bersarang namun tetap O (n * log n)
ratchet freak
Quicksort dengan pemilihan pivot acak bersifat nondeterministik, yang berarti angka O (n log n) adalah rata-rata. Masih sesuai dengan yang saya katakan: i1 = n dan i2 = log n .
Blrfl