Pertanyaan Besar O tentang algoritma dengan laju pertumbuhan (n ^ 2 + n) / 2

16

Saya mengajukan pertanyaan ini karena saya bingung tentang satu aspek tentang notasi O besar.

Saya menggunakan buku, Struktur Data dan Abstraksi dengan Java oleh Frank Carrano. Dalam bab tentang "Efisiensi Algoritma" ia menunjukkan algoritma berikut:

int sum = 0, i = 1, j = 1
for (i = 1 to n) {
    for (j = 1 to i)
        sum = sum + 1
}

Dia awalnya menggambarkan algoritma ini sebagai memiliki tingkat pertumbuhan (n 2  + n) / 2 . Yang melihatnya sepertinya intuitif.

Namun, kemudian dinyatakan bahwa (n 2  + n) / 2 berperilaku seperti n 2 ketika n besar. Dalam ayat yang sama ia menyatakan (n 2  + n) / 2 juga berperilaku seperti n 2 / 2 . Dia menggunakan ini untuk mengklasifikasikan algoritma di atas sebagai O (n 2 ) .

Saya mendapatkan bahwa (n 2  + n) / 2 adalah sama dengan n 2 / 2 karena persentase bijaksana, n membuat sedikit perbedaan. Yang tidak saya dapatkan adalah mengapa (n 2  + n) / 2 dan n 2 serupa, ketika n besar.

Misalnya, jika n = 1.000.000 :

(n^2 + n) / 2 =  500000500000 (5.000005e+11)
(n^2) / 2     =  500000000000 (5e+11)
(n^2)         = 1000000000000 (1e+12)

Yang terakhir tidak sama sekali. Faktanya, cukup jelas, ini dua kali lebih banyak dari yang di tengah. Jadi bagaimana Frank Carrano mengatakan mereka serupa? Juga, bagaimana algoritma diklasifikasikan sebagai O (n 2 ) . Melihat lingkaran dalam itu saya akan mengatakan itu adalah n 2 + n / 2

Andrew S
sumber
Jika Anda tertarik, saya telah memberikan jawaban untuk tiga loop bersarang dengan diagram diagram pohon eksekusi Teka-teki terkait dengan loop bersarang
Grijesh Chauhan
1
pada dasarnya idenya adalah bahwa ketika ntumbuh, baik fungsi 'n ^ 2` dan fungsi Anda, berperilaku sama, ada perbedaan konstan dalam tingkat pertumbuhannya. Jika Anda memiliki ekspresi yang kompleks, fungsi yang tumbuh lebih cepat mendominasi.
AK_
1
@MichaelT: Saya rasa ini bukan duplikat dari pertanyaan itu, karena yang lain hanyalah masalah salah menghitung. Ini adalah pertanyaan yang lebih halus tentang mengapa istilah yang lebih rendah (khususnya, pengganda konstan dan polinomial tingkat rendah) diabaikan. Penanya di sini rupanya sudah memahami masalah yang diangkat dalam pertanyaan lain, dan jawaban yang cukup untuk pertanyaan itu tidak akan menjawab pertanyaan ini.
sdenham

Jawaban:

38

Saat menghitung kompleksitas Big-O suatu algoritma, hal yang diperlihatkan adalah faktor yang memberikan kontribusi terbesar terhadap peningkatan waktu eksekusi jika jumlah elemen yang Anda jalankan algoritma meningkat.

Jika Anda memiliki algoritme dengan kompleksitas (n^2 + n)/2dan Anda menggandakan jumlah elemen, maka konstanta 2tidak memengaruhi peningkatan dalam waktu eksekusi, istilah tersebut nmenyebabkan penggandaan dalam waktu eksekusi dan istilah tersebut n^2menyebabkan peningkatan empat kali lipat dalam eksekusi waktu.
Karena n^2istilah memiliki kontribusi terbesar, kompleksitas Big-O adalah O(n^2).

