Big O O(log n): Memahami Binary Search dan Perhitungan Matematikanya

Big O O(log n): Memahami Binary Search dan Perhitungan Matematikanya

Dalam algoritma dan pemrograman, kita tidak hanya perlu mengetahui apakah sebuah program dapat menghasilkan output yang benar. Kita juga perlu memahami seberapa efisien algoritma tersebut ketika jumlah data semakin besar.

Salah satu konsep penting dalam analisis algoritma adalah Big O Notation.

Salah satu jenis Big O yang sangat penting untuk dipahami adalah O(log n) atau kompleksitas logaritmik.

Contoh algoritma yang menggunakan konsep O(log n) adalah Binary Search.

Artikel ini akan membahas pengertian O(log n), cara kerja Binary Search, contoh program JavaScript, serta perhitungan matematikanya secara step by step.

Apa Itu Big O?

Big O adalah notasi yang digunakan untuk menggambarkan pertumbuhan kebutuhan waktu atau jumlah operasi sebuah algoritma berdasarkan jumlah data yang diproses.

Beberapa kompleksitas yang umum digunakan antara lain:

Big ONamaContoh
O(1)ConstantMengambil data berdasarkan index
O(log n)LogarithmicBinary Search
O(n)LinearSequential Search
O(n log n)LinearithmicMerge Sort
O(n²)QuadraticNested Loop
O(2ⁿ)ExponentialRekursif tertentu
O(n!)FactorialPermutasi

Semakin lambat pertumbuhan jumlah operasi terhadap n, secara umum semakin baik skalabilitas algoritmanya.

Apa Itu O(log n)?

O(log n) disebut kompleksitas logaritmik.

Ciri utamanya adalah setiap proses mengurangi ukuran masalah secara signifikan, biasanya menjadi setengah dari ukuran sebelumnya.

Misalnya terdapat 16 data.

Pada Binary Search:

16 → 8 → 4 → 2 → 1

Setiap langkah membagi ruang pencarian menjadi dua.

Secara matematis:

log₂(16) = 4

Artinya dibutuhkan sekitar 4 kali pembagian untuk mengurangi 16 data menjadi 1 bagian.

Apa Itu Binary Search?

Binary Search adalah algoritma pencarian yang bekerja dengan cara membagi data menjadi dua bagian secara berulang.

Namun ada satu syarat penting:

Data harus sudah dalam keadaan terurut.

Contoh:

1001, 1002, 1003, 1004, 1005,
1006, 1007, 1008, 1009, 1010

Misalnya kita ingin mencari NIM:

1008

Binary Search tidak memeriksa data mulai dari 1001 satu per satu.

Program langsung memeriksa data yang berada di tengah.

Contoh Data

Kita memiliki 10 data:

Index :  0     1     2     3     4     5     6     7     8     9

Data  : 1001  1002  1003  1004  1005  1006  1007  1008  1009  1010

Target yang ingin dicari:

1008

Langkah 1: Menentukan Batas Pencarian

Awalnya:

kiri = 0
kanan = 9

Karena index terakhir adalah 9.

Kemudian mencari posisi tengah:

tengah = (kiri + kanan) / 2

Masukkan nilai:

tengah = (0 + 9) / 2
       = 9 / 2
       = 4,5

Karena index harus berupa bilangan bulat, digunakan pembulatan ke bawah:

tengah = 4

Dalam JavaScript:

Math.floor((kiri + kanan) / 2)

Maka:

data[4] = 1005

Sekarang bandingkan:

1005 dengan 1008

Karena:

1005 < 1008

maka target berada di sebelah kanan.

Bagian berikutnya tidak perlu diperiksa:

1001  1002  1003  1004  1005

Pencarian dilanjutkan dari:

kiri = tengah + 1
kiri = 4 + 1
kiri = 5

Sehingga area pencarian menjadi:

1006  1007  1008  1009  1010

Langkah 2: Mencari Nilai Tengah Lagi

Sekarang:

kiri = 5
kanan = 9

Hitung:

tengah = (5 + 9) / 2
       = 14 / 2
       = 7

