Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions searching/interpolation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package searching

// fungsi melakukan pencarian menggunakan algoritam interpolasi searching
// interpolasi searching adalah versi lanjuta dari binary searching yang menentukan
// posisi elemen dengan memperkirakan lokasinya berdasarkan nilai target
//
// Parameter:
// - sortedData []int: array tipe data int yang sudah terurut ascending
// - tebakan int: nilai yang akan dicari dalam sebuah array
//
// Return:
// - indeks pertama dari nilai yang cocok
// - error jika tidak ditemukan atau kondisi input tidak valid
func pencarianInterpolasi(sortedData []int, tebakan int) (int, error) {
// jika data kosong, langsung return error karena tidak ada data yang dicari
if len(sortedData) == 0 {
return -1, ErrorMessage
}

// inisialisasi batas bawah (low) dan atas (high)
// lowValue = nilai pada index low (elemen pertama)
// highValue = nilai pada index high (elemen terakhir)
var (
low, high = 0, len(sortedData) - 1
lowValue, highValue = sortedData[low], sortedData[high]
)

// - lowValue != highValue (untuk menghindari terjadinya pembagian dengan nol)
// - tebakan berada di antara lowValue dan highValue
// - lowValue <= tebakan && tebakan <= highValue (range valid)
for lowValue != highValue && (lowValue <= tebakan) && (tebakan <= highValue) {
// hitung posisi mid secara interpolasi
// rumus interpolasi
// mid = low + ((tebakan - lowValue) * (high - low)) / (highValue - lowValue)
// casting ke float64 digunakan untuk presisi hitungan pecahan
mid := low + int(float64(float64((tebakan-lowValue)*(high-low))/float64(highValue-lowValue)))

// jika nilai di mid sama dengan tebakan, kita cek apakah ada duplikasi di
// sebelumnya
if sortedData[mid] == tebakan {
// geser ke kiri sampai ketemu elemen yang bukan tebakan
// ini nantinya dilakukan untuk menemukan indeks pertama dari nilai
// yang cocok
for mid > 0 && sortedData[mid-1] == tebakan {
mid--
}
// setelah ketemu indeks pertama, return dari hasil
return mid, nil
}

// jika nilai di mid lebih besar dari tebakan, geser batas atas
if sortedData[mid] > tebakan {
high, highValue = mid-1, sortedData[high]
} else {
// jika nilai di mid lebih kecil dari tebakan, geser batas bawah
low, lowValue = mid+1, sortedData[low]
}
}

// setelah keluar dari looping, kita akan cek sekali lagi apakah
// lowValue == tebkan, karena bisa saja tebakan berada di tpat di lowValue
// setlah lowValue setelah iterasi selesai
if tebakan == lowValue {
return low, nil
}
// jika semua kondisi diatas tidak terpenuhi, maka nilai tidak ditemukan
return -1, ErrorMessage
}
12 changes: 12 additions & 0 deletions searching/interpolation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package searching

import "testing"

func TestInterpolasi(t *testing.T) {
for _, test := range searchTests {
valueAktual, _ := pencarianInterpolasi(test.data, test.key)
if valueAktual != test.ekspetasi {
t.Errorf("test '%s' gagal: input array `%v` dengan key `%d`, ekspetasi `%d`, aktual: `%d`", test.name, test.data, test.key, test.ekspetasi, valueAktual)
}
}
}
28 changes: 28 additions & 0 deletions searching/linear_searching.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package searching

// melakukan pencarian secar linier (sequential searching)
// untuk menemukan kemunculan pertama dari nilai tertentu (query) di
// dalam array
//
// Parameter:
// - array []int: kumpulan data bertipe integer yang akan dicari
// - query int: nilai yang ingin dicari indeksnya dalam array
//
// Return:
// - int: indeks pertama tempat query ditemukan
// - error: jika query tidak ditemukan, maka throw error
func linearSearching(array []int, query int) (int, error) {
// melakukan iterasi terhadap array menggunakan array
// dimana `i` adalah indeks dan `item` adalah nilai pada posisi tersebut
for i, item := range array {
// jika elemen saat ini (item) sama dengan nilai yang dicari
// maka kembalikan indeksnya dan error nil
if item == query {
return i, nil
}
}

// jika semua elemen telah dicek dan tidak ada yang cocok
// maka kembalikan -1 sebagai indikasi tidak diteumkan, dan throw ErrorMessage
return -1, ErrorMessage
}
12 changes: 12 additions & 0 deletions searching/linear_searching_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package searching

import "testing"

func TestLinearSearching(t *testing.T) {
for _, test := range searchTests {
valueAktual, _ := linearSearching(test.data, test.key)
if valueAktual != test.ekspetasi {
t.Errorf("test `%s` gagal: input array `%v` dengan key `%d`, ekspetasi: `%d`, aktual: `%d`", test.name, test.data, test.key, test.ekspetasi, valueAktual)
}
}
}
65 changes: 65 additions & 0 deletions searching/ternary_searching.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package searching