Bart van Ingen Schenau
sumber
2
Saya suka itu, itu menjadi sedikit lebih jelas.
Andrew S
7
Ini sangat bergelombang tangan. Bisa jadi benar atau bisa salah. Jika Anda dapat mengambil sedikit matematika, lihat salah satu jawaban di bawah ini.
usr
2
Alasan ini terlalu kabur: itu berarti kita dapat menyimpulkan itu O(n * log n) = O(n), yang tidak benar.
cfh
Itu mungkin bukan jawaban yang paling tepat atau paling semantik benar, tetapi yang penting di sini adalah bahwa itu membuat saya mulai memahami titik pusat dan saya pikir itu adalah tujuan penulis. Ini sengaja tidak jelas karena rinciannya sering dapat mengalihkan perhatian dari prinsip-prinsip inti. Sangat penting untuk melihat kayu untuk pohon.
Andrew S
Bart benar-benar berbicara tentang istilah, bukan faktor. Memahami itu, kita tidak bisa menyimpulkan itu O(n * log n) = O(n). Saya pikir ini memberikan penjelasan yang bagus tentang alasan di balik definisi tersebut.
Mark Foskey
10

Definisi itu adalah itu

f(n) = O(g(n))

jika ada beberapa konstanta C> 0 sehingga, untuk semua n lebih besar dari beberapa n_0, yang kita miliki

|f(n)| <= C * |g(n)|

Ini jelas benar untuk f (n) = n ^ 2 dan g (n) = 1/2 n ^ 2, di mana konstanta C harus 2. Juga mudah untuk melihat bahwa itu berlaku untuk f (n) = n ^ 2 dan g (n) = 1/2 (n ^ 2 + n).

cfh
sumber
4
"Jika ada beberapa konstanta C> 0 sedemikian rupa sehingga, forr all n," seharusnya "Jika ada beberapa konstanta C, n_0 sedemikian rupa sehingga, untuk semua n> n_0,"
Taemyr
@ Taemyr: Selama fungsi gini bukan nol, itu sebenarnya tidak diperlukan karena Anda selalu dapat meningkatkan konstanta C untuk membuat pernyataan itu benar untuk banyak nilai n_0 pertama.
cfh
Tidak, kami selama kami melihat fungsi tidak ada nilai n_0 potensial yang terbatas.
Taemyr
@ Taemyr: n_0 adalah angka yang terbatas. Pilih C = maks {f (i) / g (i): i = 1, ..., n_0}, dan kemudian pernyataan akan selalu berlaku untuk nilai n_0 pertama, karena Anda dapat dengan mudah memeriksa.
cfh
Dalam CS ini kurang menjadi perhatian karena n biasanya ukuran input, dan karenanya bijaksana. Dalam hal ini seseorang dapat memilih C sedemikian sehingga n_0 = 1 berfungsi. Tetapi definisi formal adalah n lebih besar dari ambang tertentu, yang menghilangkan banyak nitpicking dalam menerapkan definisi tersebut.
Taemyr
6

Saat berbicara tentang kompleksitas, Anda hanya tertarik pada perubahan faktor waktu berdasarkan jumlah elemen ( n).

Dengan demikian, Anda dapat menghapus faktor konstan (seperti di 2sini).

Ini meninggalkanmu O(n^2 + n).

Sekarang, untuk produk yang masuk akal n, yaitu n * n, akan secara signifikan lebih besar dari sekedar n, yang merupakan alasan Anda diizinkan untuk melewati bagian itu juga, yang membuat Anda memang dengan kompleksitas akhir O(n^2).

Memang benar, untuk jumlah kecil akan ada perbedaan yang signifikan, tetapi ini menjadi lebih marginal semakin besar Anda n.

Mario
sumber
Seberapa besar dan n harus menjadi perbedaan untuk menjadi marjinal? Juga, mengapa / 2 dihapus, keberadaannya membagi nilainya?
Andrew S
6
@AndrewS Karena Notasi O Besar berbicara tentang pertumbuhan. Membagi dengan 2 tidak relevan di luar konteks tolok ukur dan cap waktu karena pada akhirnya tidak mengubah tingkat pertumbuhan. Komponen terbesar, bagaimanapun, dan itulah yang Anda simpan.
Neil
2
@Niel, brilian begitu jelas. Saya berharap buku-buku itu akan seperti itu. Kadang-kadang saya pikir para penulis tahu terlalu banyak bahwa mereka lupa bahwa manusia biasa tidak memiliki pengetahuan fungsional mereka dan karena itu tidak membuat poin penting yang jelas, tetapi menguburnya dalam beberapa deskripsi matematika formal atau menghilangkan semuanya bersama-sama percaya itu tersirat.
Andrew S
Saya berharap saya dapat menjawab pertanyaan ini lebih dari satu kali! @Neil, Anda harus menulis buku O Besar.
Tersosauros
3

Bukannya "(n² + n) / 2 berperilaku seperti n² saat n besar", melainkan (n² + n) / 2 tumbuh sepertisaat n bertambah .

