Anda di halaman 1dari 51

Nama : NAZRI HAMDI

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:

def kali(a, b):


return a if b == 1 else a + kali(a, b - 1)

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

Perhatikan bahwa sebelum melakukan penambahan program melakukan pemanggilan fungsi


rekursif terlebih dahulu sampai fungsi rekursif mengembalikan nilai pasti (22). Setelah
menghilangkan semua pemanggilan fungsi, penambahan baru dilakukan, mulai dari nilai
kembalian dari fungsi yang paling terakhir. Mari kita lihat contoh fungsi rekursif lainnya, yang
digunakan untuk melakukan perhitungan faktorial:

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:

def gcd(x, y):


return x if y == 0 else gcd(y, x % y)

yang jika diimplementasikan dengan menggunakan iterasi adalah sebagai berikut:

def gcd_iterasi(x, y):


while y != 0:
temp = y
y = x % temp
x = temp
return x

Jika dibandingkan dengan fungsi matematis dari algoritma euclidean:


gcd(x,y)={xgcd(y,remainder(x,y))if y=0if y>0gcd(x,y)={xif y=0gcd(y,remainder(x,y))if y>0
tentunya implementasi secara rekursif lebih sederhana dan mudah dimengerti dibandingkan
dengan secara iterasi.
Sekarang mari kita lihat contoh algoritima yang lebih cocok diimplementasikan secara iteratif,
misalnya linear search. Implementasi standar linear search secara iteratif adalah sebagai berikut:

def linear_search(lst, search):


for i in range(0, len(lst)):
if lst[i] == search:
print("Nilai ditemukan pada posisi: " + str(i))
return 0
print("Nilai tidak ditemukan")
return -1

yang jika diimplementasikan secara rekursif akan menjadi:

def linear_search_rec(lst, search, pos):


if len(lst) <= pos:
print("Nilai tidak ditemukan.")
return -1
elif lst[pos] == search:
print("Nilai ditemukan di posisi: " + str(pos))
return 0
else:
return linear_search_rec(lst, search, pos + 1)

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)

Pada contoh fungsi tail_call di atas, pemanggilan


terhadap fungsi_1 maupun fungsi_2 adalah tail call, meskipun pemanggilan fungsi_1 tidak
berada pada akhri fungsi secara fisik. Bandingkan dengan kode berikut:

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_tc(n, r = 1):


if n <= 1:
return r
else:
return faktorial_tc(n - 1, n * r)

untuk memastikan terdapat tail call di dalam fungsi.


Implementasi algoritma rekursif disarankan untuk mengadopsi tail call, karena natur dari fungsi
rekursif yang memakan banyak memori. Tail call optimization, jika diimplementasikan oleh bahasa
pemrograman, akan mendeteksi adanya tail call pada sebuah fungsi untuk kemudian dijalankan
sebagai perulangan untuk menghindari penggunaan memori berlebihan.
Bahasa pemrograman yang mendukung tail call optimization biasanya adalah bahasa
pemrograman fungsional seperti Haskell, LISP, Scheme, dan Erlang. Python, sayangnya, tidak
mendukung optimization.
Analisis Algoritma Rekursif
secara langsung melihat berapa kali bagian rekursif dari algoritma akan dijalankan. Pada algoritma
yang menggunakan perulangan for misalnya, kita dapat langsung menghitung jumlah perulangan
untuk menghitung total langkah yang dibutuhkan. Dalam algoritma rekursif, jumlah perulangan
tidak secara eksplisit bisa didapatkan karena informasi yang kita miliki adalah kapan algoritma
berhenti, bukan berapa kali kode dieksekusi.
Misalnya, algoritma perhitungan faktorial yang telah dibahas sebelumnya:
Melakukan analisis untuk algoritma rekursif pada dasarnya sama dengan melakukan analisis
terhadap algoritma imparatif lain. Perbedaan utama pada algoritma rekursif ialah kita tidak dapat

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:

1. Sebuah kasus (atau beberapa kasus) dasar sederhana


2. Sejumlah aturan yang mengurangi kasus-kasus lainnya sampai ke kasus
dasarnya.
Sebagai contoh, berikut ini adalah definisi rekursif dari leluhur seseorang:

• Orang tua seseorang adalah leluhur seseorang (kasus dasar).


• Orang tua dari suatu leluhur juga merupakan leluhur-nya (langkah rekursi).
Bilangan Fibonacci adalah contoh klasik dari rekursi:

• Fib(0) adalah 0 [kasus dasar]


• Fib(1) adalah 1 [kasus dasar]
• Untuk semua integer n > 1: Fib(n) adalah (Fib(n-1) + Fib(n-2)) [definisi rekursif]
Banyak aksioma matematika berdasarkan aturan-aturan rekursif. Sebagai contohnya,
definisi formal dari bilangan asli pada Aksioma Peano dapat dideskripsikan sebagai: 0
adalah bilangan asli, dan setiap bilangan asli memiliki sebuah suksesor, yang juga
merupakan bilangan asli. Dengan kasus dasar ini dan aturan rekursif, seseorang dapat
membuat himpunan dari semua bilangan asli.
Gambaran humornya berbunyi: "Untuk memahami rekursi, pertama anda harus
memahami rekursi." Atau mungkin yang lebih akurat, dari Andrew Plotkin: "Jika anda telah
mengetahui apa itu rekursi, cukup ingat jawabannya. Kalau tidak, cari orang yang berdiri
paling dekat dengan Douglas Hofstadter selain anda; lalu tanya dia rekursi itu apa."
Objek matematika yang didefinisikan secara rekursif termasuk fungsi, himpunan, dan
terutama sekali fraktal.
Definisi informal
Rekursi adalah suatu proses dengan salah satu langkah dalam prosedur tersebut
menjalankan prosedur itu sendiri. Prosedur yang melakukan rekursi disebut dengan
'rekursif'.
Untuk memahami rekursi, seseorang harus mengetahui perbedaan antara sebuah
prosedur dan jalannya sebuah prosedur. Sebuah prosedur yaitu kumpulan langkah-
langkah berdasarkan sekumpulan aturan. Jalannya sebuah prosedur mengikuti aturan-
aturan dan melakukan langkah-langkah. Analoginya mungkin sebuah prosedur adalah
seperti resep yang tertulis; menjalankan sebuah prosedur adalah seperti menyiapkan
makanan.
Rekursi berhubungan dengan, tetapi tidak sama, suatu referensi dalam spesifikasi
prosedur sampai pada eksekusi beberapa prosedur lainnya. Misalnya, suatu resep bisa
mengacu pada memasak sayuran, yang merupakan prosedur yang kemudian
membutuhkan memanaskan air, dan seterusnya. Namun, prosedur rekursif adalah spesial
dengan (paling tidak) salah satu langkahnya memanggil instansi baru dari prosedur yang
sama, seperti suatu resep gandum hitam menggunakan beberapa sisa adonan dari resep
yang sama yang telah dibuat. Hal ini tentu saja membuat suatu kemungkinan perulangan
tanpa berakhir; rekursi hanya dapat digunakan secara tepat dalam definisi jika langkah
yang bersangkutan dilewat pada beberapa kasus sehingga prosedur dapat selesai,
seperti resep gandum-hitam yang memberitahu anda bagaimana membuat adonan awal
seandainya anda belum pernah membuatnya sebelumnya. Bahkan jika didefinisikan
secara tepat, prosedur rekursif tidak mudah dilakukan oleh manusia, karena ia
membutuhkan membedakan pemanggilan prosedur yang baru dengan yang lama (yang
telah dieksekusi sebagian); hal ini membutuhkan beberapa administrasi sejauh mana
berbagai prosedur instan yang berjalan bersamaan telah berjalan. Karena hal ini definisi
rekursif sangat jarang dalam keadaan harian. Contohnya dapat berupa prosedur untuk
menemukan jalan melewati sebuah labirin. Terus ke depan sampai menemui jalan keluar
atau titik percabangan (sebuah titik mati dianggap sebagai sebuah titik percabangan
dengan 0 cabang). Jika titik yang ditemui adalah suatu jalan keluar,berhenti. Kalau tidak
coba setiap cabang bergantian, menggunakan prosedur secara rekursif; jika setiap
percobaan gagal karena mencapai titik mati, kembali ke jalur yang menyebabkan titik
percabangan dan laporkan kegagalan. Apakah ini tepat mendefinisikan suatu prosedur
pemberhentian bergantung kepada bentuk labirinnya: ia tidak membolehkan perulangan.
Dalam semua kasus, mengeksekusi prosedur membutuhkan pencatatan teliti semua titik
percabangan yang telah dieksplorasi, dan cabang-cabang mana yang telah dicoba..

Rekursi dalam bahasa


Ahli linguistik Noam Chomsky memberikan teori bahwa ekstensi tak-terhingga dari
setiap bahasa alami adalah memungkinkan menggunakan perangkat rekursif dengan
menanamkan klausa dalam kalimat (Aspects of the Theory of Syntax. 1965). Sebagai
contoh, dua kalimat sederhana -- "Dorothy bertemu dengan Penyihir Jahat dari Barat di
Munchkin Land" dan "Saudara perempuan Penyihir Jahat dibunuh di Munchkin Land"—
dapat disisipkan dalam kalimat ketiga, "Dorothy menyiram Penyihir Jahat dengan seember
air", untuk mendapatkan kalimat rekursif: "Dorothy, yang bertemu dengan Penyihir Jahat
dari Barat di Munchkin Land tempat saudara perempuannya dibunuh, menyiramnya
dengan seember air."
Ide bahwa rekursi adalah suatu properti esensi dari bahasa manusia (seperti yang
Chomsky ajukan) dibantah oleh linguis Daniel Everett dalam karyanya Cultural Constraint
on Grammar and Cognition in Pirahã: Another Look at the Design Features of Human
Language, yang di sana dia berhipotesis bahwa faktor kultur membuat rekursi tidak
dibutuhkan dalam perkembangan Bahasa Piraha. Konsep ini, yang menantang ide
Chomsky bahwa rekursi satu-satunya sifat yang membedakan komunikasi manusia dan
hewan, sekarang sedang diperdebatkan. Andrew Nevins, David Pesetsky dan Cilene
Rodrigues berdebat melawan proposal tersebut. [1]
Rekursi dalam linguistik membolehkan 'diskrit tak-terbatas' dengan menanamkan frasa
dalam tipe frasa yang sama dalam suatu struktur hierarki. Tanpa rekursi, bahasa tidak
memiliki 'diskrit tak-terbatas' dan tidak dapat menanamkan kalimat menjadi tak-terbatas
(dengan suatu efek 'Sarang boneka Rusia'). Everett membantah bahwa bahasa harus
memiliki diskrit tak-terbatas, dan menegaskan bahwa bahasa Piraha—yang diklaimnya
tidak memiliki rekursi—pada kenyataannya terbatas. Dia menyamakannya dengan
permainan terbatas catur, yang memiliki sejumlah pergerakan terbatas tetapi sangat
produktif, dengan gerakan-gerakan baru diciptakan lewat sejarah.
Rekursi dalam matematika
Segitiga Sierpinski—sebuah rekursi terbatas dari segitiga membentuk
suatu jeruji geometris.
Himpunan yang didefinisikan secara rekursif
Contoh: bilangan asli
Contoh kanonikal dari himpunan yang didefinisikan secara rekursif yaitu diberikan
oleh bilangan asli:

0 ada dalam

jika n ada dalam , maka n + 1 ada dalam


Himpunan dari bilangan asli adalah himpunan terkecil yang memenuhi dua properti
sebelumnya.
Contoh: himpunan dari proposisi benar terjangkau
Contoh menarik lainnya adalah himpunan dari semua proposisi "benar
terjangkau" dalam suatu sistem aksioma.

• Jika suatu proposisi adalah sebuah aksioma, maka ia adalah suatu


proposisi benar terjangkau.
• Jika suatu proposisi dapat dihasilkan dari proposisi benar terjangkau
dengan menggunakan aturan-aturan inferensi, maka ia adalah proposisi
benar terjangkau.
• Himpunan dari proposisi benar-terjangkau adalah himpunan terkecil dari
proposisi yang memenuhi kondisi tersebut.
Himpunan ini disebut 'proposisi benar terjangkau' karena dalam pendekatan
non-konstruktif terhadap fondasi matematika, himpunan dari proposisi benar
bisa lebih besar daripada himpunan yang dibangun secara rekursif dari
aksioma-aksioma dan aturan-aturan inferensi.
Contoh:
Beberapa relasi perulangan umum yaitu:

• Rasio Golden:

• Faktorial:

• Bilangan Fibonacci:

• Bilangan Catalan: ,
3. Algoritma Divide and Conquer

Divide and Conquer


Komputer pada awalnya diciptakan sebagai perangkat untuk melakukan kalkulasi secara otomatis
dan akurat. Meskipun awalnya hanya berfokus pada kalkukasi numerik, komputer modern yang
dijumpai sekarang telah melakukan kalkulasi pada banyak hal, seperti teks ataupun gambar.
Berbagai kalkulasi dan analisa yang dilakukan komputer biasanya diimplementasikan melalui
perangkat lunak. Dengan semakin besarnya ruang lingkup hal-hal yang dilakukan oleh komputer,
perangkat lunak yang dikembangkan juga menjadi semakin kompleks. Algoritma, sebagai bagian
dari perangkat lunak yang melakukan pemrosesan, juga memerlukan berbagai teknik baru.
Misalkan, untuk menghitung total jumlah dari bilangan-bilangan yang ada di dalam sebuah list,
kita dapat menggunakan perulangan sederhana:

nums = [1, 2, 3, 5, 6, 7, 19, 28, 58, 18, 28, 67, 13]


total = 0

for i in range(0, len(nums)):


total = total + nums[i]

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:])