Maka:

data[7] = 1008

Bandingkan:

1008 = 1008

Data ditemukan.

Jadi hanya diperlukan:

2 langkah

Perjalanan Pencarian Secara Visual

Prosesnya dapat digambarkan:

DATA AWAL

1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
                         ↑
                       tengah
                       1005

1008 > 1005
→ cari ke kanan


DATA BERIKUTNYA

1006 1007 1008 1009 1010
          ↑
        tengah
         1008

1008 = 1008
→ DITEMUKAN

Mengapa Kompleksitasnya O(log n)?

Sekarang kita masuk ke bagian matematikanya.

Prinsip Binary Search adalah:

Setiap langkah → data dibagi 2

Misalnya:

n = 16

16
↓ dibagi 2
8
↓ dibagi 2
4
↓ dibagi 2
2
↓ dibagi 2
1

Jumlah pembagian:

4 kali

Secara matematika:

16 / 2⁴ = 1

atau:

2⁴ = 16

Maka:

4 = log₂(16)

Jadi:

log₂(16) = 4

Inilah dasar mengapa Binary Search mempunyai kompleksitas:

O(log n)

Contoh dengan 32 Data

Jika jumlah data:

n = 32

Maka:

32 → 16 → 8 → 4 → 2 → 1

Ada 5 kali pembagian.

Secara matematika:

log₂(32) = 5

Jadi:

O(log₂ 32) = O(5)

Dalam notasi Big O, basis logaritma biasanya tidak dituliskan karena perbedaan konstanta basis tidak mengubah kelas kompleksitas.

Sehingga ditulis:

O(log n)

Contoh dengan 1.024 Data

Sekarang bayangkan sistem mempunyai 1.024 data.

Jika menggunakan Binary Search:

1.024
↓
512
↓
256
↓
128
↓
64
↓
32
↓
16
↓
8
↓
4
↓
2
↓
1

Berapa kali pembagian?

10 kali

Karena:

2¹⁰ = 1.024

Maka:

log₂(1.024) = 10

Jadi meskipun terdapat 1.024 data, ruang pencarian dapat dipersempit hanya dalam sekitar 10 tahap pembagian.

Contoh dengan 1.048.576 Data

Sekarang jumlah data menjadi:

1.048.576

Kita cari:

log₂(1.048.576)

Karena:

2²⁰ = 1.048.576

maka:

log₂(1.048.576) = 20

Artinya Binary Search hanya membutuhkan sekitar 20 kali pembagian untuk mencapai satu kandidat data.

Perhatikan perbandingan berikut:

Jumlah Datalog₂(n)
164
325
646
1287
2568
5129
1.02410
2.04811
4.09612
65.53616
1.048.57620

Inilah karakteristik O(log n): jumlah data dapat bertambah sangat besar, tetapi jumlah langkah pencarian bertambah relatif lambat.

Program Binary Search dengan JavaScript

Berikut contoh implementasinya:

<!DOCTYPE html>
<html>
<head>
    <title>Binary Search</title>
</head>
<body>

    <h2>Pencarian Data Mahasiswa</h2>

    <p>Data NIM:</p>

    <p>
        1001, 1002, 1003, 1004, 1005,
        1006, 1007, 1008, 1009, 1010
    </p>

    <input type="number" id="cari" placeholder="Masukkan NIM">

    <button onclick="cariData()">Cari</button>

    <p id="hasil"></p>

    <script>

        let data = [
            1001, 1002, 1003, 1004, 1005,
            1006, 1007, 1008, 1009, 1010
        ];

        function cariData() {

            let target =
                Number(document.getElementById("cari").value);

            let kiri = 0;
            let kanan = data.length - 1;

            let langkah = 0;
            let ditemukan = false;

            while (kiri <= kanan) {

                langkah++;

                let tengah =
                    Math.floor((kiri + kanan) / 2);

                if (data[tengah] === target) {

                    ditemukan = true;

                    document.getElementById("hasil").innerHTML =
                        "Data ditemukan: " + data[tengah] +
                        "<br>Index: " + tengah +
                        "<br>Jumlah langkah: " + langkah;

                    break;

                } else if (data[tengah] < target) {

                    kiri = tengah + 1;

                } else {

                    kanan = tengah - 1;
                }
            }

            if (ditemukan == false) {

                document.getElementById("hasil").innerHTML =
                    "Data tidak ditemukan" +
                    "<br>Jumlah langkah: " + langkah;
            }
        }

    </script>