Misalnya, ketika n bertambah dari 1.000 menjadi 1.000.000

(n² + n) / 2  increases from  500500 to  500000500000
(n²) / 2      increases from  500000 to  500000000000
(n²)          increases from 1000000 to 1000000000000

Demikian pula, ketika n meningkat dari 1.000.000 menjadi 1.000.000.000

(n² + n) / 2  increases from  500000500000 to  500000000500000000
(n²) / 2      increases from  500000000000 to  500000000000000000
(n²)          increases from 1000000000000 to 1000000000000000000

Mereka tumbuh dengan cara yang sama, yang merupakan notasi Big O.

Jika Anda memplot (n² + n) / 2 dan n² / 2 di Wolfram Alpha , mereka sangat mirip sehingga sulit dibedakan dengan n = 100. Jika Anda memetakan ketiganya di Wolfram Alpha , Anda melihat dua garis yang dipisahkan oleh faktor konstan 2.

ShadSterling
sumber
Ini bagus, itu membuatnya sangat jelas bagi saya. Terima kasih telah membalas.
Andrew S
2

Sepertinya Anda perlu memikirkan notasi O lebih besar. Betapa nyaman notasi ini, sangat menyesatkan karena penggunaan tanda yang sama, yang tidak digunakan di sini untuk menunjukkan persamaan fungsi.

Seperti yang Anda ketahui, notasi ini mengekspresikan perbandingan fungsi asimptotik, dan menulis f = O (g) berarti bahwa f (n) tumbuh paling cepat secepat g (n) saat n menuju tak terhingga. Cara sederhana untuk menerjemahkan ini adalah dengan mengatakan bahwa fungsi f / g dibatasi. Tetapi tentu saja, kita harus menjaga tempat-tempat di mana g adalah nol dan kita berakhir dengan definisi yang lebih kuat yang dapat Anda baca hampir di mana-mana .

Notasi ini ternyata sangat nyaman untuk komputasi - inilah sebabnya mengapa begitu luas - tetapi harus ditangani dengan hati-hati karena tanda sama dengan yang kita lihat di sana tidak menunjukkan kesetaraan fungsi . Ini hampir seperti mengatakan bahwa 2 = 5 mod 3 tidak menyiratkan bahwa 2 = 5 dan jika Anda tertarik pada aljabar, Anda benar-benar dapat memahami notasi O besar sebagai modulo kesetaraan sesuatu.

Sekarang, untuk kembali ke pertanyaan spesifik Anda, sama sekali tidak berguna untuk menghitung beberapa nilai numerik dan membandingkannya: betapapun besarnya satu juta, itu tidak memperhitungkan perilaku asimptotik. Akan lebih berguna untuk memplot rasio fungsi f (n) = n (n-1) / 2 dan g (n) = n² - tetapi dalam kasus khusus ini kita dapat dengan mudah melihat bahwa f (n) / g (n) lebih kecil dari 1/2 jika n> 0 yang menyiratkan bahwa f = O (g) .

Untuk meningkatkan pemahaman Anda tentang notasi, Anda harus

  • Bekerja dengan definisi yang bersih, bukan kesan fuzzy berdasarkan hal-hal yang serupa - seperti yang baru saja Anda alami, kesan fuzzy seperti itu tidak bekerja dengan baik.

  • Luangkan waktu untuk mengerjakan contoh secara detail. Jika Anda berolahraga sedikitnya lima contoh dalam seminggu, itu akan cukup untuk meningkatkan kepercayaan diri Anda. Ini adalah upaya yang pasti bernilai.


Catatan sisi aljabar Jika A adalah aljabar dari semua fungsi Ν → Ν dan C subalgebra dari fungsi yang dibatasi, diberi fungsi f himpunan fungsi milik O (f) adalah modul- C C, modul aturan A , dan aturan perhitungan pada besar O notasi hanya menggambarkan bagaimana A beroperasi pada submodul ini. Dengan demikian, persamaan yang kita lihat adalah persamaan C- sub-modul A , ini hanyalah jenis modulus lain.