return left + right

print(sums(nums)) # 255

Apa yang kita lakukan pada kode di atas?

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 Kerja Divide and Conquer


Apa kelebihan pendekatan dengan membagi-bagikan masalah ini? Dengan menggunakan bahasa
dan library yang tepat, kita dapat membagi-bagikan setiap bagian rekursif
(left = ... dan right = ...) ke satu unit kerja baru, yang dikenal dengan nama thread.
Mekanisme pada sistem operasi atau compiler kemudian akan membagi-bagikan tugas pembagian
dan perhitungan lanjutan agar dapat dijalankan secara paralel, misalnya dengan membagikan
tugas ke dalam beberapa core prosesor, atau bahkan ke dalam mesin lain (jika terdapat sistem
dengan banyak mesin).
Dengan membagi-bagikan pekerjaan ke dalam banyak unit, tentunya pekerjaan akan lebih cepat
selesai! Teknik memecah-mecah pekerjaan untuk kemudian dibagikan kepada banyak pekerja ini
dikenal dengan nama divide and conquer.

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),

• Conquer (solve): menyelesaikan masing-masing upa-masalah (secara langsung atau secara


rekursif).

• Combine: mengabungkan solusi masing-masing upa-masalah sehingga membentuk solusi


persoalan semula.

•Obyek persoalan yang dibagi : masukan (input) atau instances persoalan yang berukuran n
seperti:

- tabel (larik),

- matriks,

- eksponen,

- dll, bergantung persoalannya.

• 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.

Skema Umum Algoritma Divide and Conquer

procedure DIVIDE_and_CONQUER(input n : integer) { Menyelesaikan masalah dengan algoritma


D-and-C. Masukan: masukan yang berukuran n Keluaran: solusi dari masalah semula }
Deklarasi

r, k : integer

Algoritma

if n  n0 then {ukuran masalah sudah cukup kecil }

SOLVE upa-masalah yang berukuran n ini

else

Bagi menjadi r upa-masalah, masing-masing berukuran n/r

for masing-masing dari r upa-masalah do

DIVIDE_and_CONQUER(n/k) endfor COMBINE solusi dari r upa-masalah menjadi solusi masalah


semula } endif
Ide penyelesaian dengan Divide and Conquer

• 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.

MinMaks(A, n, min, maks)

Algoritma:

1. Untuk kasus n = 1 atau n = 2,


SOLVE: Jika n = 1, maka min = maks = A[n]
Jika n = 2, maka bandingkan kedua elemen untuk
menentukan min dan maks.

2. Untuk kasus n > 2,

(a) DIVIDE: Bagi dua tabel A menjadi dua bagian yang

sama, A1 dan A2

(b) CONQUER: MinMaks(A1, n/2, min1, maks1) MInMaks(A2, n/2,

min2, maks2)

(c) COMBINE: if min1 < min2 then min  min1 else min  min2 if

maks1 < maks2 then maks  maks2 else maks  maks1

Divide and Conquer procedure

Sort(input/output A : TabelInt, input n : integer)

{ 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)

Sort(A1, n1) { urut bagian kiri yang berukuran n1 elemen }

Sort(A2, n2) { urut bagian kanan yang berukuran n2 elemen }

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).

2. Untuk kasus n > 1, maka

(a) DIVIDE: bagi tabel A menjadi dua bagian, bagian kiri dan bagian kanan, masing-masing bagian
berukuran n/2 elemen. (

b) CONQUER: Secara rekursif, terapkan algoritma D-and-C pada masing-masing bagian.

(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

= 2(2T(n/4) + cn/2) + cn = 4T(n/4) + 2cn

= 4(2T(n/8) + cn/4) + 2cn = 8T(n/8) + 3cn

= ...

= 2k T(n/2k ) +kcn

Berhenti jika ukuran tabel terkecil, n = 1:

n/2k = 1 → k = 2 log n

sehingga

T(n) = nT(1) + cn 2 log n = na + cn 2 log n = O(n 2 log n)

Contoh:

A 4 12 3 9 1 21 5 2

Dua pendekatan (approach) pengurutan:

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

Algoritma pengurutan yang termasuk jenis ini:


a. urut-gabung (Merge Sort)
b. urut-sisip (Insertion Sort)

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

Algoritma pengurutan yang termasuk jenis ini:


a. urut-cepat (Quick Sort) b.
b. urut-seleksi (Selection Sort)

Algoritma Dynamic Programing

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:

Pemanggilan Fungsi Fibonacci


Perhatikan bagaimana [Math Processing Error]f(n−2) dan [Math Processing
Error]f(n−3) dikalkulasikan sebanyak dua kali, dan semakin kita masuk ke dalam pohon
pemanggilan, kita akan melihat semakin banyak fungsi-fungsi yang dipanggil berkali-kali.
Pendekatan DP menghindari kalkulasi fungsi yang berulang kali seperti ini dengan
melakukan memoization, yaitu menyimpan hasil kalkulasi fungsi tersebut dan menggunakan nilai
yang disimpan ketika perhitungan yang sama dibutuhkan kembali. Dengan menyimpan hasil
kalkulasi seperti ini, tentunya jumlah total langkah perhitungan yang harus dilakukan menjadi
berkurang.
Misalnya, kita dapat menyimpan hasil kalkulasi dari fungsi fibonacci tersebut pada
sebuah dictionary, 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

Pemanggilan Fungsi Fibonacci Dynamic Programming


Seperti yang dapat dilihat, pohon pemanggilan fungsi terpotong setengahnya! Tentunya
perhitungan fibonacci akan menjadi sangat efisien dengan menggunakan fungsi yang baru ini.
Pendekatan lain dalam menghitung fibonacci lagi, yang masih adalah DP, yaitu dengan
menghitung nilai fibonacci dari bawah pohon (pada kode sebelumnya kita melakukan perhitungan
dari atas pohon):

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:

1. DP menyelesaikan masalah dengan memecah masalah menjadi sub-permasalahan.


2. Setiap solusi dari sub-permasalahan yang telah didapatkan disimpan untuk digunakan
kembali jika terdapat sub-permasalahan yang sama. Teknik ini dikenal dengan
nama memoization.
3. DP tidak harus menggunakan rekursif. Pemecahan sub-permasalahan juga dapat dilakukan
dengan iterasi maupun kalkulasi sederhana.

Contoh Aplikasi Dynamic Programming: Text Justification

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:

Fitur Pemerataan Teks pada Microsoft Word


Bagaimana kita menentukan kemerataan teks? Secara umum, kemerataan sebuah teks ditentukan
oleh beberapa hal berikut:

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):

def naive_justify(text, page_size):


next = text.get_next()
total_size = 0
next_total_size = total_size + next.size()

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()

while total_size != page_size:


add_space(lines[current_line])

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:

Hasil Algoritma Pemerataan Teks Sederhana

Perhatikan bagaimana teks “Dynamic Programming”, “dikembangkan untuk”, dan “memecah


permasalahan” memiliki spasi yang sangat lebar. Menggunakan DP, kita dapat menghasilkan
pemerataan teks yang lebih optimal.
Berdasarkan algoritma sebelumnya yang kita kembangkan, dapat dilihat bagaimana optimasi dari
rata penuh sebuah teks terdapat pada kapan kita melakukan pergantian baris. Jika kita mengganti
baris terlalu cepat (jumlah kata masih sedikit), maka secara otomatis kita harus menambahkan
banyak spasi, yang menyebabkan teks tidak enak dilihat. Untuk mendapatkan jumlah kata yang
optimal dalam sebuah baris, kita akan melakukan perhitungan tingkat “keburukan” sebuah kata
dalam teks, jika kata tersebut dijadikan pengganti baris. Kita kemudian dapat mencari tingkat
keburukan setiap kata yang ada dalam teks, dan mengambil kata yang memiliki tingkat keburukan
terendah sebagai tanda berganti baris.
Pengukuran tingkat keburukan teks sendiri tentunya ditentukan oleh jumlah ruang kosong yang
ada dari teks sampai ke ujung halaman. Misalnya, pada gambar di bawah kita dapat melihat
contoh ruang kosong dari teks sampai ke ujung halaman:

Tingkat Keburukan Teks


Pada gambar di atas, blok berwarna merah berarti tingkat keburukannya tinggi, dan blok
berwarna hijau berarti tingkat kebukurannya rendah. Untuk mendapatkan nilai keburukan yang
paling kecil dalam sebuah teks, tentunya kita harus menghitung seluruh kombinasi nilai keburukan
dari elemen-elemen yang ada dalam teks. Perhitungan kombinasi nilai keburukan ini tentunya
merupakan masalah yang tepat untuk algoritma DP, karena setiap perhitungan nilai keburukan
pada dasarnya adalah sebuah sub-masalah!
Jadi sekarang kita telah menemukan sub-masalahnya: mencari nilai keburukan dari sebuah
elemen. Bagaimanakah kita dapat menggunakan teknik DP untuk menyelesaikan masalah ini?
Ketika menghitung kombinasi dari nilai keburukan dari setiap elemen, secara tidak langsung kita
akan membangun sebuah Directed Acyclic Graph, seperti yang tampak pada gambar berikut:

DAG dalam Teks


dengan setiap [Math Processing Error]k merepresentasikan tingkat keburukan dari elemen
tersebut. Menggunakan informasi tersebut, kita dapat mencari nilai minimal dari total seluruh
nilai keburukan yang ada pada sebuah teks untuk mendapatkan titik penggantian baris yang paling
tepat. Untuk merangkum, berikut adalah langkah-langkah untuk algoritma yang sedang kita
kembangkan:

1. Ambil setiap elemen dari dalam teks.


2. Untuk setiap elemen yang ada, lakukan: 1. Hitung nilai keburukan dari elemen terhadap
elemen-elemen lain dalam teks. 2. Hitung total nilai keburukan yang ada pada elemen
yang sedang dicari.
3. Tentukan nilai keburukan minimum dari nilai keburukan seluruh elemen yang telah
dihitung pada langkah 2.
4. Ambil elemen yang memiliki nilai keburukan minimum.
5. Ganti baris pada elemen dengan nilai keburukan minimum.

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:

def length(word_lengths, i, j):


return sum(word_lengths[i- 1:j]) + j - i + 1

def break_line(text, L):


# wl = lengths of words
wl = [len(word) for word in text.split()]
# n = number of words in the text
n = len(wl)

# total badness of a text l1 ... li


m = dict()
m[0] = 0

s = dict()

for i in range(1, n + 1):


sums = dict()
k = i
while (length(wl, k, i) <= L and k > 0):
# badness calculation
sums[(L - length(wl, k, i))**3 + m[k - 1]] = k
k -= 1
m[i] = min(sums)
s[i] = sums[min(sums)]

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

(a) • Misalkan x1 , x2 , x3 , x4 adalah simpul-simpul yang dikunjungi pada tahap k (k =


1, 2, 3, 4). Tahap 5 tidak ada karena x5=10

(b) • Misalkan digunakan pendekatan program dinamis maju

(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.

Definisikan hubungan rekursif solusi optimal


Relasi rekurens berikut menyatakan lintasan terpendek pada setiap tahap:
f1 (s) = cx1,s (basis) f k (s) = min
{f k – 1 (xk ) + cxk,s } (rekurens) k = 2, 3, 4
Keterangan:
a. xk : peubah keputusan pada tahap k (k = 2, 3, 4).
b. s : status pada setiap tahap
c. cxk,s : bobot (cost) sisi dari ke xk ke s
d. f k (s) : nilai minimum dari f k (xk , s)
e. f k – 1 (xk ) : nilai minimum tahap sebelumnya dari xk ke s

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

Graph Berarah Beserta Jarak Masing-Masing Titik 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.

Pernyataan di atas dapat ditulis dalam pseudo-code sebagai berikut.

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.

Lambung cembung untuk kumpulan titik ini adalah poligon


cembung dengan simpul di P1, P5, P6, P7, P3.
Ruas garis P1 dan Pn dari himpunan titik n adalah bagian dari
lambung cembung jika dan hanya jika semua titik lain dari himpunan
terletak di dalam batas poligon yang dibentuk oleh segmen garis.
Mari kita kaitkan dengan karet gelang,
Titik (x1, y1), (x2,y2) membuat garis ax+by = c
Ketika a = y2-y1, b = x2-x1 dan c = x1*y2 – x2*y1 dan membagi
bidang dengan ax+by-c < 0 dan ax+by-c > 0
Jadi kita perlu memeriksa ax+by-c untuk titik lainnya.
Brute force memecahkan masalah ini dengan kompleksitas waktu O(n3)

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.

Contoh klasik dalam ilmu komputer adalah travelling salesman


problem (TSP). Misalkan seorang salesman perlu mengunjungi 10 kota
di seluruh negeri. Bagaimana cara menentukan urutan kota-kota yang
harus dikunjungi sedemikian rupa sehingga total jarak tempuh
diminimalkan?
Solusi brute force adalah dengan menghitung total jarak untuk setiap
rute yang mungkin dan kemudian memilih yang terpendek. Ini tidak
terlalu efisien karena dimungkinkan untuk menghilangkan banyak
kemungkinan rute melalui algoritma pintar.
Kompleksitas waktu dari brute force adalah O(mn), yang terkadang
ditulis sebagai O(n*m) . Jadi, jika kita mencari string karakter "n"
dalam string karakter "m" menggunakan brute force, kita perlu n * m
percobaan.
Contoh-contoh (Berdasarkan pernyataan persoalan) 1. Mencari
elemen terbesar (terkecil) Persoalan: Diberikan sebuah senarai yang
berisi n buah bilangan bulat (a1 , a2 , …, an ). Carilah elemen terbesar
di dalam senarai tersebut. Algoritma brute force: bandingkan setiap
elemen senarai mulai dari a1 sampai an untuk menemukan elemen
terbesar
procedure CariElemenTerbesar(input a1 , a2 , ..., an : integer, output
maks : integer)
{ Mencari elemen terbesar di antara elemen a1 , a2 , ..., an .
Elemen terbesar disimpan di dalam maks.
Masukan: a1 , a2 , ..., an Luaran: maks
} Deklarasi
k : integer
Algoritma: maksa1
for k2 to n do
if ak > maks then
maksak
endif
endfor
Jumlah operasi perbandingan elemen senarai: n – 1 kali Kompleksitas
waktu algoritma: O(n).
1. Pencarian beruntun (Sequential Search) Persoalan: Diberikan
senarai yang berisi n buah bilangan bulat (a1 , a2 , …, an ). Carilah nilai
x di dalam senarai tersebut. Jika x ditemukan, maka luarannya adalah
indeks elemen senarai, jika x tidak ditemukan, maka luarannya adalah -
1.
Algoritma brute force: setiap elemen senarai dibandingkan
dengan x. Pencarian selesai jika x ditemukan atau seluruh elemen
senarai sudah habis diperiksa.
procedure PencarianBeruntun(input a1 , a2 , ..., an : integer, x : integer;
output idx : integer) { Mencari elemen bernilai x di dalam senarai a1 ,
a2 , ..., an . Lokasi (indeks elemen) tempat x ditemukan diisi ke dalam
idx. Jika x tidak ditemukan, maka idx diisi dengan 0.
Masukan: a1 , a2 , ..., an
Luaran: idx }
Deklarasi k : integer
Algoritma:
k1
while (k < n) and (ak  x) do
kk+1
endwhile
{ k = n or ak = x }
if ak = x then { x ditemukan }
idx  k
else idx  –1 { x tidak ditemukan }
endif
Jumlah operasi perbandingan elemen senarai maksimal sebanyak: n kali
Kompleksitas waktu algoritma: O(n). Adakah algoritma pencarian
elemen yang lebih mangkus daripada brute force?

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:
p0
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)

