KELAS : 1C (sore)
NPM : 221055201115
JURUSAN : PRODI INFORMATIKA
PERTEMUAN : Ke – 2
1. Algoritma Rekursif
Salah satu konsep paling dasar dalam ilmu komputer dan pemrograman adalah pengunaan fungsi
sebagai abstraksi untuk kode-kode yang digunakan berulang kali. Kedekatan ilmu komputer
dengan matematika juga menyebabkan konsep-konsep fungsi pada matematika seringkali
dijumpai. Salah satu konsep fungsi pada matematika yang ditemui pada ilmu komputer adalah
fungsi rekursif: sebuah fungsi yang memanggil dirinya sendiri.
Kode berikut memperlihatkan contoh fungsi rekursif, untuk menghitung hasil kali dari dua
bilangan:
Bagaimana cara kerja fungsi rekursif ini? Sederhananya, selama nilai b bukan 1, fungsi akan terus
memanggil perintah a + kali(a, b - 1), yang tiap tahapnya memanggil dirinya sendiri
sambil mengurangi nilai b. Mari kita coba panggil fungsi kali dan uraikan langkah
pemanggilannya:
kali(2, 4)
-> 2 + kali(2, 3)
-> 2 + (2 + kali(2, 2))
-> 2 + (2 + (2 + kali(2, 1)))
-> 2 + (2 + (2 + 2))
-> 2 + (2 + 4)
-> 2 + 6
-> 8
def faktorial(n):
return n if n == 1 else n * faktorial(n - 1)
Fungsi faktorial memiliki cara kerja yang sama dengan fungsi kali. Mari kita panggil dan lihat l
angkah pemanggilannya: faktorial(5)
-> 5 * faktorial(4)
-> 5 * (4 * faktorial(3))
-> 5 * (4 * (3 * faktorial(2)))
-> 5 * (4 * (3 * (2 * faktorial(1))))
-> 5 * (4 * (3 * (2 * 1)))
-> 5 * (4 * (3 * 2))
-> 5 * (4 * 6)
-> 5 * 24
-> 120
Dengan melihat kemiripan cara kerja serta kode dari fungsi faktorial dan kali, kita dapat
melihat bagaimana fungsi rekursif memiliki dua ciri khas:
1. Fungsi rekursif selalu memiliki kondisi yang menyatakan kapan fungsi tersebut berhenti.
Kondisi ini harus dapat dibuktikan akan tercapai, karena jika tidak tercapai maka kita tidak
dapat membuktikan bahwa fungsi akan berhenti, yang berarti algoritma kita tidak benar.
2. Fungsi rekursif selalu memanggil dirinya sendiri sambil mengurangi atau memecahkan
data masukan setiap panggilannya. Hal ini penting diingat, karena tujuan utama dari
rekursif ialah memecahkan masalah dengan mengurangi masalah tersebut menjadi
masalah-masalah kecil.
Setiap fungsi rekursif yang ada harus memenuhi kedua persyaratan di atas untuk memastikan
fungsi rekursif dapat berhenti dan memberikan hasil. Kebenaran dari nilai yang dihasilkan tentu
saja memerlukan pembuktian dengan cara tersendiri. Tetapi sebelum masuk ke analisa dan
pembuktian fungsi rekursif, mari kita lihat kegunaan dan contoh-contoh fungsi rekursif lainnya
lagi.
Fungsi Rekursif dan Iterasi
Pembaca yang jeli akan menyadari bahwa kedua contoh fungsi rekursif yang diberikan
sebelumnya, faktorial dan kali, dapat diimplementasikan tanpa menggunakan fungsi
rekursif. Berikut adalah contoh kode untuk perhitungan faktorial tanpa menggunakan rekursif:
def faktorial_iterasi(n):
hasil = 1
for i in range(1, n + 1):
hasil = hasil * i
return hasil
Dalam menghitung nilai faktorial menggunakan iterasi, kita meningkatkan nilai hasil terus
menerus, sampai mendapatkan jawaban yang tepat. Yang perlu kita pastikan benar pada fungsi ini
adalah berapa kali kita harus meningkatkan nilai hasil. Jika jumlah peningkatan salah, maka hasil
akhir yang didapatkan juga akan salah.
Pendekatan iteratif berbeda dengan rekursif dalam hal ini: jika pendekatan rekursif memecah-
mecah masalah untuk kemudian menyelesaikan masalah sedikit demi sedikit, pendekatan iteratif
justru langsung mencoba menyelesaikan masalah, tanpa memecah-mecahkan masalah tersebut
menjadi lebih kecil terlebih dahulu. Untungnya, baik teknik iterasi maupun rekursi sama-sama
memiliki tingkat ekspresi yang sama: segala hal yang dapat diselesaikan dengan itearsi akan dapat
diselesaikan dengan rekursif. Lalu, kapan dan kenapa kita harus menggunakan rekursif?
Meskipun dapat menyelesaikan masalah yang sama, terdapat beberapa permasalahan atau solusi
yang lebih tepat diselesaikan dengan menggunakan fungsi rekursif. Salah satu contoh dari
masalah ini adalah penelurusan data di dalam sebuah binary tree. Sebuah binary tree, yang dapat
didefinisikan sebagai sebuah pohon dengan jumlah cabang yang selalu dua, secara alami adalah
struktur data rekursif.
Binary Tree
Sebagai struktur rekursif, tentunya penelusuran binary tree akan lebih mudah dilakukan secara
rekursif dibandingkan iterasi. Hal ini sangat kontras dengan, misalnya, pencarian karakter di dalam
string. Sebagai data yang disimpan secara linear, pencarian karakter dalam string akan lebih
mudah untuk dilakukan secara iteratif.
Untuk mempermudah ilustrasi, mari kita lakukan perbandingan antara implementasi rekursif dan
iteratif untuk masalah yang lebih cocok diselesaikan dengan masing-masing pendekatan.
Misalnya, implementasi algoritma euclidean untuk menghitung faktor persekutuan terbesar (FPB)
yang lebih cocok untuk diimplementasikan dengan metode rekursif seperti berikut:
Perhatikan bagaimana diperlukan lebih banyak pengecekan pada fungsi rekursif, serta tambahan
parameter pos yang berguna untuk menyimpan posisi pengujian dan ditemukannya elemen yang
dicari. Jika menggunakan iterasi variabel pos tidak dibutuhkan lagi karena posisi ini akan
didapatkan secara otomatis ketika sedang menelusuri list. Dengan melihat jumlah argumen dan
pengecekan yang harus dilakukan, dapat dilihat bahwa implementasi linear search menjadi lebih
sederhana dan mudah dengan menggunakan metode iterasi.
2. Tail Call
Sesuai definisinya, dalam membuat fungsi rekursif pada satu titik kita akan harus memanggil
fungsi itu sendiri. Pemanggilan diri sendiri di dalam fungsi tentunya memiliki konsekuensi
tersendiri, yaitu pengunaan memori. Dengan memanggil dirinya sendiri, secara otomatis sebuah
fungsi akan memerlukan memori tambahan, untuk menampung seluruh variabel baru serta proses
yang harus dilakukan terhadap nilai-nilai baru tersebut. Penambahan memori ini seringkali
menyebabkan stack overflow ketika terjadi pemanggilan rekursif yang terlalu banyak.
Untuk menanggulangi kesalahan stack overflow ini, para pengembang bahasa pemrograman
mengimplementasikan apa yang dikenal dengan tail call optimization. Dalam merancang dan
menganalisa algoritma rekursif, pengertian akan tail call optimization merupakan hal yang sangat
penting. Jadi, apa itu tail call?
Tail call merupakan pemanggilan fungsi sebagai perintah terakhir di dalam fungsi lain.
Sederhananya, ketika kita memanggil sebuah fungsi pada bagian akhir dari fungsi lain, kita
melakukan tail call, seperti pada kode di bawah:
def fungsi(x):
y = x + 10
return fungsi_lain(y)
Pada kode di atas, pemanggilan fungsi_lain sebagai kode terakhir yang dieksekusi
oleh fungsi dapat dikatakan sebagai tail call. Ingat juga bahwa pemanggilan tidak harus berada
di akhir fungsi secara fisik. Yang penting adalah bahwa kode terakhir yang dieksekusi adalah
pemanggilan fungsi lain:
def tail_call(n):
if n == 0:
return fungsi_1(n + 1)
else:
return fungsi_2(n)
def bukan_tail_call(n):
result = fungsi_lain(n % 5)
return result * 10
yang bukan merupakan tail call, karena kode terakhir yang dieksekusi (result * 10e) adalah
sebuah operasi, bukan pemanggilan fungsi. Cara kerja ini tentunya juga dibawa ke fungsi rekursif,
di mana:
def faktorial(n):
if n == 1:
return 1
else:
return n * faktorial(n - 1)
bukan merupakan tail call karena baik return 1 maupun n * faktorial(n - 1) bukanlah
pemanggilan fungsi. Ingat bahwa n * faktorial(n - 1) merupakan operator perkalian,
bukan pemanggilan fungsi karena faktorial(n - 1) akan harus mengembalikan hasil terlebih
dahulu agar bisa dikalikan dengan n. Jika ingin membuat fungsi rekursif yang memanfaatkan tail
call, kita harus memastikan kode terakhir yang dieksekusi adalah fungsi lain, tanpa operasi
lanjutan. Misalnya, kita dapat menyimpan hasil kalkulasi sebagai parameter, seperti berikut:
def faktorial(n):
return n if n == 1 else n * faktorial(n - 1)
Salah satu informasi yang didapatkan di sini adalah kapan algoritma berhenti melakukan rekursif,
yaitu n == 1. Informasi lain yang kita miliki adalah berkurangnya jumlah data pada setiap
pemanggilan faktorial. Bagaimana kita melakukan analisis algoritma ini? Cara termudahnya
adalah dengan menggambarkan fungsi matematika dari faktorial terlebih dahulu:
faktorial(n)={1n∗faktorial(n−1)remainder(x,y))if n=1if n>1faktorial(n)={1if n=1n∗faktorial(n−1)
remainder(x,y))if n>1
Melalui fungsi matematika ini, kita dapat mulai melakukan penurunan untuk fungsi perhitungan
langkah fungsi faktorialfaktorial untuk kasus n>1n>1:
f(n)=1+f(n−1)=1+1+f(n−2)=1+1+1+f(n−3)...=n+f(n−k)f(n)=1+f(n−1)=1+1+f(n−2)=1+1+1+f(n−3)..
.=n+f(n−k)
Dan tentunya kita dapat mengabaikan penambahan langkah nn di awal, serta dengan syarat
bahwa fungsi berhenti ketika n−k=1n−k=1:
n−kk=1=n−1
Maka dapat disimpulkan bahwa fungsi faktorial memiliki kompleksitas n−1n−1, atau O(n)O(n).
Ingat bahwa kunci dari perhitungan kompleksitas untuk algoritma rekursif terdapat pada fungsi
matematis algoritma dan definisi kapan berhentinya fungsi rekursif tersebut.
Kesimpulan
Fungsi rekursif merupakan fungsi yang memanggil dirinya sendiri. Terdapat dua komponen
penting dalam fungsi rekursif, yaitu kondisi kapan berhentinya fungsi dan pengurangan atau
pembagian data ketika fungsi memanggil dirinya sendiri. Optimasi fungsi rekursif dapat dilakukan
dengan menggunakan teknik tail call, meskipun teknik ini tidak selalu diimplementasikan oleh
semua bahasa pemrograman.
Selain sebagai fungsi, konsep rekursif juga terkadang digunakan untuk struktur data seperti binary
tree atau list.
Definisi formal dari rekursi
Rekursi dalam program perekaman layar, dengan suatu jendela paling kecil mengandung
foto keseluruhan layar.
Dalam matematika dan ilmu komputer, kelas dari objek atau metode memperlihatkan
perilaku rekursif bila mereka dapat didefinisikan oleh dua properti berikut:
0 ada dalam
• Rasio Golden:
• Faktorial:
• Bilangan Fibonacci:
• Bilangan Catalan: ,
3. Algoritma Divide and Conquer
print(total) # 255
Algoritma perulangan yang digunakan pada kode di atas memang sederhana dan memberikan
hasil yang benar, tetapi terdapat beberapa masalah pada kode tersebut, yaitu perhitungan
dilakukan secara linear, yang menghasilkan kompleksitas \(O(n)\). Hal ini tentunya cukup ideal
untuk ukuran list kecil, tetapi jika ukuran list menjadi besar (beberapa Milyar elemen) maka
perhitungan akan menjadi sangat lambat. Kenapa perhitungannya menjadi lambat? Karena nilai
dari total tergantung kepada kalkulasi nilai total sebelumnya. Kita tidak dapat melakukan
perhitungan total dari depan dan belakang list sekaligus, sehingga kita dapat mempercepat
perhitungan dua kali lipat. Dengan kode di atas, kita tidak dapat membagi-bagikan pekerjaan ke
banyak pekerja / CPU!
Lalu apa yang dapat kita lakukan? Langkah pertama yang dapat kita lakukan adalah menerapkan
teknik rekursif untuk membagi-bagikan masalah menjadi masalah yang lebih kecil. Jika awalnya
kita harus menghitung total keseluruhan list satu per satu, sekarang kita dapat melakukan
perhitungan dengan memecah-mecah list terlebih dahulu:
def sums(lst):
if len(lst) >= 1:
return lst[0]
mid = len(lst) // 2
left = sums(lst[:mid])
right = sums(lst[mid:])
print(sums(nums)) # 255
1. Baris if len(lst) >= 1 memberikan syarat pemberhentian fungsi rekursif, yang akan
mengembalikan isi dari list ketika list berukuran 1 (hanya memiliki satu elemen).
2. Baris mid = len(lst) // 2 mengambil median dari list, sebagai referensi ketika kita
membagi list menjadi dua bagian.
3. Baris left = sum(lst[:mid]) dan selanjutnya membagikan list menjadi dua bagian,
dengan nilai mid sebagai tengah dari list.
Singkatnya, setelah membagikan list menjadi dua bagian terus menerus sampai bagian
terkecilnya, kita menjumlahkan kedua nilai list tersebut, seperti pada gambar berikut:
Langkah-langkah
Langkah-langkah umum algoritme Divide and Conquer adalah: [1]
• Divide: Membagi masalah menjadi beberapa upa-masalah yang memiliki kemiripan
dengan masalah semula namun berukuran lebih kecil (idealnya berukuran hampir
sama).
• Conquer: Memecahkan (menyelesaikan) masing-masing upa-masalah (secara
rekursif).
• Combine: Menggabungkan solusi masing-masing masalah sehingga membentuk
solusi masalah semula
Divide and Conquer dulunya adalah strategi militer yang dikenal dengan nama divide ut imperes.
Sekarang strategi tersebut menjadi strategi fundamental di dalam ilmu komputer dengan nama
Divide and Conquer.
Definisi
• Divide: membagi persoalan menjadi beberapa upa-masalah yang memiliki kemiripan dengan
persoalan semula namun berukuran lebih kecil (idealnya berukuran hampir sama),
•Obyek persoalan yang dibagi : masukan (input) atau instances persoalan yang berukuran n
seperti:
- tabel (larik),
- matriks,
- eksponen,
• Tiap-tiap upa-masalah mempunyai karakteristik yang sama (the same type) dengan karakteristik
masalah asal
• sehingga metode Divide and Conquer lebih natural diungkapkan dengan skema rekursif.
r, k : integer
Algoritma
else
• Ukuran tabel hasil pembagian dapat dibuat cukup kecil sehingga mencari minimum dan
maksimum dapat diselesaikan (SOLVE) secara trivial.
• Dalam hal ini, ukuran “kecil” yang dipilih adalah 1 elemen atau 2 elemen.
Algoritma:
sama, A1 dan A2
min2, maks2)
(c) COMBINE: if min1 < min2 then min min1 else min min2 if
{ Mengurutkan tabel A dengan metode Divide and Conquer Masukan: Tabel A dengan n elemen
Keluaran: Tabel A yang terurut }
Algoritma:
if Ukuran(A) > 1 then Bagi A menjadi dua bagian, A1 dan A2, masing-masing berukuran n1 dan n2
(n = n1 + n2)
Combine (A1, A2, A) { gabung hasil pengurutan bagian kiri dan bagian kanan } end
Merge Sort
Algoritma:
1. Untuk kasus n = 1, maka tabel A sudah terurut dengan sendirinya (langkah SOLVE).
(a) DIVIDE: bagi tabel A menjadi dua bagian, bagian kiri dan bagian kanan, masing-masing bagian
berukuran n/2 elemen. (
(c) MERGE: gabung hasil pengurutan kedua bagian sehingga diperoleh tabel A yang terurut.
• Kompleksitas waktu:
Asumsi: n = 2k T(n) = jumlah perbandingan pada pengurutan dua buah upatabel + jumlah
perbandingan pada prosedur Merge + = = 2 ( / 2) , 1
Penyelesaian:
T(n) = 2T(n/2) + cn
= ...
= 2k T(n/2k ) +kcn
n/2k = 1 → k = 2 log n
sehingga
Contoh:
A 4 12 3 9 1 21 5 2
1.Mudah membagi, sulit menggabung (easy split/hard join) Tabel A dibagidua berdasarkan posisi
elemen:
Divide: A1 4 12 3 9 A2 1 21 5 2
Sort: A1 3 4 9 12 A2 1 2 5 21
Combine: A1 1 2 3 4 5 9 12 21
2.Sulit membagi, mudah menggabung (hard split/easy join) Tabel A dibagidua berdasarkan nilai
elemennya. Misalkan elemen-elemen A1 elemen-elemen A2.
A 4 12 3 9 1 21 5 2
Divide: A1 4 2 3 1 A2 9 21 5 12
Sort: A1 1 2 3 4 A2 5 9 12 21
Combine: A 1 2 3 4 5 9 12 21
Dynamic Programming (selanjutnya disebut “DP” saja) merupakan salah satu teknik perancangan
algoritma yang dikembangkan untuk menyelesaikan permasalahan yang sangat kompleks dengan
memecah permasalahan tersebut menjadi banyak sub-permasalahan. Perbedaan utama DP
dengan Divide and Conquer (selanjutnya disebut “D&C”) adalah pada DP kita menggunakan
kembali hasil kalkulasi sub-masalah yang telah dilakukan sebelumnya. Apa artinya?
Untuk mempermudah penjelasan, mari kita selesaikan masalah sederhana yang telah kita bahas
berkali-kali: perhitungan bilangan fibonacci. Algoritma untuk menyelesaikan perhitungan fibonacci
secara naif adalah seperti berikut:
def fibonacci(n):
if n <= 2:
hasil = 1
else:
hasil = fibonacci(n - 1) + fibonacci(n - 2)
return hasil
Algoritma fibonacci sederhana seperti di atas dapat dikatakan sebagai algoritma D&C, karena kita
membagikan perhitungan fibonacci ke dua fungsi fibonacci, sampai didapatkan nilai hasil
terkecilnya. Pemanggilan fungsi fibonacci di atas dapat digambarkan seperti berikut:
memo = dict()
def fibonacci_dp(n):
if n in memo.keys():
return memo[n]
elif n <= 2:
hasil = 1
else:
hasil = fibonacci_dp(n - 1) + fibonacci_dp(n - 2)
memo[n] = hasil
return hasil
Dengan menyimpan hasil kalkulasi dari fungsi yang telah ada, maka proses pemanggilan fungsi
akan menjadi seperti berikut
def fibonacci_dp_bu(n):
memo = dict()
for i in range(1, n + 1):
if i <= 2:
hasil = 1
else:
hasil = memo[i - 1] + memo[i - 2]
memo[i] = hasil
return memo[n]
Untuk melihat efek langsung dari ketiga fungsi tersebut, coba jalankan ketiga fungsi tersebut
untuk n yang sama, dan lihat perbedaan waktu eksekusinya! Sebagai latihan tambahan, hitung
juga kompleksitas dari ketiga fungsi perhitungan fibonacci tersebut.
Mari kita rangkum hal yang telah kita pelajari mengenai DP sejauh ini:
Kegunaan utama dari DP adalah untuk menyelesaikan masalah optimasi. Permasalahan optimasi
artinya permasalahan yang mencari nilai terbaik, baik maksimal maupun minimal, dari sebuah
solusi. Salah satu contoh paling praktis dalam penerapan DP model ini adalah algoritma untuk
membuat teks rata tengah. Bagaimana cara kerja algoritma ini? Mari kita lihat masalah yang ingin
diselesaikan terlebih dahulu.
Pada aplikasi pengolah kata seperti Microsoft Word, biasanya terdapat fitur untuk menentukan
kemerataan teks yang ada pada paragraf, seperti yang nampak pada gambar di bawah:
1. Ukuran dari halaman, yang tentunya akan mempengaruhi berapa lebar maksimal dari
sebuah teks.
2. Ukuran setiap kata yang ada dalam teks, untuk menghitung berapa banyak kata yang
dapat dimasukkan ke dalam satu baris teks.
3. Ukuran spasi dalam teks, seperti ukuran kata, untuk menghitung jumlah kata yang dapat
dimasukkan ke dalam teks.
4. Ukuran karakter-karakter khusus seperti ”!”, ”?”, ”,”,”.”, dan lainnya. Meskipun biasanya
berukuran kecil, karakter khusus tetap berperan dalam mengisi ruang tulisan.
Dengan melakukan kalkulasi sederhana dari teks, tentunya kita bisa saja melakukan pemerataan
teks dengan mudah. Misalnya, untuk menghitung total teks yang dapat masuk ke dalam sebuah
baris tulisan, kita dapat menggunakan persamaan berikut:
[Math Processing Error]ukuran halaman←total ukuran kata+total ukuran spasi+total ukuran
simbol
Sehingga untuk membuat sebuah teks menjadi rata penuh (justified) kita dapat memasukkan
setiap kata, spasi, dan simbol satu demi satu sampai kita memenuhi sebuah baris. Jika kata
selanjutnya tidak lagi memiliki ukuran yang cukup, maka kita dapat menambahkan spasi di
tengah-tengah kata sebelumnya sampai baris penuh, dan lalu berpindah baris.
Secara sederhana, algoritma naif untuk melakukan rata penuh teks adalah seperti berikut:
1. Ambil satu elemen dalam teks, baik berupa kata, simbol, maupun spasi. Masukkan elemen
ini ke dalam baris.
2. Hitung ukuran baris sekarang.
3. Ambil satu elemen lagi dalam teks, dan hitung ukurannya.
4. Tambahkan ukuran baris sekarang dengan ukuran elemen berikutnya. Hasil pengukuran
ini selanjutnya akan disebut “Ukuran Baris Selanjutnya” atau UBS.
5. Cek nilai UBS:
1. Jika UBS masih lebih kecil dari lebar halaman, kembali ke langkah 1
2. Jika UBS sudah lebih dari lebar halaman:
1. Tambahkan spasi di antara setiap kata dalam baris sampai ukuran baris sama
dengan lebar halaman.
Secara kasar, algoritma di atas dapat diimplementasikan seperti kode berikut (yang jelas tidak
dapat dijalankan):
lines = [[next]]
current_line = 0
while(!text.empty()):
while(next_total_size < page_size):
total_size = next_total_size
next = text.get_next()
lines[current_line].push(next)
next_total_size = total_size + next.size()
current_line = current_line + 1
Hasil algoritma di atas kurang optimal, karena ketika terdapat kata-kata yang panjang dalam
sebuah kalimat, kita terpaksa harus memotong baris terlalu cepat, dan akhirnya menambahkan
banyak spasi. Contoh eksekusi dari algoritma di atas dapat dilihat pada gambar berikut:
Perhitungan nilai keburukan sendiri dapat dilakukan dengan menggunakan rumus sederhana
berikut:
[Math Processing Error]keburukan(i,j)={ukuran baris>lebar halaman∞(lebar halaman−ukuran
baris)3
dengan [Math Processing Error]i dan [Math Processing Error]j sebagai awal dan akhir dari kata
yang ingin dihitung tingkat keburukannya. Jika dijadikan kode program, algoritma tersebut dapat
dituliskan seperti berikut:
s = dict()
return s
Perlu dicatat bahwa kode di atas belum mengikut sertakan spasi dalam perhitungan, dan juga
belum membangun kembali baris-baris yang telah dipecah menjadi sebuah teks (paragraf).
Kesimpulan
Secara sederhana, teknik DP dapat dikatakan adalah sebuah teknik brute force yang pintar. Kita
memecah-mecah masalah menjadi sub-masalah, dan menyelesaikan seluruh sub-masalah
tersebut. Perbedaan utama dari DP dengan D&C adalah DP melakukan penyimpanan hasil
penyelesaian sub-masalah sehingga kita tidak perlu menyelesaikan sub-masalah yang sama
berulang kali
Contoh:
Karakteristikkan struktur solusi optimal
(c) • Maka rute yang dilalui adalah x1→x2→x3→x4 → x5=10 Pada persoalan ini,
(d) • Tahap (k) adalah proses memilih simpul tujuan berikutnya (ada 4 tahap).
(e) • Status (s) yang berhubungan dengan masing-masing tahap adalah simpulsimpul
di dalam graf multi-tahap.
4. ALGORITMA GREEDY
Algoritma greedy merupakan jenis algoritma yang menggunakan pendekatan penyelesaian masalah
dengan mencari nilai maksimum sementara pada setiap langkahnya. Nilai maksimum sementara ini
dikenal dengan istilah local maximum. Pada kebanyakan kasus, algoritma greedy tidak akan
menghasilkan solusi paling optimal, begitupun algoritma greedy biasanya memberikan solusi yang
mendekati nilai optimum dalam waktu yang cukup cepat.
Sebagai contoh dari penyelesaian masalah dengan algoritma greedy, mari kita lihat sebuah masalah
klasik yang sering dijumpai dalam kehidupan sehari-hari: mencari jarak terpendek dari peta.
Misalkan kita ingin bergerak dari titik A ke titik B, dan kita telah menemukan beberapa jalur dari
peta:
Jalur dari Titik A ke B
Jalur dari Titik A ke B
Dari peta yang ditampilkan di atas, dapat dilihat bahwa terdapat beberapa jalur dari titik A ke titik
B. Sistem peta pada gambar secara otomatis telah memilih jalur terpendek (berwarna biru). Kita
akan mencoba mencari jalur terpendek juga, dengan menggunakan algoritma greedy.
Langkah pertama yang harus kita lakukan tentunya adalah memilih struktur data yang tepat untuk
digunakan dalam merepresentasikan peta. Jika dilihat kembali, sebuah peta seperti pada gambar di
atas pada dasarnya hanya menunjukkan titik-titik yang saling berhubungan, dengan jarak tertentu
pada masing-masing titik tersebut. Misalnya, peta di atas dapat direpresentasikan dengan titik-titik
penghubung seperti berikut:
Graph Sederhana dari Titik A ke B
Graph Sederhana dari Titik A ke B
Dari gambar di atas, kita dapat melihat bagaimana sebuah peta jalur perjalanan dapat
direpresentasikan dengan menggunakan graph, spesifiknya Directed Graph (graph berarah). Maka
dari itu, untuk menyelesaikan permasalahan jarak terpendek ini kita akan menggunakan struktur
data graph untuk merepresentasikan peta. Berikut adalah graph yang akan digunakan:
Graph Berarah dari Titik A ke B
Graph Berarah dari Titik A ke B
Untuk mencari jarak terpendek dari A ke B, sebuah algoritma greedy akan menjalankan langkah-
langkah seperti berikut:
Kunjungi satu titik pada graph, dan ambil seluruh titik yang dapat dikunjungi dari titik sekarang.
Cari local maximum ke titik selanjutnya.
Tandai graph sekarang sebagai graph yang telah dikunjungi, dan pindah ke local maximum yang
telah ditentukan.
Kembali ke langkah 1 sampai titik tujuan didapatkan.
Jika mengapliikasikan langkah-langkah di atas pada graph A ke B sebelumnya maka kita akan
mendapatkan pergerakan seperti berikut:
Mulai dari titik awal (A). Ambil seluruh titik yang dapat dikunjungi.
Langkah Pertama Greedy
Langkah Pertama Greedy
Local maximum adalah ke C, karena jarak ke C adalah yang paling dekat.
Tandai A sebagai titik yang telah dikunjungi, dan pindah ke C.
Ambil seluruh titik yang dapat dikunjungi dari C.
Langkah Kedua Greedy
Langkah Kedua Greedy
Local maximum adaah ke D, dengan jarak 6.
Tandai C sebagai titik yang telah dikunjungi, dan pindah ke D.
Langkah Ketiga Greedy
Langkah Ketiga Greedy
(Langkah selanjutnya diserahkan kepada pembaca sebagai latihan).
Dengan menggunakan algoritma greedy pada graph di atas, hasil akhir yang akan didapatkan
sebagai jarak terpendek adalah A-C-D-E-F-B. Hasi jarak terpendek yang didapatkan ini tidak tepat
dengan jarak terpendek yang
sebenarnya (A-G-E-F-B). Algoritma greedy memang tidak selamanya memberikan solusi yang
optimal, dikarenakan pencarian local maximum pada setiap langkahnya, tanpa memperhatikan
solusi secara keseluruhan. Gambar berikut memperlihatkan bagaimana algoritma greedy dapat
memberikan solusi yang kurang optimal:
Solusi Kurang Optimal dari Greedy
Solusi Kurang Optimal dari Greedy
Tetapi ingat bahwa untuk kasus umum, kerap kali algoritma greedy memberikan hasil yang cukup
baik dengan kompleksitas waktu yang cepat. Hal ini mengakibatkan algoritma greedy sering
digunakan untuk menyelesaikan permasalahan kompleks yang memerlukan kecepatan jawaban,
bukan solusi optimal, misalnya pada game.
Implementasi Algoritma Greedy
Untuk memperdalam pengertian algoritma greedy, kita akan mengimplementasikan algoritma yang
telah dijelaskan pada bagian sebelumnya ke dalam kode program. Seperti biasa, contoh kode
program akan diberikan dalam bahasa pemrograman python. Sebagai langkah awal, tentunya kita
terlebih dahulu harus merepresentasikan graph. Pada implementasi yang kita lakukan, graph
direpresentasikan dengan menggunakan dictionary di dalam dictionary, seperti berikut:
DAG = {'A': {'C': 4, 'G': 9},
'G': {'E': 6},
'C': {'D': 6, 'H': 12},
'D': {'E': 7},
'H': {'F': 15},
'E': {'F': 8},
'F': {'B': 5}
# Hasil Representasi:
{'A': {'C': 4, 'G': 9},
'C': {'D': 6, 'H': 12},
'D': {'E': 7},
'E': {'F': 8},
'F': {'B': 5},
'G': {'E': 6},
'H': {'F': 15}}
Selanjutnya kita akan membuat fungsi yang mencari jarak terpendek dari graph yang dibangun,
dengan menggunakan algoritma greedy. Definisi dari fungsi tersebut sangat sederhana, hanya
sebuah fungsi yang mengambil graph, titik awal, dan titik akhir sebagai argumennya:
def shortest_path(graph, source, dest):
Jarak terpendek yang didapatkan akan dibangun langkah demi langkah, seperti pada algoritma
greedy yang mengambil nilai local maximum pada setiap langkahnya. Untuk hal ini, tentunya kita
akan perlu menyimpan jarak terpendek ke dalam sebuah variabel, dengan source sebagai isi awal
variabel tersebut. Jarak terpendek kita simpan ke dalam sebuah list, untuk menyederhanakan proses
penambahan nilai.
result = []
result.append(source)
Penelusuran graph sendiri akan kita lakukan melalui result, karena variabel ini merepresentasikan
seluruh node yang telah kita kunjungi dari keseluruhan graph. Variabel result pada dasarnya
merupakan hasil implementasi dari langkah 3 algoritma (“Tandai graph sekarang sebagai graph
yang telah dikunjungi”). Titik awal dari rute tentunya secara otomatis ditandai sebagai node yang
telah dikunjungi.
Selanjutnya, kita akan menelusuri graph sampai titik tujuan ditemukan, dengan menggunakan
iterasi:
while dest not in result:
current_node = result[-1]
dengan mengambil node yang sekarang sedang dicari local maximum-nya dari isi terakhir result.
Pencarian local maximum sendiri lebih memerlukan pengetahuan python daripada algoritma:
# Cari local maximum
local_max = min(graph[current_node].values())
# Ambil node dari local maximum,
# dan tambahkan ke result
# agar iterasi selanjutnya dimulai
# dari node sekarang.
for node, weight in graph[current_node].items():
if weight == local_max:
result.append(node)
Setelah seluruh graph ditelurusi sampai mendapatkan hasil, kita dapat mengembalikan result ke
pemanggil fungsi:
return result
Keseluruhan fungsi yang dibangun adalah sebagai berikut:
def shortest_path(graph, source, dest):
result = []
result.append(source)
while dest not in result:
current_node = result[-1]
local_max = min(graph[current_node].values())
for node, weight in graph[current_node].items():
if weight == local_max:
result.append(node)
return result
Perlu diingat bahwa fungsi ini masih banyak memiliki kekurangan, misalnya tidak adanya
penanganan kasus jika titik tujuan tidak ditemukan, atau jika terdapat node yang memiliki nilai
negatif (bergerak balik). Penanganan hal-hal tersebut tidak dibuat karena fungsi hanya bertujuan
untuk mengilustrasikan cara kerja algoritma greedy, bukan untuk digunakan pada aplikasi nyata.
Algoritma greedy merupakan algoritma yang besifat heuristik, mencari nilai maksimal sementara
dengan harapan akan mendapatkan solusi yang cukup baik. Meskipun tidak selalu mendapatkan
solusi terbaik (optimum), algoritma greedy umumnya memiliki kompleksitas waktu yang cukup
baik, sehingga algoritma ini sering digunakan untuk kasus yang memerlukan solusi cepat meskipun
tidak optimal seperti sistem real-time atau game.
Dari impementasi yang kita lakukan, dapat dilihat bagaimana algoritma greedy memiliki beberapa
fungsionalitas dasar, yaitu:
Fungsi untuk melakukan penelusuran masalah.
Fungsi untuk memilih local maximum dari pilihan-pilihan yang ada tiap langkahnya.
Fungsi untuk mengisikan nilai local maximum ke solusi keseluruhan.
Fungsi yang menentukan apakah solusi telah didapatkan.
Tentunya fungsi-fungsi di atas juga dapat digabungkan atau dipecah lebih lanjut lagi, menyesuaikan
dengan strategi greedy yang dikembangkan.
Contoh penyelesaian masalah menggunakan algoritma greedy
Dalam memasarkan produk minuman dan makanan ringan, Indonesia masih banyak menggunakan
tenaga manusia untuk menyalurkan produk tersebut dari pabrik sampai ke konsumen akhir. Jika
setiap toko membeli produk dalam jumlah banyak, maka didapatkan keuntungan yang besar dalam
penjualan produk tersebut. Hal ini menyebabkan harga penjualan produk yang sampai ke konsumen
akhir lebih mahal daripada harga asli yang diberikan oleh pabrik. Dengan permasalahan tersebut,
penulis mencoba membuat aplikasi mesin penjual otomatis (vending machine). Pada umumnya
vending machine tidak memberikan uang kembalian. Disini penulis mencoba membuat vending
machine dengan menerapkan algoritma Greedy agar dapat memberikan uang kembalian sehingga
harga penjualan produk sesuai dengan harga asli pabrik. Algoritma Greedy diterapkan untuk
menentukan pecahan berapa saja yang muncul dalam proses pengembalian uang dengan
meminimalkan jumlah uang logamnya. Penulis menggunakan aplikasi Visual Basic untuk
menerapkan program vending machine.
Contoh-contoh Algoritma Greedy
Diposting oleh Unknown 14:01:00 1 comment
1. Masalah penukaran uang
Nilai uang yang ditukar: A
Himpunan koin (multiset): {d1, d2, …, dn}.
Himpunan solusi: X = {x1, x2, …, xn},
xi = 1 jika di dipilih, xi = 0 jika di tidak dipilih.
Contoh Contoh Algoritma Greedy
Related: Klasifikasi Berbasis Artificial Neural Network
Penyelesaian dengan exhaustive search
Untuk lebih jelasnya apa itu algoritma exhaustive search anda bisa membacanya pada artikel ini
"Contoh Implementasi Algoritma Exhaustive Search "
Terdapat 2n kemungkinan solusi
(nilai-nilai X = {x1, x2, …, xn} )
Untuk mengevaluasi fungsi obyektif = O(n)
Kompleksitas algoritma exhaustive search seluruhnya = O(n 2n ).
Penyelesaian dengan algoritma greedy
Strategi greedy: Pada setiap langkah, pilih koin dengan nilai terbesar dari himpunan koin yang
tersisa.
Contoh Contoh Algoritma Greedy
Agar pemilihan koin berikutnya optimal, maka perlu mengurutkan himpunan koin dalam urutan
yang menurun (noninceasing order).
Jika himpunan koin sudah terurut menurun, maka kompleksitas algoritma greedy = O(n).
Sayangnya, algoritma greedy untuk masalah penukaran uang ini tidak selalu menghasilkan solusi
optimal (lihat contoh sebelumnya).
2. Minimisasi Waktu di dalam Sistem (Penjadwalan)
Related: Algoritma Fuzzy C-Means
Persoalan: Sebuah server (dapat berupa processor, pompa, kasir di bank, dll) mempunai n
pelanggan (customer, client) yang harus dilayani. Waktu pelayanan untuk setiap pelanggan i adalah
ti. Minimumkan total waktu di dalam sistem:
T= Rumus Contoh Algoritma Greedy (Waku di dalam sistem)
Ekivalen dengan meminimumkan waktu rata-rata pelanggan di dalam sistem. akan selalu
menghasilkan solusi optimum.
Teorema. Jika t1 ≤ t2 ≤ … ≤ tn maka pengurutan ij = j, 1 ≤ j ≤ n meminimumkan
T = Algoritma Greedy
untuk semua kemungkinan permutasi
Salah satu contoh dari penyelesaian masalah dengan algoritma greedy yaitu mencari jarak
terpendek dari peta :
Misalkan kita ingin bergerak dari titik A ke titik I, dan kita telah menemukan beberapa jalur dari
peta:
Dari peta yang ditampilkan di atas, dapat dilihat bahwa terdapat beberapa jalur dari titik A ke titik I.
Sistem peta pada gambar secara otomatis telah memilih jalur terpendek (berwarna biru). Kita akan
mencoba mencari jalur terpendek juga, dengan menggunakan algoritma greedy.
Graph Sederhana dari Titik A ke I
Dari gambar di atas, kita dapat melihat bagaimana sebuah peta jalur perjalanan dapat
direpresentasikan dengan menggunakan graph, spesifiknya Directed Graph (graph berarah). Maka
dari itu, untuk menyelesaikan permasalahan jarak terpendek ini kita akan menggunakan struktur
data graph untuk merepresentasikan peta. Berikut adalah graph yang akan digunakan:
Graph Berarah dari Titik A ke I
Untuk mencari jarak terpendek dari A ke B, sebuah algoritma greedy akan menjalankan langkah-
langkah seperti berikut:
a. Kunjungi satu titik pada graph, dan ambil seluruh titik yang dapat dikunjungi dari titik sekarang.
b. Cari local maximum ke titik selanjutnya.
c. Tandai graph sekarang sebagai graph yang telah dikunjungi, dan pindah ke local maximum yang
telah ditentukan.
d. Kembali ke langkah 1 sampai titik tujuan didapatkan.
Dengan menggunakan algoritma greedy pada graph di atas, hasil akhir yang akan didapatkan
sebagai jarak terpendek adalah A-C-D-G-I. Hasi jarak terpendek yang didapatkan ini tidak tepat
dengan jarak terpendek yang sebenarnya (A-B-H-I). Algoritma greedy memang tidak selamanya
memberikan solusi yang optimal, dikarenakan pencarian local maximum pada setiap langkahnya,
tanpa memperhatikan solusi secara keseluruhan.
5. Algoritma Brute Force
Pengantar Algoritma Brute Force
“Data adalah minyak baru” ini adalah mantra baru yang mengatur
ekonomi global. Kita hidup di dunia digital, dan setiap bisnis berputar di
sekitar data, yang diterjemahkan menjadi keuntungan dan membantu
industri tetap terdepan dalam persaingan mereka. Dengan digitalisasi yang
cepat, peningkatan eksponensial dalam model bisnis berbasis aplikasi,
kejahatan dunia maya adalah ancaman yang konstan. Salah satu aktivitas
umum yang dilakukan peretas adalah Brute force.
Brute Force adalah pendekatan coba-coba di mana penyerang
menggunakan program untuk mencoba berbagai kombinasi untuk
membobol situs web atau sistem apa pun. Mereka menggunakan perangkat
lunak otomatis untuk menghasilkan kombinasi id dan kata sandi berulang
kali hingga akhirnya menghasilkan kombinasi yang tepat.
Brute-Force Search
Brute-Force Search adalah algoritma pencarian yang paling umum
karena tidak memerlukan pengetahuan domain apa pun; semua yang
diperlukan adalah deskripsi negara, operator hukum, status awal dan
deskripsi negara tujuan. Itu tidak meningkatkan kinerja dan sepenuhnya
bergantung pada daya komputasi untuk mencoba kemungkinan kombinasi.
Algoritma brute force mencari semua posisi dalam teks antara 0 dan n-
m, apakah kemunculan pola dimulai di sana atau tidak. Setelah setiap
upaya, itu menggeser pola ke kanan dengan tepat 1 posisi. Kompleksitas
waktu dari algoritma ini adalah O(m*n). Jika kita mencari n karakter
dalam string m karakter, maka dibutuhkan n*m percobaan.
Mari kita lihat contoh klasik dari seorang salesman keliling untuk
memahami algoritme dengan cara yang mudah.
Misalkan seorang penjual perlu melakukan perjalanan ke 10 kota
berbeda di suatu negara, dan dia ingin menentukan rute terpendek dari
semua kombinasi yang mungkin. Di sini algoritma brute force hanya
menghitung jarak antara semua kota dan memilih yang terpendek. Contoh
lain adalah mencoba membobol 5 digit kata sandi; maka kekerasan
mungkin memerlukan hingga 105 upaya untuk memecahkan kode.
Brute Force Sort
Dalam teknik pengurutan brute force, daftar data dipindai beberapa kali
untuk menemukan elemen terkecil dalam daftar. Setelah setiap iterasi di
atas daftar, itu menggantikan elemen terkecil ke bagian atas tumpukan dan
memulai iterasi berikutnya dari data terkecil kedua dalam daftar.
Di sini masalahnya adalah ukuran 'n', dan operasi dasarnya adalah tes
'jika' di mana item data dibandingkan dalam setiap iterasi. Tidak akan ada
perbedaan antara kasus terburuk dan terbaik karena jumlah swap selalu n-
1.
Brute Force String Matching
Jika semua karakter dalam pola unik, maka pencocokan string Brute
force dapat diterapkan dengan kompleksitas Big O(n) di mana n adalah
panjang string. Brute force Pencocokan string membandingkan pola
dengan substring karakter teks demi karakter hingga mendapatkan karakter
yang tidak cocok. Segera setelah ketidakcocokan ditemukan, karakter
substring yang tersisa dihapus, dan algoritme berpindah ke substring
berikutnya.
Kode semu di bawah ini menjelaskan logika pencocokan string. Di sini
algoritma mencoba mencari pola P[0…m-1] pada teks T[0….n-1].
Di sini kasus terburuk adalah ketika pergeseran ke substring lain tidak
dilakukan sampai perbandingan MTh.
Closest Pair
Rumusan masalah: Untuk mengetahui dua titik terdekat dalam
himpunan n titik pada bidang kartesius dua dimensi. Ada n jumlah
skenario di mana masalah ini muncul. Contoh kehidupan nyata adalah
dalam sistem kontrol lalu lintas udara di mana Anda harus memantau
pesawat yang terbang berdekatan satu sama lain, dan Anda harus
mencari tahu jarak minimum teraman yang harus dijaga oleh pesawat-
pesawat ini.
Algoritma brute force menghitung jarak antara setiap set titik yang
berbeda dan mengembalikan indeks titik yang jaraknya terkecil.
Brute force memecahkan masalah ini dengan kompleksitas waktu
[O(n2)] di mana n adalah jumlah titik.
Di bawah pseudo-code menggunakan algoritma brute force untuk
menemukan titik terdekat.
Convex Hull
Pernyataan Masalah: Lambung cembung adalah poligon terkecil
yang berisi semua titik. Lambung cembung dari himpunan titik s adalah
poligon cembung terkecil yang berisi s.
Exhaustive Search
Untuk masalah diskrit di mana tidak ada solusi efisien yang
diketahui, menjadi perlu untuk menguji setiap solusi yang mungkin
secara berurutan.
Exhaustive search adalah kegiatan untuk menemukan semua
kemungkinan pemecahan masalah secara sistematis.
Mari kita coba memecahkan masalah Travelling salesman (TSP)
menggunakan algoritma pencarian Brute exhaustive.
Problem Statement: Ada n kota yang harus dilalui oleh salesman, dia
ingin mencari rute terpendek yang mencakup semua kota.
Kami sedang mempertimbangkan Sirkuit Hamilton untuk
menyelesaikan masalah ini. Jika ada sirkuit, maka titik mana pun dapat
memulai dan mengakhiri simpul. Setelah simpul awal dipilih, maka kita
hanya membutuhkan urutan simpul yang tersisa, yaitu n-1
Maka akan ada (n-1)! Kemungkinan kombinasi dan total biaya
untuk menghitung jalur adalah O(n). dengan demikian total
kompleksitas waktu akan menjadi O(n!).
Algoritma Brute Force persis seperti yang terdengar – metode
langsung untuk memecahkan masalah yang mengandalkan kekuatan
komputasi belaka dan mencoba setiap kemungkinan daripada teknik
canggih untuk meningkatkan efisiensi.
Misalnya, bayangkan Anda memiliki gembok kecil dengan 4 angka,
masing-masing dari 0-9. Anda lupa kombinasi Anda, tetapi Anda tidak
ingin membeli gembok lagi. Karena Anda tidak dapat mengingat angka
apa pun, Anda harus menggunakan metode brute force untuk membuka
kunci.
Jadi Anda mengatur semua angka kembali ke 0 dan mencobanya
satu per satu: 0001, 0002, 0003, dan seterusnya sampai terbuka. Dalam
skenario terburuk, dibutuhkan 104, atau 10.000 percobaan untuk
menemukan kombinasi Anda.
Mengevaluasi polinom
• Persoalan: Hitung nilai polinom
p(x) = an x n + an – 1 x n – 1 + … + a1 x + a0 pada x = t.
• Algoritma brute force: x k dihitung secara brute force (seperti pada
perhitungan a n ). Kalikan nilai x k dengan ak , lalu jumlahkan dengan
suku-suku lainnya.
function polinom(t : real)→real
{ Menghitung nilai p(x) pada x = t. Koefisien-koefisein polinom sudah
disimpan di dalam a[0..n].
Masukan: t
Keluaran: nilai polinom pada x = t. }
Deklarasi
i, j : integer
p, pangkat : real
Algoritma:
p0
for i n downto 0 do
pangkat 1
for j 1 to i do {hitung t i }
pangkat pangkat * t
endfor
p p + a[i] * pangkat
endfor
return p
Jumlah operasi perkalian: n(n + 1)/2 + (n + 1) Kompleksitas waktu
algoritma: O(n 2 ).
Perbaikan (improve): Nilai pangkat pada suku sebelumnya (x n – 1 )
digunakan untuk perhitungan pangkat pada suku sekarang
function polinom2(t : real)→real { Menghitung nilai p(x) pada x = t.
Koefisien-koefisein polinom sudah disimpan di dalam a[0..n].
Masukan: t
Keluaran: nilai polinom pada x = t. }
Deklarasi
i, j : integer
p, pangkat : real
Algoritma:
p a[0]
pangkat 1
for i 1 to n do
pangkat pangkat * t
p p + a[i] * pangkat
endfor
Jumlah operasi perkalian: 2n Kompleksitas algoritma ini adalah O(n).
Adakah algoritma perhitungan nilai polinom yang lebih mangkus
daripada brute force?
1. Travelling Salesperson Problem (TSP)
Contoh 3:
TSP dengan n = 4, simpul awal = a
(4 – 1)! = 3! = 6
buah rute perjalanan.
• Jika TSP diselesaikan dengan exhaustive search, maka kita harus
mengenumerasi sebanyak (n – 1)! buah sirkuit Hamilton,
menghitung bobot setiap sirkuitnya, lalu memilih sirkuit
Hamilton dengan bobot terkecil.
• Kompleksitas waktu algoritma exhaustive search untuk persoalan
TSP sebanding dengan (n – 1)! dikali dengan waktu untuk
menghitung bobot setiap sirkuit Hamilton.
• Menghitung bobot setiap sirkuit Hamilton membutuhkan waktu
O(n), sehingga kompleksitas waktu algoritma exhaustive search
untuk persoalan TSP adalah O(n n!).
• Perbaikan: setengah dari semua rute perjalanan adalah hasil
pencerminan dari setengah rute yang lain, yakni dengan
mengubah arah rute perjalanan
1 dan 6
2 dan 4
3 dan 5
Algoritma Bactracking
Algoritma Backtracking merupakan salah satu bentuk algoritma yang banyak
digunakan oleh para programmer ataupun pengguna komputer ahli untuk
menyelesaikan suatu permasalahan komputasional pada perangkat komputer
yang mereka gunakan. Dalam programming algoritma backtracking, rekursi
adalah kunci dari programming backtracking. Rekursi sendiri merupakan
proses pengulangan suatu hal yang mencakup kesamaan-diri. Penggunaan
yang paling umum dari rekursi terdapat dalam kajian ilmu matematika dan
ilmu komputer.
Dengan mempelejari hal ini lebih lanjut tentu akan membuat anda
merasakan manfaat mempelajari ilmu komputer. Begitu juga dengan
memahami cara kerja algoritma backtracking. Dengan memahami potensi
yang dimiliki oleh algoritma backtracking anda dapat menghasilkan sebuah
algoritma yang nantinya dapat mempermudah pekerjaan manusia dalam
menyelesaikan suatu masalah di masa depan.
Sejarah
Algoritma backtrack pertama kali diperkenalkan oleh D.H. Lehmer pada
tahun1950. Dalam perkembangannya beberapa ahli seperti Rwalker, Golomb,
dan Baumert menyajikan uraian umum tentang backtrack dan penerapannya
dalam berbagai persoalan dan aplikasi. Algoritma backtracking (runut balik)
merupakan salah satu metode pemecahan masalah yang termasuk dalam
strategi yang berbasis pencarian pada ruang status. Langkah-langkah
pencarian solusi dengan backtracking adalah sebagai berikut [7]: x Solusi
dicari dengan membentuk lintasan dari akar ke daun. Simpul yang sudah
dilahirkan dinamakan simpul hidup dan simpul hidup yang diperluas
dinamakan simpul-E
Fungsi Himmelblau adalah salah satu fungsi yang dapat digunakan untuk
mengoptimasi suatu permasalahan. Fungsi ini memiliki sebuah nilai
maksimum pada x = -0.270845, and y = -0.923039 dengan nilai fungsi sebesar
f(x,y) = 181.617, dengan asumsi bahwa rentang minimal dan maksimal dari
sebaran titik adalah -2 sampai dengan 2
Grafik fungsi Himmelblau yang normal, atau untuk sebaran titik tak terbatas
adalah sebagai berikut.
I=tmpPosisiUb;
tmpPosisi(J)=Ub(J);
posisiAnak(i,:)=tmpPosisi;
nilaiFungsiAnak(i) = HitungNilaiFungsi(posisiAnak(i,:));
end
4g. Bandingkan nilai fungsi antara populasi anak dengan populasi sebenarnya
Cari indeks dimana populasi anak memiliki nilai fungsi lebih tinggi dari nilai
fungsi populasi sebenarnya
Ganti semua posisi dan nilai fungsi dari populasi sebenarnya pada indeks
tersebut sesuai dengan posisi dan nilai fungsi pada populasi anak
idxLebihBaik = nilaiFungsiAnak>nilaiFungsi;
nilaiFungsi(idxLebihBaik) = nilaiFungsiAnak(idxLebihBaik);
posisi(idxLebihBaik,:) = posisiAnak(idxLebihBaik,:);
4h. Hitung nilai fungsi terbaik yang terdapat pada populasi sebenarnya
[nilaiFungsiPopulasiTerbaik,idxTerbaik]=max(nilaiFungsi);
4i. Jika nilai fungsi tersebut ternyata lebih baik dari nilai fungsi terbaik secara
umum,
maka ambil posisi yang baru sebagai posisi yang terbaik
if nilaiFungsiPopulasiTerbaik > nilaiFungsiTerbaik,
posisiTerbaik = posisi(idxTerbaik,:);
nilaiFungsiTerbaik = nilaiFungsiPopulasiTerbaik;
end