Michael Le Barbier Grünewald
sumber
1
Artikel Wikipedia itu sulit diikuti setelah bagian kecil pertama. Itu ditulis untuk ahli matematika ulung oleh ahli matematika ulung dan bukan jenis teks pengantar yang saya harapkan dari artikel ensiklopedis. Terima kasih atas wawasan Anda meskipun semuanya baik.
Andrew S
Anda melebih-lebihkan level dalam teks Wikipedia! :) Ini tidak ditulis dengan baik, pasti. Graham, Knuth dan Patashnik menulis sebuah buku yang indah "Matematika Beton" untuk siswa di CS. Anda juga dapat mencoba “Seni Pemrograman Komputer” atau buku teori angka yang ditulis pada tahun 50-an (Hardy & Wright, Rose) karena mereka biasanya menargetkan tingkat siswa sekolah menengah. Anda tidak perlu membaca buku selengkapnya, jika Anda memilih satu, cukup bagian tentang asimptotik! Tetapi sebelum Anda perlu memutuskan seberapa banyak Anda perlu memahami. :)
Michael Le Barbier Grünewald
1

Saya pikir Anda salah paham apa arti notasi O besar.

Ketika Anda melihat O (N ^ 2) pada dasarnya berarti: ketika masalahnya menjadi 10 kali lebih besar, waktu untuk menyelesaikannya adalah: 10 ^ 2 = 100 kali lebih besar.

Mari kita memukulkan 1000 dan 10000 dalam persamaan Anda: 1000: (1000 ^ 2 + 1000) / 2 = 500500 10000: (10000 ^ 2 + 10000) / 2 = 50005000

50005000/500500 = 99,91

Jadi, sementara N mendapat 10 kali lebih besar, solusinya mendapat 100 kali lebih besar. Karena itu berperilaku: O (N ^ 2)

Pieter B
sumber
1

jika n adalah 1,000,000saat itu

(n^2 + n) / 2  =  500000500000  (5.00001E+11)
(n^2) / 2      =  500000000000  (5E+11)
(n^2)          = 1000000000000  (1E+12)

1000000000000.00 apa?

Sementara kompleksitas memberi kita cara untuk memprediksi biaya dunia nyata (detik atau byte tergantung pada apakah kita berbicara tentang kompleksitas waktu atau kompleksitas ruang), itu tidak memberi kita beberapa detik, atau unit khusus lainnya.

Ini memberi kita tingkat proporsi.

Jika suatu algoritma harus melakukan sesuatu n² kali, maka dibutuhkan n² × c untuk beberapa nilai c yaitu berapa lama setiap iterasi berlangsung.

Jika suatu algoritma harus melakukan sesuatu n² ÷ 2 kali, maka dibutuhkan n² × c untuk beberapa nilai c yang dua kali lebih lama dari setiap iterasi.

Either way, waktu yang diambil masih sebanding dengan n².

Sekarang, faktor-faktor konstan ini bukanlah sesuatu yang bisa kita abaikan; memang Anda dapat memiliki kasus di mana algoritma dengan kompleksitas O (n²) melakukan lebih baik daripada algoritma dengan kompleksitas O (n), karena jika kita bekerja pada sejumlah kecil item maka dampak dari faktor konsonan lebih besar dan dapat membanjiri masalah lain . (Memang, bahkan O (n!) Sama dengan O (1) untuk nilai n yang cukup rendah).

Tetapi bukan kompleksitas yang memberi tahu kita.

Dalam praktiknya, ada beberapa cara berbeda untuk meningkatkan kinerja suatu algoritma:

  1. Tingkatkan efisiensi setiap iterasi: O (n²) masih berjalan dalam n² × c detik, tetapi c lebih kecil.
  2. Kurangi jumlah kasus yang terlihat: O (n²) masih berjalan dalam n² × c detik, tetapi n lebih kecil.
  3. Ganti algoritme dengan yang memiliki hasil yang sama, tetapi kompleksitasnya lebih rendah: Misalnya, jika kita dapat menghitung ulang sesuatu O (n²) menjadi sesuatu O (n log n) dan karenanya berubah dari n² × c₀ detik menjadi (n log n) × c₁ detik .

Atau untuk melihatnya dengan cara lain, kami memiliki f(n)×cdetik yang diambil dan Anda dapat meningkatkan kinerja dengan mengurangi c, mengurangi natau mengurangi fpengembalian apa yang diberikan n.

Yang pertama bisa kita lakukan dengan beberapa mikro-opts di dalam satu loop, atau menggunakan perangkat keras yang lebih baik. Itu akan selalu memberikan peningkatan.