import (
"fmt"
"math"
)

// ternaryMaxSearching mencari nilai maks lokal dari fungsi f(x) dalam interval [a, b]
// dengan toleransi epsilon menggunakan ternary searching
//
// algoritma
// - interval [a, b] dibagi menjadi tiga bagian yang sama panjang
// - evaluasi nilai f(x) pada dua titik tengah: kiri dan kanan
// - jika f(kiri) < f(kanan), maka nilai maks berada dis sisi kanan
// proses ini akan berlanjut secara rekursif hingga interval lebih kecil dari epsilon
//
// Parameter:
// a - batas kiri interval (float64)
// b - batas kanan interval (float64)
// epsilon - toleransi akurasi hasil (float64)
// f - fungsi matematis satu variable bertipe func(x float64) float64
//
// Return:
// float64 - nilai maks dari f(x) dalam unterval [a, b]
// error - error jika input tidak valid atau terjadi masalah selama eksekusi
func ternaryMaxSearching(a, b, epsilon float64, f func(x float64) float64) (float64, error) {
if a == math.Inf(-1) || b == math.Inf(1) {
return -1, fmt.Errorf("interval harus memiliki angka float64")
}
if math.Abs(a-b) <= epsilon {
return f((a + b) / 2), nil
}

kiri := (2*a + b) / 3
kanan := (a + 2*b) / 3
if f(kiri) < f(kanan) {
return ternaryMaxSearching(kiri, b, epsilon, f)
}

return ternaryMaxSearching(a, kanan, epsilon, f)
}

// ternaryMinSearching mencari nilai minimum lokal dari fungsi(x) dalam interval [a, b]
//
// algoritma:
// - interval [a, b] dibagi menjadi tiga bagian sama panjang
// - evaluasi nilai f(x) pada dua titik tengah: kiri dan kanan
// - jika f(kiri) > f(kanan), maka nilai minimum berbeda di sisi kanan
// proses berlanjut secara rekursif hingga interval lebih kecil dari epsilon
func ternaryMinSearching(a, b, epsilon float64, f func(x float64) float64) (float64, error) {
if a == math.Inf(-1) || b == math.Inf(1) {
return -1, fmt.Errorf("interval harus memiliki angka float64")
}

if math.Abs(a-b) <= epsilon {
return f((a + b) / 2), nil
}

kiri := (2*a + b) / 3
kanan := (a + 2*b) / 3
if f(kiri) > f(kanan) {
return ternaryMinSearching(kiri, b, epsilon, f)
}
return ternaryMinSearching(a, kanan, epsilon, f)
}
57 changes: 57 additions & 0 deletions searching/ternary_searching_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package searching

import (
"math"
"testing"
)

const EPS = 1e-6

func equal(a, b float64) bool {
return math.Abs(a-b) <= EPS
}

func TestTernaryMax(t *testing.T) {
var tests = []struct {
f func(x float64) float64
a float64
b float64
ekspetasi float64
}{
{f: func(x float64) float64 { return -x * x }, a: 1, b: -1, ekspetasi: 0},
}

for _, test := range tests {
hasil, error := ternaryMaxSearching(test.a, test.b, EPS, test.f)
if error != nil {
t.Errorf("error, data: `%v`", error)
}

if !equal(hasil, test.ekspetasi) {
t.Errorf("hasil salah, ekspetasi: `%v`, return aktual: `%v`", test.ekspetasi, hasil)
}
}
}

func TestTernaryMin(t *testing.T) {
var tests = []struct {
f func(x float64) float64
a float64
b float64
ekspetasi float64
}{
{f: func(x float64) float64 { return x * x }, a: -1, b: 1, ekspetasi: 0},
{f: func(x float64) float64 { return 2*x*x + x + 1 }, a: -1, b: 1, ekspetasi: 0.875},
}

for _, test := range tests {
hasil, error := ternaryMinSearching(test.a, test.b, EPS, test.f)
if error != nil {
t.Errorf("error, data: `%v`", error)
}

if !equal(hasil, test.ekspetasi) {
t.Errorf("hasil salah, ekspetasi: `%v`, return aktual: `%v`", test.ekspetasi, hasil)
}
}
}
26 changes: 26 additions & 0 deletions searching/utilities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package searching

import "errors"

var ErrorMessage = errors.New("target tidak terdapat pada array")

type searchTest struct {
data []int
key int
ekspetasi int
ekspetasiError error
name string
}

var searchTests = []searchTest{
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 10, 9, nil, "Sanity"},
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 9, 8, nil, "Sanity"},
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 8, 7, nil, "Sanity"},
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 7, 6, nil, "Sanity"},
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 6, 5, nil, "Sanity"},
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 5, 4, nil, "Sanity"},
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 4, 3, nil, "Sanity"},
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 3, 2, nil, "Sanity"},
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 2, 1, nil, "Sanity"},
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 1, 0, nil, "Sanity"},
}