This repository has been archived by the owner on Feb 27, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
algorithm.js
256 lines (231 loc) · 6.38 KB
/
algorithm.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
// class sebuah kota
class City {
constructor(x, y) {
// koordinat dari kota
this.x = x;
this.y = y;
// element untuk render
this.circle_element = new Circle({ x: x, y: y }, { r: 0, g: 0, b: 0, a: 1.0 });
}
isSamePlace(city) {
// fungsi untuk test jika kotanya sama
return this.x == city.x && this.y == city.y;
}
}
// class sebuah semut yang berjalan
class Ant {
constructor(city) {
// kota yang sudah dikunjungi
this.visited = [];
this.visited.push(city);
// jalan yang dilalui
this.paths = [];
// kota pertama yang dikunjungi
this.starting = city;
// kota yang sedang dikunjungi
this.current = city;
// jarak yang ditempuh
this.cost = 0;
}
hasVisited(city) {
// apakah sudah mengunjungi kota
return this.visited.includes(city);
}
hasWalked(path) {
// apakah melalui suatu jalan
let result = false;
this.paths.forEach((path_a) => {
if (path_a.isPathOf(path.city_a, path.city_b)) {
result = true;
return true;
}
});
return result;
}
// fungsi memindah semut
goToCity(city) {
// menambah daftar yang sudah dikunjungi
this.visited.push(city);
// menambah jalan yang sudah dilewati
let newpath = map.getPath(this.current, city);
this.paths.push(newpath);
// menghitung distance ulang
this.cost += newpath.distance();
// set city yang dikunjungi
this.current = city;
}
// get kota sekarang
currentCity() {
return this.current;
}
}
// class jalan yang dapat di tempuh
class Path {
constructor(city_a, city_b) {
// feromon, bahan kimia sebagai penanda yang digunakan semut
this.pheromones = 1;
// kota yang menghubungkan 2 tempat
this.city_a = city_a;
this.city_b = city_b;
}
// jarak suatu jalan
distance() {
let delta_y = this.city_a.y - this.city_b.y;
let delta_x = this.city_a.x - this.city_b.x;
// menggunakan euclidean
return Math.hypot(delta_y, delta_x);
}
//check kalau salah satu kota ada dalam path
isPathOf(city_a, city_b) {
return this.city_a.isSamePlace(city_a) && this.city_b.isSamePlace(city_b);
}
}
// class kumpulan dari path path membentu suatu map
class Map {
constructor(cities) {
// generate array yang merupakan setiap kemungkinan jalan dari 2 kota (map)
this.map = [];
// feromon terbesar buat render
this.maxpheromones = 1;
// membuat map yang pathnya tidak menunjuk diri sendiri (xx A-A)
cities.forEach((city_a) => {
cities.forEach((city_b) => {
// cek kalau kotanya sama
if (!city_a.isSamePlace(city_b)) {
// kalau kota tidak sama cek apakah path sudah ada
if (!this.getPath(city_a, city_b)) {
// jika belum tambah ke map
this.map.push(new Path(city_a, city_b));
}
}
});
});
}
// melakukan pencarian jalan
getPath(city_a, city_b) {
let result = false;
// untuk setiap path dalam map
this.map.some((path) => {
if (path.isPathOf(city_a, city_b)) {
// jika menemukan jalan yang ujungnya sesuai dengan yang diminta
// return jalan
result = path;
return true;
}
});
// return hasil
return result;
}
}
// fungsi roulettewheel mengambil data secara random
function rouletteWheel(pairs) {
let rand = Math.random();
let sum = pairs.reduce((tot, cur) => {
return tot + cur.p;
}, 0);
// pencegahan error ketika jumlah probabilitas 0
let app_sum = sum == 0 ? 1 : sum;
let offset = 0;
let result;
pairs.some((pair) => {
// jika jumlah probabilitas 0, semua memiliki probabilitas sama
if (sum == 0) offset += 1 / pairs.length;
else offset += pair.p / app_sum;
if (offset > rand) {
result = pair;
return true;
}
});
return result.c;
}
// menghitung kemungkinan semut dari satu kota ke kota lain
function getProbability(current, destination) {
//path yang dipakai
let selected_path = map.getPath(current, destination);
// tingkat kuat feromon
let pheromones_level = Math.pow(selected_path.pheromones, ALPHA_VAR);
// tingkat ketertarikan
let attractiveness = Math.pow(1 / selected_path.distance(), BETA_VAR);
return pheromones_level * attractiveness;
}
//fungsi untuk mengupdate feromon
function updatePheromone() {
//untuk setiap path dalam map
map.map.forEach((path) => {
// feromon lama di evaporasi
let temp = path.pheromones * EVAPORATE_VAR;
// untuk setiap semut
ants.forEach((ant) => {
// ditambah dengan setiap semut yang melewati path
if (ant.hasWalked(path)) temp += ant.cost;
});
// set feromon baru
path.pheromones = temp;
if (map.maxpheromones < temp) map.maxpheromones = temp;
});
}
// masukkan data city
// mempersiapkan ACO
function initializeACO() {
// buat map (paths)
map = new Map(cities);
ants = [];
}
// menjalankan simulasi
function iterateACO() {
ants = [];
for (let i = 0; i < ANTS_VAR; i++) {
ants.push(new Ant(cities[Math.floor(Math.random() * cities.length)]));
}
// untuk setiap kota
cities.forEach((_element, index) => {
// untuk setiap semut
ants.forEach((ant) => {
// jika bukan kota terakhir (ketika semua kota telah dikunjungi)
if (index < cities.length - 1) {
// untuk setiap kota
// array probabilitias untuk roulete wheel
let probability_pairs = [];
cities.forEach((city) => {
if (!ant.hasVisited(city)) {
// jika kotanya belum dikunjungi, hitung probabilitas
probability_pairs.push({ c: city, p: getProbability(ant.currentCity(), city) });
}
});
// ambil 1 dari array setelah dirandom
let next_city = rouletteWheel(probability_pairs);
// set kota selantjutnya
ant.goToCity(next_city);
} else {
// jika kota terakhir (ketika semua kota telak dikunjungi)
// pergi ke rumah
ant.goToCity(ant.starting);
}
});
});
//update feromon
updatePheromone();
}
//mendapatkan semut terbaik
function getBestAnt() {
// sorting semut dari cost terkecil
ants.sort((a, b) => {
if (a.cost < b.cost) {
return -1;
}
if (a.cost > b.cost) {
return 1;
}
return 0;
});
// return semut cost terkecil
return ants[0];
}
// menjalankan fungsi keseluruhan
function runACO() {
initializeACO();
for (let i = 0; i < ITERATION_VAR; i++) {
iterateACO();
}
return getBestAnt();
}