Notasi Big-O adalah bahasa standar untuk menyatakan batas atas pertumbuhan sebuah fungsi. Ketika kita menulis T(n) = O(n log n), kita tidak menyatakan bahwa T(n) sama persis dengan n log n, melainkan bahwa T(n) tidak tumbuh lebih cepat dari n log n untuk n yang cukup besar. Pada Chapter ini kita akan memformalkan definisi Big-O, mempelajari cara membuktikan sebuah fungsi berada dalam kelas Big-O tertentu, dan menerapkan aturan aljabar untuk menyederhanakan ekspresi kompleksitas.
Definisi Formal Big-O #
Misalkan f(n) dan g(n) adalah fungsi yang memetakan bilangan bulat non-negatif ke bilangan real non-negatif. Kita menyatakan bahwa:
f(n) = O(g(n))jika dan hanya jika terdapat konstanta positifcdann₀sehingga0 ≤ f(n) ≤ c·g(n)untuk semuan ≥ n₀.
Artinya, mulai dari suatu titik n₀, fungsi f(n) tidak pernah melebihi c kali g(n). Konstanta c menyerap faktor-faktor konstan (seperti koefisien atau biaya per operasi), dan n₀ menyerap suku-suku ordo lebih rendah yang hanya dominan untuk n kecil.
Mekanisme: Mengapa Definisi seperti Ini #
Definisi Big-O menggunakan batas atas agar fleksibel: bila f(n) = O(g(n)) dan ternyata g(n) lebih besar dari yang sebenarnya dibutuhkan, hubungan tersebut tetap benar. Misal f(n) = 2n + 5 juga O(n²) secara matematis, tetapi kita biasanya menyatakan O(n) karena itu batas yang ketat (tight). Big-O tidak mengikat kita pada batas ketat; itu tugas Big-Theta yang akan kita pelajari nanti.
Cara Membuktikan f(n) = O(g(n)) #
Untuk membuktikan f(n) = O(g(n)), kita harus menemukan c dan n₀ eksplisit sehingga pertidaksamaan f(n) ≤ c·g(n) dipenuhi untuk semua n ≥ n₀.
Contoh Pembuktian #
Misalkan f(n) = 3n² + 5n + 7. Kita ingin menunjukkan f(n) = O(n²).
Langkah 1: cari batas atas dengan ekspresi sederhana.
Untuk n ≥ 1:
5n ≤ 5n²(karenan ≤ n²untukn ≥ 1)7 ≤ 7n²(karena1 ≤ n²untukn ≥ 1)3n² = 3n²
Maka f(n) = 3n² + 5n + 7 ≤ 3n² + 5n² + 7n² = 15n².
Langkah 2: pilih c = 15 dan n₀ = 1. Untuk semua n ≥ 1, 0 ≤ f(n) ≤ 15·n². ∎
Aturan Aljabar Big-O #
Berikut aturan praktis untuk menyederhanakan ekspresi Big-O:
| Aturan | Bentuk asal | Hasil |
|---|---|---|
| Konstanta diabaikan | O(5n) | O(n) |
| Suku ordo rendah diabaikan | O(n² + n + 1) | O(n²) |
| Penjumlahan: ambil maksimum | O(n²) + O(n) | O(n²) |
| Perkalian | O(n) × O(n) | O(n²) |
O(log n) < O(n) | O(log n + n) | O(n) |
| Komposisi loop bersarang | O(n) × O(m) | O(n·m) |
Kode: Verifikasi Numerik Pembuktian #
#include <iostream>
#include <cmath>
// f(n) yang akan kita analisis
long long f(int n) {
return 3LL * n * n + 5LL * n + 7;
}
// g(n) = 15 * n^2 sebagai batas atas
long long g(int n) {
return 15LL * n * n;
}
int main() {
std::cout << "n\tf(n)\tg(n)=15n^2\tf<=g?\n";
for (int n : {0, 1, 2, 5, 10, 100, 1000}) {
long long fn = f(n);
long long gn = g(n);
std::cout << n << '\t' << fn << '\t' << gn
<< (fn <= gn ? "\tYA" : "\tTIDAK") << '\n';
}
return 0;
}Output yang Dihasilkan:
n f(n) g(n)=15n^2 f<=g?
0 7 0 TIDAK
1 15 15 YA
2 29 60 YA
5 107 375 YA
10 357 1500 YA
100 30507 150000 YA
1000 3005007 15000000 YABedah Kode Baris-per-Baris #
- Fungsi
f: menghitung3n² + 5n + 7dengan tipelong longuntuk menghindari overflow padanbesar. - Fungsi
g: batas atas teoritis15n²sesuai pembuktian kita. - Loop
for (int n : {...}): structured binding list menyederhanakan iterasi nilainyang berbeda. - Baris output: menampilkan
n,f(n),g(n), dan hasil pembandingan. Output menunjukkan untukn=0,f(0) = 7 > g(0) = 0, tetapi mulain=1, pertidaksamaan terpenuhi. Inilah mengapa definisi Big-O menggunakann₀: kita hanya peduli padanyang cukup besar. - Implikasi: konstanta
c=15dann₀=1adalah satu pilihan valid. Banyak pilihan lain juga valid; tidak ada keharusan menemukancminimum.
Hierarki Fungsi Asimptotik #
Berikut pertumbuhan beberapa kelas fungsi umum, diurutkan dari paling lambat tumbuh ke paling cepat:
| Fungsi | n=10 | n=100 | n=1.000 | n=10.000 |
|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 |
log₂ n | 3,3 | 6,6 | 10,0 | 13,3 |
√n | 3,2 | 10,0 | 31,6 | 100,0 |
n | 10 | 100 | 1.000 | 10.000 |
n log₂ n | 33 | 664 | 9.966 | 132.877 |
n² | 100 | 10.000 | 1.000.000 | 100.000.000 |
n³ | 1.000 | 1.000.000 | 10⁹ | 10¹² |
2ⁿ | 1.024 | ≈10³⁰ | ≈10³⁰¹ | ≈10³⁰¹⁰ |
n! | ≈3,6×10⁶ | ≈9,3×10¹⁵⁷ | ≈4×10²⁵⁶⁷ | ≈2,8×10³⁵⁶⁵⁹ |
Aturan Praktis Menentukan Big-O #
Saat membaca kode, gunakan aturan praktis berikut untuk menentukan Big-O dengan cepat:
| Konstruksi Kode | Big-O |
|---|---|
Urutan pernyataan O(1) | O(1) |
if-else dengan kedua cabang O(1) | O(1) |
Loop tunggal for (i=0; i<n; ++i) berisi O(1) | O(n) |
Loop bersarang n × n | O(n²) |
Loop bersarang n × m | O(n·m) |
Loop dengan i = i * 2 (binary search) | O(log n) |
Loop dengan i = i * 2 luar + loop n dalam | O(n log n) |
Rekursi f(n) = f(n-1) + O(1) | O(n) |
Rekursi f(n) = 2·f(n/2) + O(n) | O(n log n) |
std::sort(v.begin(), v.end()) | O(n log n) |
Kegunaan dan Keterbatasan Big-O #
Big-O berguna untuk membandingkan algoritma secara asimptotik, tetapi memiliki beberapa keterbatasan yang perlu disadari:
- Mengabaikan konstanta:
O(n)belum tentu lebih cepat dariO(n²)padankecil. Misal insertion sortO(n²)sering kali mengalahkan merge sortO(n log n)untukn < 50karena konstanta kecil. - Hanya menjawab worst case asimptotik: tidak menjelaskan kasus rata-rata atau best case.
- Tidak memperhitungkan cache: algoritma cache-friendly dengan Big-O sama bisa 10× lebih cepat di praktik.
- Tidak menjelaskan awal memori: dua fungsi
O(1)bisa memiliki jejak memori sangat berbeda.
Ringkasan #
f(n) = O(g(n))berartif(n)dibatasi atas olehc·g(n)untukn ≥ n₀.- Pembuktian Big-O dilakukan dengan menemukan
cdann₀eksplisit. - Aturan aljabar: abaikan konstanta, abaikan suku ordo rendah, gunakan penjumlahan dan perkalian.
- Hierarki pertumbuhan:
1 < log n < √n < n < n log n < n² < n³ < 2ⁿ < n!. - Big-O adalah batas atas; tidak menjanjikan ketat (tight), sehingga kita perlu Big-Theta untuk pernyataan yang lebih presisi.
Pada Chapter berikutnya kita akan mempelajari Big-Omega — notasi kembar Big-O yang menyatakan batas bawah asimptotik.