</body>
</html>

Hubungan Setiap Bagian Program dengan Algoritma

Bagian:

let kiri = 0;
let kanan = data.length - 1;

digunakan untuk menentukan batas area pencarian.

Bagian:

let tengah = Math.floor((kiri + kanan) / 2);

digunakan untuk menentukan posisi tengah.

Bagian:

if (data[tengah] === target)

digunakan untuk memeriksa apakah data ditemukan.

Bagian:

else if (data[tengah] < target) {
    kiri = tengah + 1;
}

berarti target berada di sebelah kanan.

Sedangkan:

else {
    kanan = tengah - 1;
}

berarti target berada di sebelah kiri.

Dengan demikian, setiap perulangan mengurangi area pencarian.

Perbandingan Sequential Search dan Binary Search

Misalnya terdapat 1.000 data.

Sequential Search

Pencarian dilakukan satu per satu:

1001
1002
1003
1004
...

Kompleksitas:

O(n)

Binary Search

Pencarian dilakukan dengan membagi area menjadi dua:

1000
 ↓
500
 ↓
250
 ↓
125
 ↓
...

Kompleksitas:

O(log n)

Namun Binary Search memiliki syarat bahwa data harus sudah terurut.

Kesimpulan

O(log n) adalah kompleksitas logaritmik yang memiliki karakteristik bahwa ukuran masalah berkurang secara signifikan pada setiap langkah.

Binary Search merupakan contoh klasik algoritma O(log n).

Prinsip dasarnya sangat sederhana:

Cari data
↓
Ambil nilai tengah
↓
Bandingkan
↓
Buang setengah data
↓
Cari lagi
↓
Ulangi

Secara matematis:

n / 2ᵏ = 1

Maka:

n = 2ᵏ

Sehingga:

k = log₂(n)

Itulah alasan kompleksitas Binary Search adalah:

O(log n)

Konsep ini penting dalam Algoritma dan Pemrograman karena membantu kita memahami bahwa ketika jumlah data semakin besar, pemilihan algoritma dapat memberikan perbedaan yang sangat besar terhadap jumlah proses yang harus dilakukan komputer.

Kasus Data Tidak Ditemukan

Selain kondisi ketika data berhasil ditemukan, Binary Search juga harus menangani kondisi ketika data yang dicari tidak terdapat di dalam array.

Misalnya kita memiliki data:

1001, 1002, 1003, 1004, 1005,
1006, 1007, 1008, 1009, 1010

Kemudian kita mencari:

1015

Data 1015 tidak terdapat dalam array.

Langkah 1

Kondisi awal:

kiri = 0
kanan = 9

Hitung nilai tengah:

tengah = floor((0 + 9) / 2)
       = floor(4,5)
       = 4

Data pada index 4:

data[4] = 1005

Bandingkan:

1005 < 1015

Karena target lebih besar, pencarian dilanjutkan ke kanan.

kiri = tengah + 1
kiri = 5

Sekarang area pencarian:

1006  1007  1008  1009  1010

Langkah 2

Sekarang:

kiri = 5
kanan = 9

Hitung:

tengah = floor((5 + 9) / 2)
       = 7

Data:

data[7] = 1008

Bandingkan:

1008 < 1015

Maka pencarian kembali bergerak ke kanan:

kiri = tengah + 1
kiri = 8

Langkah 3

Sekarang:

kiri = 8
kanan = 9

Hitung:

tengah = floor((8 + 9) / 2)
       = floor(8,5)
       = 8

Data:

data[8] = 1009

Bandingkan:

1009 < 1015

Maka:

kiri = tengah + 1
kiri = 9

Langkah 4

Sekarang:

kiri = 9
kanan = 9

Hitung:

tengah = floor((9 + 9) / 2)
       = 9

Data:

data[9] = 1010

Bandingkan:

1010 < 1015

Maka:

kiri = tengah + 1
kiri = 10

Sekarang kondisi:

kiri = 10
kanan = 9

Perhatikan:

kiri > kanan

Maka kondisi while:

while (kiri <= kanan)

menjadi:

10 <= 9

Hasilnya:

false

Perulangan berhenti.

Karena data tidak ditemukan, program menjalankan:

if (ditemukan == false) {

    document.getElementById("hasil").innerHTML =
        "Data tidak ditemukan" +
        "<br>Jumlah langkah: " + langkah;
}

Output:

Data tidak ditemukan
Jumlah langkah: 4

Mengapa kiri > kanan Berarti Data Tidak Ditemukan?

Ini merupakan konsep penting dalam Binary Search.

Selama:

kiri <= kanan

masih terdapat area yang mungkin berisi data yang dicari.

Tetapi ketika:

kiri > kanan

area pencarian sudah kosong.

Contohnya:

kiri = 10
kanan = 9

Tidak mungkin lagi terdapat index yang berada di antara 10 dan 9.

Dengan demikian, kita dapat menyimpulkan bahwa data tidak ditemukan.

Visualisasi

Data:

1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
 |----|----|----|----|----|----|----|----|----|----|
  0    1    2    3    4    5    6    7    8    9

Target = 1015

Pencarian terus bergerak ke kanan:

1001 1002 1003 1004 1005 | 1006 1007 1008 1009 1010
                              ↓
                            kanan

Kemudian:

1006 1007 1008 1009 1010
                  ↓
                1009

Kemudian:

1010
  ↓
1010 < 1015

Akhirnya:

kiri = 10
kanan = 9

kiri > kanan
↓
Data tidak ditemukan

Perbedaan Dua Kondisi Akhir

Binary Search memiliki dua kemungkinan hasil:

1. Data ditemukan

Ketika:

data[tengah] === target

Program langsung berhenti.

Contoh:

Target = 1008

data[7] = 1008

1008 === 1008
→ Data ditemukan

2. Data tidak ditemukan

Ketika:

kiri > kanan

Program berhenti karena sudah tidak ada area yang dapat dicari.

Contoh:

Target = 1015

kiri = 10
kanan = 9

10 > 9
→ Data tidak ditemukan

Kompleksitas Saat Data Tidak Ditemukan

Menariknya, kasus data tidak ditemukan tetap memiliki kompleksitas:

O(log n)

Mengapa?

Karena meskipun data tidak ada, Binary Search tetap membagi area pencarian menjadi dua pada setiap langkah.

Misalnya terdapat 1.024 data:

1024
 ↓
512
 ↓
256
 ↓
128
 ↓
64
 ↓
32
 ↓
16
 ↓
8
 ↓
4
 ↓
2
 ↓
1
 ↓
0

Jumlah langkah tetap berada pada orde logaritmik.

Secara matematis:

log₂(1024) = 10

Jadi pencarian data yang tidak ditemukan juga dapat diselesaikan dalam sekitar 10 tahap pembagian, bukan harus memeriksa seluruh 1.024 data satu per satu.

Kesimpulan Kasus Data Tidak Ditemukan

Dalam Binary Search, kondisi berhenti dapat terjadi karena dua hal:

                Binary Search
                     │
            ┌────────┴────────┐
            │                 │
      Data ditemukan    Data tidak ditemukan
            │                 │
data[mid] === target    kiri > kanan
            │                 │
          STOP              STOP

Jadi, kondisi:

while (kiri <= kanan)

sangat penting karena menjadi batas selama masih ada area pencarian.

Ketika:

kiri > kanan

artinya seluruh kemungkinan posisi data sudah diperiksa dan data tersebut tidak terdapat dalam array.

Baik ketika data ditemukan maupun tidak ditemukan, Binary Search memiliki kompleksitas waktu O(log n) pada kasus pencarian berbasis pembagian dua seperti ini.

Leave a Reply

Your email address will not be published. Required fields are marked *