Yang kedua dapat kita lakukan dengan mengidentifikasi kasus di mana kita dapat melakukan hubungan singkat dari algoritma sebelum semuanya diperiksa, atau menyaring beberapa data yang tidak signifikan. Itu tidak akan memberikan peningkatan jika biaya melakukan ini melebihi keuntungan, tetapi umumnya akan menjadi peningkatan yang lebih besar dari kasus pertama, terutama dengan n besar.

Yang ketiga bisa kita lakukan dengan menggunakan algoritma yang sama sekali berbeda. Contoh klasik akan menggantikan semacam gelembung dengan quicksort. Dengan jumlah elemen yang sedikit kita mungkin telah memperburuk keadaan (jika c₁ lebih besar dari c₀), tetapi umumnya memungkinkan untuk mendapatkan keuntungan terbesar, terutama dengan n yang sangat besar.

Dalam penggunaan praktis, langkah-langkah kompleksitas memungkinkan kita untuk beralasan tentang perbedaan antara algoritma justru karena mereka mengabaikan masalah bagaimana mengurangi n atau c akan membantu, untuk berkonsentrasi pada examinging f()

Jon Hanna
sumber
"O (n!) Sama dengan O (1) untuk nilai n yang cukup rendah" salah. Harus ada cara yang lebih baik untuk menjelaskan bahwa "ketika ndijaga cukup rendah, Big-O tidak masalah".
Ben Voigt
@ BenVoigt Saya belum pernah menemukan yang memiliki dampak retorika yang sama seperti ketika saya pertama kali membacanya; itu bukan milik saya, saya mencurinya dari Eric Lippert, yang mungkin berasal atau mengambilnya dari orang lain. Tentu saja itu merujuk lelucon seperti "π sama dengan 3 untuk nilai kecil π dan nilai besar 3" yang masih lebih tua.
Jon Hanna
0

Faktor konstan

Inti dari notasi O besar adalah bahwa Anda dapat memilih faktor konstanta besar yang sewenang-wenang sehingga O (fungsi (n)) selalu lebih besar dari fungsi C * (n). Jika algoritma A adalah satu miliar kali lebih lambat dari algoritma B, maka mereka memiliki kompleksitas O yang sama, selama perbedaan itu tidak tumbuh ketika n tumbuh besar secara sewenang-wenang.

Mari kita asumsikan faktor konstan 1000000 untuk mengilustrasikan konsep - ini sejuta kali lebih besar dari yang diperlukan, tetapi itu menggambarkan titik bahwa mereka dianggap tidak relevan.

(n ^ 2 + n) / 2 "pas di dalam" O (n ^ 2) karena untuk n apa pun, tidak peduli seberapa besar, (n ^ 2 + n) / 2 <1000000 * n ^ 2.

(n ^ 2 + n) / 2 "tidak cocok" dengan set yang lebih kecil, misalnya O (n) karena untuk beberapa nilai (n ^ 2 + n) / 2> 1000000 * n.

Faktor konstan dapat menjadi besar secara sewenang-wenang - suatu algoritma dengan waktu berjalan n tahun memiliki O (n) kompleksitas yang "lebih baik" dari suatu algoritma dengan waktu berjalan n * log (n) mikrodetik.

Peter adalah
sumber
0

Big-O adalah tentang "betapa rumitnya" suatu algoritma. Jika Anda memiliki dua algoritme, dan satu membutuhkan waktu beberapa n^2*kdetik untuk berjalan, dan yang lainnya membutuhkan beberapa n^2*jdetik untuk berjalan, maka Anda dapat berdebat tentang mana yang lebih baik, dan Anda mungkin dapat membuat beberapa optimasi yang menarik untuk mencoba memengaruhi katau j, tetapi keduanya algoritma ini mati lambat dibandingkan dengan algoritma yang dibutuhkan n*muntuk berjalan. Tidak masalah seberapa kecil Anda membuat konstanta katau j, untuk input yang cukup besar n*malgoritme akan selalu menang, meskipun mcukup besar.

Jadi kami memanggil dua algoritma pertama O(n^2), dan kami memanggil yang kedua O(n). Ini membagi dunia dengan baik ke dalam kelas algoritma. Inilah yang dimaksud dengan big-O. Ini seperti membagi kendaraan menjadi mobil dan truk dan bus dll ... Ada banyak variasi antara mobil, dan Anda dapat menghabiskan sepanjang hari berdebat tentang apakah Prius lebih baik daripada Chevy Volt, tetapi pada akhirnya jika Anda perlu menempatkan 12 orang menjadi satu, maka ini adalah argumen yang agak tidak masuk akal. :)

Jason Walton
sumber