Persoalan: Diberikan n buah kota serta diketahui jarak antara setiap


kota satu sama lain. Temukan perjalanan (tour) dengan jarak terpendek
yang dilakukan oleh seorang pedagang sehingga ia melalui setiap kota
tepat hanya sekali dan kembali lagi ke kota asal keberangkatan.
• Persoalan TSP tidak lain adalah menemukan sirkuit Hamilton
dengan bobot minimum.
• Algoritma exhaustive search untuk TSP:
1. Enumerasikan (list) semua sirkuit Hamilton dari graf lengkap
dengan n buah simpul.
2. Hitung (evaluasi) bobot setiap sirkuit Hamilton yang
ditemukan pada langkah 1.
3. Pilih sirkuit Hamilton yang mempunyai bobot terkecil.
Itulah solusinya.

Contoh 3:
TSP dengan n = 4, simpul awal = a

Rute perjalananan terpendek adalah a→c→b→d→a


a→d→b→c→a dengan bobot = 32.

• Untuk graf lengkap n buah simpul semua rute perjalanan


dibangkitkan dengan permutasi dari n – 1 buah simpul.
• Permutasi n – 1 buah simpul adalah
(n – 1)!
• Pada contoh di atas, untuk n = 4 akan terdapat

(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

• maka dapat dihilangkan setengah dari jumlah permutasi (dari 6


menjadi 3).
• Untuk graf lengkap dengan n buah simpul, kita hanya perlu
mengevaluasi (n – 1)!/2 sirkuit Hamilton.
• Untuk TSP dengan n yang besar, jelas algoritma exhaustive
search menjadi sangat tidak mangkus.
2. 1/0 Knapsack Problem
• Persoalan: Diberikan n buah objek dan sebuah knapsack dengan
kapasitas bobot K. Setiap objek memiliki properti bobot (weigth)
w i dan keuntungan (profit) p i
Bagaimana cara memilih objek -objek yang dimasukkan ke
dalam knapsack sedemikian sehingga diperoleh total keuntungan
yang maksimal. Total bobot objek yang dimasukkan tidak boleh
melebihi kapasitas knapsack.
• Disebut 1/0 knapsack problem karena suatu objek dapat
dimasukkan ke dalam knapsack (1) atau tidak dimasukkan sama
sekali (0)
• Persoalan 0/1 Knapsack dapat kita pandang sebagai mencari
himpunan bagian (subset) dari himpunan n objek yang dapat
dimuat ke dalam knapsack dan memberikan total keuntungan
terbesar.
• Solusi persoalan dinyatakan sebagai X = {x1 , x2 , …, xn } xi =
1, jika objek ke-i dipilih, xi = 0, jika objek ke-i tidak dipilih.
• Formulasi persoalan knapsack secara matematis:

Algoritma exhaustive search untuk persoalan 1/0 Knapsack:


1. Enumerasikan (list) semua himpunan bagian dari himpunan
dengan n objek.
2. Hitung (evaluasi) total keuntungan dari setiap himpunan
bagian dari langkah 1.
3. Pilih himpunan bagian yang memberikan total keuntungan
terbesar namun total bobotnya tidak melebihi kapasitas knapsack.
Contoh 4: Misalkan terdapat n = 4 buah objek dan sebuah
knapsack dengan kapasitas K = 16. Properti setiap objek adalah
sbb
Langkah-langkah pencarian solusi 0/1 Knapsack secara
exhaustive search dirangkum dalam tabel di bawah ini:

• Himpunan bagian objek yang memberikan keuntungan


maksimum adalah {2, 3} dengan total keuntungan adalah 80.
•Solusi: X = {0, 1, 1, 0}
• Banyaknya himpunan bagian dari sebuah himpunan
dengan n elemen adalah 2 n . Waktu untuk menghitung total
bobot di dalam himpunan bagian adalah O(n) Sehingga,
Kompleksitas algoritma exhaustive search untuk persoalan 0/1
Knapsack adalah O(n. 2n ).
• Banyaknya himpunan bagian dari sebuah himpunan
dengan n elemen adalah 2 n . Waktu untuk menghitung total
bobot di dalam himpunan bagian adalah O(n) Sehingga,
Kompleksitas algoritma exhaustive search untuk persoalan 0/1
Knapsack adalah O(n. 2n )
6. Algoritma Becktracking

Pengertian algoritma pemograman


Pengertian algoritma pemograman merupakan hal yang akan sering
ditemukan dalam perkuliahan yang berkaitan dengan ilmu komputer dimana
algoritma merupakan langkah – langkah untuk menyelesaikan masalah
sedangkan pemograman adalah kegiatan membuat program, dalam hal ini
program komputer.
Algoritma menjadi salah satu mata kuliah yang selalu ada dan diajarkan bagi
mahasiswa yang mengambil perkuliahan yang berkaitan dengan ilmu
komputer. Ilmu yang mengajarkan tentang algoritma merupakan salah satu
ilmu yang sangat dihandalkan pada era perkembangan teknologi seperti saat
ini dimana hampir semua elemen komputer membutuhkan inovasi – inovasi
baru untuk menyelesaikan permasalahan-permaslahan yang nantinya
membuat penggunaan komputer semakin lebih efisien dan mampu
menyelesaikan berbagai permasalahan.

Algoritma ditulis untuk membuat komputer menjadi alat yang dapat


menyelesaikan permasalahan – permasahan yang telah dihadapkan oleh para
pembuatnya. Permasalahan – permasalahan ini umumnya mencakup
permasalahan – permasalahan komputasional dari berbagai macam aspek
kehidupan.

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.

Algoritma rekursi merupakan algoritma yang memanggil dirinya sendiri


secara berulang kali. Backtracking sebuah algoritma secara umum digunakan
untuk menemukan semua (atau beberapa) solusi terhadap sebuah
permasalahan komputasional. Proses backtracking dapat diaplikasikan hanya
pada beberapa permasalahan yang mengikuti konsep “solusi kandidat parsial”
dan juga sebuah tes yang cukup relatif cepat untuk menentukan kemungkinan
apakah solusi tersebut valid atau tidak.
Backtracking tidaklah berguna untuk menyelesaikan permasalahaan seperti
menentukan sebuah nilai yang diberikan pada sebuah tabel yang tidak
beraturan. Akan tetapi ketika diaplikasikan, backtracking biasanya lebih cepat
bila dibandingkan proses pemecahan masalah brute force yang mana harus
mencoba semua kandidat kemungkinannya.
Sebagai salah satu algoritma yang banyak digunakan oleh para programmer.
Backtracking merupakan sebuah alat yang penting untuk dapat menyelesaikan
permasalahan pemenuhan terbatas, seperti teka – teki silang, aritmatika
verbal, sudoku dan berbagai macam puzzle sejenisnya. Algoritma ini juga
dapat digunakan untuk menyelesaikan permasalahan komputasional seperti
memecahkan kata sandi atau password pada suatu program, membuat sistem
kerja atau mekanisme kerja dari suatu video game, ataupun sistem dasar dari
suatu simulasi komputer terhadap permasalahan di dunia nyata.

Dalam penggunaannya backtracking bergantung pada perintah yang diberikan


oleh penggunanya (prosedur kotak hitam) yang mana menentukan
permasalahan untuk diselesaikan dengan cara kandidat parsial dan bagaimana
mereka dikembangkan untuk menjadi kandidat penyelesaian maslaah secara
sepenuhnya. Untuk dapat mengimplementasikannya dengan baik anda
haruslah memahami fungsi flowchart dalam pemograman agar bisa
memberikan hasil yang maksimal.

Penggunaannya Secara Umum


Penggunaan algoritma pemograman bisa digunakan di berbagai macam-
macam bahasa pemrograman. Algoritma backtracking merupakan sebuah
bentuk tipikal dari algoritma rekursif. Saat ini algoritma backtracking banyak
diterapkan untuk membuat permainan video games menjadi lebih menarik,
banyak video games yang memanfaatkan kecerdasan buatan dengan
menggunakan algoritma backtracking untuk membuat permainan lebih
menarik.
Selain hal tersebut algoritma backtracking juga sangatlah berguna untuk
mencari solusi yang membutuhkan berbagai macam kombinasi jalan untuk
menyelesaikan permasalahan yang selalu berubah/ bersifat dinamis.

Untuk menyelesaikan masalah – masalah yang mempunyai kemungkinan


solusi yang kompleks, seperti permainan catur ataupun permasalahan –
permasalahan sejenisnya, sebaiknya ke dalam pohon kemungkinan diberikan
batas tertentu. Hal ini dilakukan sehingga tidak menghabiskan waktu yang
sangat lama (hal ini akan bergantung pada jenis kekuatan atau performa
komputer yang digunakan untuk menyelesaikan masalahnya). Semakin dalam
pohon yang ditelusuri, maka akan semakin akurat pula jalan menuju solusi
yang ingin dicari.

Alokasi memori yang akan digunakan untuk menyimpan jalan solusi


sebaiknya menggunakan dynamic array yang mengingat sebagian besar
program yang menggunakan algortima ini menghasilkan solusi yang sedang
dijelajahi (explore) menuju ke solusi yang diharapkan.

Mengenai Algoritma Backtracking


Algoritma backtracking menghitung kumpulan kandidat parsial, yang dalam
prinsipnya, hanya dapat diselesaikan dalam berbagai cara untu kmemberikan
berbagai kemungkinan solusi terhadap permasalahan yang hendak
diselesaikan. Penyelesaiannya juga dilakukan secara bertahap dari satu
sekuens kandidat kemungkinan ke kemungkinan lainnya.

Secara konseptual, setiap kandidat parsial direpresentasikan sebagai sebuah


node dari struktur pohon yang merupakan sebuah pencarian dari kandidat
potensial penyelesaian masalah. Setiap kandidat parsial merupakan sebuah
turunan dari kandidat yang berbeda dari asalnya yang memiliki langkah
perpanjangan dalam penyelesaiannya, turunan dari kandidat parsial yang tidak
dapat diperpanjang lagi.

Penggunaan algoritma backtracking tidak dapat dilepaskan oleh tingkat


perkembangan komputer yang ada beberapa dekade belakangan. Dewasa ini
komputer menjadi kebutuhan bagi semua kalangan dalam berbagai bidang.
Dengan kemunculannya berbagai macam algoritma yang dapat menyelesaikan
berbagai macam permasalahan – permasalahan yang dimiliki oleh manusia
dan juga kemunculan algoritma yang memiliki fungsi – fungsi yang beragam.
Tentu akan membuat perkembangan teknologi yang semakin canggih akan
menjadi mencapai berbagai aspek kehidupan manusia.

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

x Jika lintasan yang diperoleh dari perluasan simpul-E tidak mengarah ke


solusi maka simpul itu akan menjadi simpul mati dimana simpul itu tidak
akan diperluas lagi. x Jika posisi terakhir ada di simpul mati, maka pencarian
dilakukan dengan membangkitkan simpul anak yang lainnya dan jika tidak
ada simpul child maka dilakukan backtracking ke simpul parent. x Pencarian
dihentikan jika kita telah menemukan solusi atau tidak ada simpul hidup yang
dapat di diperlukan
1. Contoh Algoritma Backtracking
pengembangan sistem yang digunakan dalam penelitian ini yaitu incremental
model. Incremental model adalah model pengembangan sistem pada software
engineering berdasarkan requirement software yang dipecah menjadi
beberapa fungsi atau bagian sehingga model pengembangannya secara
bertahap. Dilain pihak ada yang mengartikan model incremental sebagai
perbaikan dari model waterfall dan sebagai standar pendekatan topdown.

Teori Algoritma Backtracking 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. Algoritma backtracking bekerja secara rekursif dan
melakukan pencarian solusi persoalan secara sistematis pada semua
kemungkinan solusi yang ada. Oleh karena algoritma ini berbasis pada
algoritma Depth-First Search (DFS), maka pencarian solusi dilakukan dengan
menelusuri suatu struktur berbentuk pohon berakar secara preorder. Algoritma
DFS merupakan algoritma pencarian secara mendalam. Cara kerja algoritma
ini adalah menelusuri salah satu kemungkinan yang ada hingga akar-akar
maksimal, baru setelah itu menelusuri kemungkinan yang lain hingga akar-
akar maksimal juga. Proses ini dicirikan dengan ekspansi simpul terdalam
lebih dahulu sampai tidak ditemukan lagi suksesor dari suatu simpul.

2. Prinsip dasar algoritma Backtracking


Prinsip dasar Algoritma Backtracking adalah mencoba semua kemungkinan
solusi yang ada. Perbedaan utamanya adalah pada konsep dasarnya,yaitu
pada backtracking semua solusi dibuat dalam bentuk pohon solusi (tree), dan
kemudian pohon tersebut sehingga ditemukan solusi terbaik yang diinginkan.
Seperti yang telah dijelaskan diatas bahwa pencarian solusi dengan
menggunakan algoritma backtracking ini berbasis pada DFS, maka kita
menggunakan pohon ruang status Langkah-langkah pencarian solusi dengan
Backtracking adalah sebagai berikut :
1. Solusi dicari dengan membentuk lintasan dari akar ke daun. Simpul
yang sudah dilahirkan dinamakan simpul hidup dan simpul hidup yang
diperluas dinamakan simpul-E (Expand- node).

2. Jika lintasan yang diperoleh dari perluasan simpul-E tidak mengarah ke


solusi, maka simpul itu akan menjadi simpul mati dimana simpul itu tidak
akan diperluas lagi.
3. Jika posisi terakhir ada di simpul mati, maka pencarian dilakukan dengan
membangkitkan simpul anak yang lainnya dan jika tidak ada simpul child
maka dilakukan backtracking ke simpul parent
4. Pencarian dihentikan jika kita telah menemukan solusi atau tidak ada
simpul hidup yang dapat di diperlukan. Gambar pertama kali diperkenalkan
oleh Tarjan dan Hopcroft 20 tahun yang lalu. Mereka menunjukkan
bagaimana DFS dapat digunakan untuk membangun sejumlah algoritma graph
yang efisien. Metode Depth First Search (DFS) merupakan metode pencarian
secara mendalam.
Cara kerjanya adalah menelusuri salah satu kemungkinan yang ada hingga
akarakar yang maksimal, baru setelah itu menelusuri kemungkinan yang lain
hingga akar-akar maksimal juga. Perlu diperhatikan bahwa kebutuhan waktu
dalam proses pencarian dengan DFS, sejalan dengan kedalaman maksimum
pohon pencarian. Jika kedalaman pohon tidak terbatas, maka dapat
dimungkinkan bahwa algoritma tidak akan berhenti. Hal ini dapat terjadi jika
ruang pencarian tidak terbatas, atau jika ruang pencarian mengandung siklus
keadaan. Dengan demikian algoritma DFS tidak menunjukkan sifat komplit,
hanya sebagian ruang pencarian yang ditempuh.

Algoritma BSA (Backtracking Search Algorithm) adalah salah satu algoritma


optimasi yang dapat digunakan untuk pengambilan keputusan. Contoh yang
dibahas kali ini adalah mengenai pencarian posisi dengan pengembalian nilai
fungsi maksimal.
Backtracking adalah istilah untuk mencari solusi dengan cara membuat calon
solusi baru secara terus menerus, dan calon solusi yang lebih baik akan
menggantikan solusi yang sudah dianggap tidak dapat menghasilkan solusi
yang lebih baik.
Diasumsikan ada sebaran titik 2 dimensi antara -2 sampai dengan 2
Fungsi yang diketahui adalah fungsi Himmelblau, dengan rumus f(x, y) =
(x^2+y-11)^2 + (x+y^2-7)^2
Tentukan posisi dimana fungsi tersebut mengembalikan nilai maksimal

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.

Sedangkan Grafik fungsi Himmelblau untuk sebaran titik dengan rentang


minimal -2 dan maksimal 2 adalah sebagai berikut.
Dapat dilihat bahwa pada gambar tersebut, didapatkan area dengan titik
tertinggi (berwarna merah) berada pada area x = -0, and y = -1, dimana titik
tersebut mengembalikan nilai fungsi tertinggi. Oleh sebab itu digunakan
algoritma ini untuk mencari titik di area berwarna merah tersebut.

Sebelum masuk kedalam langkah-langkah pembahasan algoritma, ada


beberapa konstanta atau parameter yang harus diketahui, yaitu:
* Tentukan ukuran populasi yang digunakan
Diasumsikan dalam kasus ini, ukuran populasi adalah 20
ukuranPopulasi = 20;
* Tentukan jumlah maksimal iterasi yang digunakan
Diasumsikan dalam kasus ini, jumlah maksimal iterasi adalah 500 kali
jumlahIterasi = 500;
* Tentukan jumlah dimensi yang digunakan
Diasumsikan dalam kasus ini, jumlah dimensi adalah 2 karena posisi bunga
hanya ditentukan dari 2 sumbu yaitu sumbu x dan y
jumlahDimensi = 2;
* Tentukan posisi minimal dan maksimal dari fungsi yang akan dihitung
Jika tidak ada batasan posisi, tentu saja posisi yang mendekati tak terhingga
akan terpilih karena akan mengembalikan nilai fungsi yang sangat besar
Diasumsikan dalam kasus ini, posisi minimal adalah -2, dan posisi maksimal
adalah +2
minPosisi = -2;
maksPosisi = 2;

Langkah-langkah penggunaan algoritma ini adalah


* Lakukan proses perhitungan dengan metode BSA (Backtracking Search
Algorithm)
Penjelasan tentang fungsi ini akan dijelaskan pada perhitungan dibawah ini
(poin 1 – 4)
[posisiTerbaik,nilaiFungsiTerbaik] = BSA( ...
ukuranPopulasi, jumlahIterasi, jumlahDimensi, Lb, Ub);
Memasuki perhitungan pada fungsi BSA
1. Lakukan inisialisasi individu populasi pada posisi acak,
Kemudian hitung nilai fungsi pada posisi tersebut
Penjelasan tentang fungsi ini akan dijelaskan pada perhitungan dibawah ini
for i = 1:ukuranPopulasi,
posisi(i,:) = Lb + (Ub-Lb).* rand(1,jumlahDimensi);
nilaiFungsi(i) = HitungNilaiFungsi(posisi(i,:));
end
* Gunakan fungsi ini untuk menghitung nilai fungsi dengan rumus:
f(x, y) = (x ^ 2 + y – 11) ^ 2 + (x + y ^ 2 – 7) ^ 2
function z = HitungNilaiFungsi(u)
z = (u(1)^2 + u(2) - 11) ^ 2 + (u(1) + u(2)^2 - 7) ^ 2;
2. Catat posisi terbaik dengan nilai fungsi tertinggi sementara
[nilaiFungsiTerbaik,I] = max(nilaiFungsi);
posisiTerbaik = posisi(I,:);
3. Lakukan inisialisasi posisi acak pada populasi dummy
Populasi ini akan digunakan untuk menghitung populasi anak pada setiap
perulangan
for i = 1:ukuranPopulasi,
posisiDummy(i,:) = Lb + (Ub-Lb).* rand(1,jumlahDimensi);
end
4. Lakukan perhitungan sebanyak jumlah iterasi (poin 4a – 4i)
for iterasi = 1:jumlahIterasi,
...
4a. Tentukan nilai acak antara 0 sampai dengan 1
Jika nilai acak ini kurang dari 0.5,
%maka posisi populasi sebenarnya akan menggantikan nilai posisi populasi
dummy
if rand < 0.5, posisiDummy = posisi; end;
4b. Lakukan pengacakan urutan posisi individu dalam populasi dummy
posisiDummy=posisiDummy(randperm(ukuranPopulasi),:);
4c. Tentukan faktor skala untuk digunakan dalam perhitungan posisi anak
faktor skala dapat dihitung menggunakan metode Brownian, invers distribusi
gamma, atau distribusi Levy
Dalam kasus ini akan digunakan metode Brownian yang paling standar
faktorSkala = 3 * randn; % STANDARD brownian
4d. Lakukan pemetaaan posisi dimensi yang akan digunakan untuk
menghitung posisi anak
Posisi pada dimensi acak tertentu akan bernilai 1,
dan nantinya posisi tersebut akan dikalikan dengan faktor skala untuk
mendapatkan nilai sebenarnya
map=zeros(ukuranPopulasi,jumlahDimensi);
if rand < 0.5,
for i=1:ukuranPopulasi,
u=randperm(jumlahDimensi);
map(i,u(1:ceil(rand*jumlahDimensi)))=1;
end
else
for i=1:ukuranPopulasi,
map(i,randi(jumlahDimensi))=1;
end
end
4e. Hitung posisi anak dengan rumus:
posisi anak = pop + (map * F) * (selisih populasi dummy dan populasi
sebenarnya)
posisiAnak = posisi + (map.*faktorSkala) .* (posisiDummy-posisi);
4f. Jika posisi anak tersebut ternyata diluar batas yang diperbolehkan,
maka kembalikan posisinya agar masuk dalam batas tersebut
Kemudian hitung nilai fungsi pada posisi yang baru
for i=1:ukuranPopulasi
tmpPosisi=posisiAnak(i,:);

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

- PENERAPAN ALGORITMA BACKTRACKING PADA


PERMAINAN MATH MAZE
Math Maze Permainan Math Maze hanya dimainkan oleh 1 orang.
Permainan Math Maze merupakan game sederhana yang bertujuan
menentukan jalur yang tepat untuk mencapai tujuan yang telah ditetapkan.
Pada dasarnya Math Maze adalah permainan yang merupakan perkembangan
dari permainan Maze (labirin). Perbedaan yang ada pada Math Maze adalah
permainan ini menggunakan angka-angka yang ada pada sisi bagian kiri dan
bagian atas yang disebut dengan nilai-nilai pemandu . Angka-angka tersebut
merupakan indikator yang menyatakan berapa banyak sel di dalam baris atau
kolom tersebut yang dilalui oleh garis yang merupakan jalur menuju tujuan
akhir. Arah pergerakan jalur ditentukan dengan mengklik dari satu sel ke sel
lain pada grid. Pilihan sel untuk di klik ada 4 yaitu atas, kanan, kiri dan
bawah. Pemain tidak boleh mengklik sel lain dengan arah diagonal. Saat di
klik sel satu akan terhubung dengan sel selanjutnya dengan suatu garis.
Apabila pemain berhasil mencapai pintu keluar atau finish maka terbentuk
jalur dari garis-garis tersebut. Dengan nilai- nilai pemandu pada sisi kiri dan
sisi atas papan permainan maka pemain harus memperhitungkan sel mana
yang harus diklik setelah pemain mulai dari sel Start atau Pintu Masuk.
Pemain harus memperkirakan jalur solusi yang terbentuk nanti harus sesuai
dengan nilai- nilai pemandu. Apabila jumlah nilai baris dan kolom yang
dilewati tidak sesuai sampai mencapai garis selesai maka dianggap gagal
menemukan jalur yang tepat menuju pintu keluar atau selesai. Berikut ini akan
dijelaskan proses permainan Math maze. Pada awal permainan akan
disediakan papan permainan berupa grid atau sel-sel dengan ukuran n x m Di
antara sel tersebut terdapat pintu masuk dan pintu keluar. Papan permainan
Math Maze ukuran 6x6 60
- Penerapan Metode Sistem yang akan dibuat dengan penerapan algoritma
backtracking bukan untuk mencari solusi pada Papan permainan Math maze
yang sudah siap, tapi penerapan yang dilakukan adalah proses pembuatan atau
pembangkitan papan permainan Math Maze itu sendiri. Proses Pembuatan
papan permainan Math maze itu terdiri dari beberapa langkah yaitu: proses
Pembangkitan sebuah maze, pencarian solusi dari Maze yang sudah di
bangkitkan, dan membuat papan permainan Math Maze baru dengan
memanfaatkan Maze yang sudah di ketahui solusinya.

- Pembangkitan Maze Proses Pembangkitan sebuah maze yang


dilakukan dengan backtracking adalah dengan prosedur berikut.
• Pilih satu sel pada sebuah grid
• Pilih secara acak sel terdekat atau sel tetangga dengan gerakan yang di
perbolehkan adalah atas, bawah, kiri, kanan. Tidak diperbolehkan memilih sel
tetangga secara diagonal.
• Jika sel tetangga tersebut belum pernah kita datangi, maka pindah ke sel
tersebut dan hapus pembatas atau wall antara sel tetangga tersebut dengan sel
yang sebelumnya di tempati.
• Jika Tidak ditemukan sel tetangga yang belum didatangi maka kita harus
melakukan backtracking ke sel sebelumnya.
• Langkah ini dilakukan sampai semua sel pada grid sudah didatangi.
• Tentukan posisi Start dan Finish.
- Pencarian solusi dari Maze yang sudah dibangkitkan. Apabila maze sudah di
bangkitkan maka selanjutnya dalah mencari solusinya. Langkahlangkahnya
adalah sebagai berikut • Dari sel Start, pilih secara acak sel terdekat yang bisa
di kunjungi. Sama seperti proses pembangkitan maze, hanya ada 4 gerakan
yang diperbolehkan yaitu atas, bawah, kiri dan kanan. • Apabila Sel belum
pernah di datangi maka pindah ke sel tersebut. Pilih lagi sel tetangga secara
acak. 61 - JURNAL INFORMATIKA, VOLUME 6 NOMOR 1, APRIL 2010
• Apabila Sel menemui tembok atau wall, maka lakukan backtracking ke sel
sebelumnya. • Lakukan langkah-langkah ini sampai menemukan sel yang
menjadi selesai atau pintu keluar. 3.3 Pembuatan papan permainan Math
Maze Proses ini adalah proses untuk membuat papan permainan Math Maze
dengan memanfaatkan maze yang sudah dibangkitkan dan solusi yang sudah
di temukan dari maze tersebut. Langkah-langkahnya adalah menghitung
jumlah total sel pada baris dan kolom yang di lewati oleh garis atau jalur
solusi. Jumlah sel yang dilewati pada baris dan kolom itulah yang nantinya
akan menjadi nilai-nilai baris dan kolom sebagai panduan pada papan
permainan. Papan permainan Math Maze sendiri akan dibentuk dengan
menghilangkan tembok atau wall pada maze sempurna sehingga akan kembali
berbentuk seperti grid biasa, hanya saja sudah disediakan pintu masuk dan
pintu keluar serta nilai-nilai pada baris dan kolom sebagai panduan untuk
menemukan jalur solusi. Dengan demikian papan permainan sudah dapat
dimainkan oleh pemakai. 3.4 Algoritma Program Langkah pertama dalam
Proses pembangkitan ini dilakukan dengan Algoritma Backtracking untuk
membentuk sebuah Maze. Mulai a. Masukkan ukuran grid ( i x j) b. Selama
Total sel pada grid < sel yang telah di kunjungi lakukan langkah d, e, dan f. c.
Pilih secara acak sel tetangga (kanan, kiri , atas, bawah) d. Cek apakah sel
sudah pernah dikunjungi, jika ya kembali ke langkah d, jika tidak lakukan
langkah f. e. Hapus wall atau pembatas pada sel f. Tentukan pintu masuk
(start) dan goal (pintu keluar) g. Selesai

Anda mungkin juga menyukai