Skip to content

Commit 806cbd1

Browse files
authored
chore: menambahkan algoritma searching (#49)
Signed-off-by: slowy07 <[email protected]>
1 parent df5375e commit 806cbd1

File tree

7 files changed

+268
-0
lines changed

7 files changed

+268
-0
lines changed

searching/interpolation.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package searching
2+
3+
// fungsi melakukan pencarian menggunakan algoritam interpolasi searching
4+
// interpolasi searching adalah versi lanjuta dari binary searching yang menentukan
5+
// posisi elemen dengan memperkirakan lokasinya berdasarkan nilai target
6+
//
7+
// Parameter:
8+
// - sortedData []int: array tipe data int yang sudah terurut ascending
9+
// - tebakan int: nilai yang akan dicari dalam sebuah array
10+
//
11+
// Return:
12+
// - indeks pertama dari nilai yang cocok
13+
// - error jika tidak ditemukan atau kondisi input tidak valid
14+
func pencarianInterpolasi(sortedData []int, tebakan int) (int, error) {
15+
// jika data kosong, langsung return error karena tidak ada data yang dicari
16+
if len(sortedData) == 0 {
17+
return -1, ErrorMessage
18+
}
19+
20+
// inisialisasi batas bawah (low) dan atas (high)
21+
// lowValue = nilai pada index low (elemen pertama)
22+
// highValue = nilai pada index high (elemen terakhir)
23+
var (
24+
low, high = 0, len(sortedData) - 1
25+
lowValue, highValue = sortedData[low], sortedData[high]
26+
)
27+
28+
// - lowValue != highValue (untuk menghindari terjadinya pembagian dengan nol)
29+
// - tebakan berada di antara lowValue dan highValue
30+
// - lowValue <= tebakan && tebakan <= highValue (range valid)
31+
for lowValue != highValue && (lowValue <= tebakan) && (tebakan <= highValue) {
32+
// hitung posisi mid secara interpolasi
33+
// rumus interpolasi
34+
// mid = low + ((tebakan - lowValue) * (high - low)) / (highValue - lowValue)
35+
// casting ke float64 digunakan untuk presisi hitungan pecahan
36+
mid := low + int(float64(float64((tebakan-lowValue)*(high-low))/float64(highValue-lowValue)))
37+
38+
// jika nilai di mid sama dengan tebakan, kita cek apakah ada duplikasi di
39+
// sebelumnya
40+
if sortedData[mid] == tebakan {
41+
// geser ke kiri sampai ketemu elemen yang bukan tebakan
42+
// ini nantinya dilakukan untuk menemukan indeks pertama dari nilai
43+
// yang cocok
44+
for mid > 0 && sortedData[mid-1] == tebakan {
45+
mid--
46+
}
47+
// setelah ketemu indeks pertama, return dari hasil
48+
return mid, nil
49+
}
50+
51+
// jika nilai di mid lebih besar dari tebakan, geser batas atas
52+
if sortedData[mid] > tebakan {
53+
high, highValue = mid-1, sortedData[high]
54+
} else {
55+
// jika nilai di mid lebih kecil dari tebakan, geser batas bawah
56+
low, lowValue = mid+1, sortedData[low]
57+
}
58+
}
59+
60+
// setelah keluar dari looping, kita akan cek sekali lagi apakah
61+
// lowValue == tebkan, karena bisa saja tebakan berada di tpat di lowValue
62+
// setlah lowValue setelah iterasi selesai
63+
if tebakan == lowValue {
64+
return low, nil
65+
}
66+
// jika semua kondisi diatas tidak terpenuhi, maka nilai tidak ditemukan
67+
return -1, ErrorMessage
68+
}

searching/interpolation_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package searching
2+
3+
import "testing"
4+
5+
func TestInterpolasi(t *testing.T) {
6+
for _, test := range searchTests {
7+
valueAktual, _ := pencarianInterpolasi(test.data, test.key)
8+
if valueAktual != test.ekspetasi {
9+
t.Errorf("test '%s' gagal: input array `%v` dengan key `%d`, ekspetasi `%d`, aktual: `%d`", test.name, test.data, test.key, test.ekspetasi, valueAktual)
10+
}
11+
}
12+
}

searching/linear_searching.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package searching
2+
3+
// melakukan pencarian secar linier (sequential searching)
4+
// untuk menemukan kemunculan pertama dari nilai tertentu (query) di
5+
// dalam array
6+
//
7+
// Parameter:
8+
// - array []int: kumpulan data bertipe integer yang akan dicari
9+
// - query int: nilai yang ingin dicari indeksnya dalam array
10+
//
11+
// Return:
12+
// - int: indeks pertama tempat query ditemukan
13+
// - error: jika query tidak ditemukan, maka throw error
14+
func linearSearching(array []int, query int) (int, error) {
15+
// melakukan iterasi terhadap array menggunakan array
16+
// dimana `i` adalah indeks dan `item` adalah nilai pada posisi tersebut
17+
for i, item := range array {
18+
// jika elemen saat ini (item) sama dengan nilai yang dicari
19+
// maka kembalikan indeksnya dan error nil
20+
if item == query {
21+
return i, nil
22+
}
23+
}
24+
25+
// jika semua elemen telah dicek dan tidak ada yang cocok
26+
// maka kembalikan -1 sebagai indikasi tidak diteumkan, dan throw ErrorMessage
27+
return -1, ErrorMessage
28+
}

searching/linear_searching_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package searching
2+
3+
import "testing"
4+
5+
func TestLinearSearching(t *testing.T) {
6+
for _, test := range searchTests {
7+
valueAktual, _ := linearSearching(test.data, test.key)
8+
if valueAktual != test.ekspetasi {
9+
t.Errorf("test `%s` gagal: input array `%v` dengan key `%d`, ekspetasi: `%d`, aktual: `%d`", test.name, test.data, test.key, test.ekspetasi, valueAktual)
10+
}
11+
}
12+
}

searching/ternary_searching.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package searching
2+
3+
import (
4+
"fmt"
5+
"math"
6+
)
7+
8+
// ternaryMaxSearching mencari nilai maks lokal dari fungsi f(x) dalam interval [a, b]
9+
// dengan toleransi epsilon menggunakan ternary searching
10+
//
11+
// algoritma
12+
// - interval [a, b] dibagi menjadi tiga bagian yang sama panjang
13+
// - evaluasi nilai f(x) pada dua titik tengah: kiri dan kanan
14+
// - jika f(kiri) < f(kanan), maka nilai maks berada dis sisi kanan
15+
// proses ini akan berlanjut secara rekursif hingga interval lebih kecil dari epsilon
16+
//
17+
// Parameter:
18+
// a - batas kiri interval (float64)
19+
// b - batas kanan interval (float64)
20+
// epsilon - toleransi akurasi hasil (float64)
21+
// f - fungsi matematis satu variable bertipe func(x float64) float64
22+
//
23+
// Return:
24+
// float64 - nilai maks dari f(x) dalam unterval [a, b]
25+
// error - error jika input tidak valid atau terjadi masalah selama eksekusi
26+
func ternaryMaxSearching(a, b, epsilon float64, f func(x float64) float64) (float64, error) {
27+
if a == math.Inf(-1) || b == math.Inf(1) {
28+
return -1, fmt.Errorf("interval harus memiliki angka float64")
29+
}
30+
if math.Abs(a-b) <= epsilon {
31+
return f((a + b) / 2), nil
32+
}
33+
34+
kiri := (2*a + b) / 3
35+
kanan := (a + 2*b) / 3
36+
if f(kiri) < f(kanan) {
37+
return ternaryMaxSearching(kiri, b, epsilon, f)
38+
}
39+
40+
return ternaryMaxSearching(a, kanan, epsilon, f)
41+
}
42+
43+
// ternaryMinSearching mencari nilai minimum lokal dari fungsi(x) dalam interval [a, b]
44+
//
45+
// algoritma:
46+
// - interval [a, b] dibagi menjadi tiga bagian sama panjang
47+
// - evaluasi nilai f(x) pada dua titik tengah: kiri dan kanan
48+
// - jika f(kiri) > f(kanan), maka nilai minimum berbeda di sisi kanan
49+
// proses berlanjut secara rekursif hingga interval lebih kecil dari epsilon
50+
func ternaryMinSearching(a, b, epsilon float64, f func(x float64) float64) (float64, error) {
51+
if a == math.Inf(-1) || b == math.Inf(1) {
52+
return -1, fmt.Errorf("interval harus memiliki angka float64")
53+
}
54+
55+
if math.Abs(a-b) <= epsilon {
56+
return f((a + b) / 2), nil
57+
}
58+
59+
kiri := (2*a + b) / 3
60+
kanan := (a + 2*b) / 3
61+
if f(kiri) > f(kanan) {
62+
return ternaryMinSearching(kiri, b, epsilon, f)
63+
}
64+
return ternaryMinSearching(a, kanan, epsilon, f)
65+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package searching
2+
3+
import (
4+
"math"
5+
"testing"
6+
)
7+
8+
const EPS = 1e-6
9+
10+
func equal(a, b float64) bool {
11+
return math.Abs(a-b) <= EPS
12+
}
13+
14+
func TestTernaryMax(t *testing.T) {
15+
var tests = []struct {
16+
f func(x float64) float64
17+
a float64
18+
b float64
19+
ekspetasi float64
20+
}{
21+
{f: func(x float64) float64 { return -x * x }, a: 1, b: -1, ekspetasi: 0},
22+
}
23+
24+
for _, test := range tests {
25+
hasil, error := ternaryMaxSearching(test.a, test.b, EPS, test.f)
26+
if error != nil {
27+
t.Errorf("error, data: `%v`", error)
28+
}
29+
30+
if !equal(hasil, test.ekspetasi) {
31+
t.Errorf("hasil salah, ekspetasi: `%v`, return aktual: `%v`", test.ekspetasi, hasil)
32+
}
33+
}
34+
}
35+
36+
func TestTernaryMin(t *testing.T) {
37+
var tests = []struct {
38+
f func(x float64) float64
39+
a float64
40+
b float64
41+
ekspetasi float64
42+
}{
43+
{f: func(x float64) float64 { return x * x }, a: -1, b: 1, ekspetasi: 0},
44+
{f: func(x float64) float64 { return 2*x*x + x + 1 }, a: -1, b: 1, ekspetasi: 0.875},
45+
}
46+
47+
for _, test := range tests {
48+
hasil, error := ternaryMinSearching(test.a, test.b, EPS, test.f)
49+
if error != nil {
50+
t.Errorf("error, data: `%v`", error)
51+
}
52+
53+
if !equal(hasil, test.ekspetasi) {
54+
t.Errorf("hasil salah, ekspetasi: `%v`, return aktual: `%v`", test.ekspetasi, hasil)
55+
}
56+
}
57+
}

searching/utilities.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package searching
2+
3+
import "errors"
4+
5+
var ErrorMessage = errors.New("target tidak terdapat pada array")
6+
7+
type searchTest struct {
8+
data []int
9+
key int
10+
ekspetasi int
11+
ekspetasiError error
12+
name string
13+
}
14+
15+
var searchTests = []searchTest{
16+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 10, 9, nil, "Sanity"},
17+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 9, 8, nil, "Sanity"},
18+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 8, 7, nil, "Sanity"},
19+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 7, 6, nil, "Sanity"},
20+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 6, 5, nil, "Sanity"},
21+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 5, 4, nil, "Sanity"},
22+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 4, 3, nil, "Sanity"},
23+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 3, 2, nil, "Sanity"},
24+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 2, 1, nil, "Sanity"},
25+
{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 1, 0, nil, "Sanity"},
26+
}

0 commit comments

Comments
 (0)