Pengoptimuman Koloni Semut dengan Contoh Java

1. Pengenalan

Tujuan siri ini adalah untuk menerangkan idea algoritma genetik dan menunjukkan pelaksanaan yang paling terkenal .

Dalam tutorial ini, kita akan menerangkan konsep pengoptimuman koloni semut (ACO), diikuti dengan contoh kod.

2. Bagaimana ACO Berfungsi

ACO adalah algoritma genetik yang diilhamkan oleh tingkah laku semut semula jadi. Untuk memahami sepenuhnya algoritma ACO, kita perlu membiasakan konsep asasnya:

  • semut menggunakan feromon untuk mencari jalan terpendek antara rumah dan sumber makanan
  • feromon menguap dengan cepat
  • semut lebih suka menggunakan jalan yang lebih pendek dengan feromon yang lebih padat

Mari tunjukkan contoh ringkas ACO yang digunakan dalam Masalah Penjual Perjalanan. Dalam kes berikut, kita perlu mencari jalan terpendek antara semua nod dalam grafik:

Disusuli dengan tingkah laku semula jadi, semut akan mula meneroka jalan baru semasa penerokaan. Warna biru yang lebih kuat menunjukkan jalur yang digunakan lebih kerap daripada yang lain, sedangkan warna hijau menunjukkan jalan terpendek semasa yang dijumpai:

Hasilnya, kami akan mencapai jalan terpendek antara semua nod:

Alat berasaskan GUI yang bagus untuk ujian ACO boleh didapati di sini.

3. Pelaksanaan Java

3.1. Parameter ACO

Mari kita bincangkan parameter utama untuk algoritma ACO, yang dinyatakan dalam kelas AntColonyOptimization :

private double c = 1.0; private double alpha = 1; private double beta = 5; private double evaporation = 0.5; private double Q = 500; private double antFactor = 0.8; private double randomFactor = 0.01;

Parameter c menunjukkan bilangan lintasan asal, pada permulaan simulasi. Tambahan pula, alpha mengawal kepentingan feromon, sementara beta mengawal keutamaan jarak. Secara amnya, parameter beta harus lebih besar daripada alpha untuk hasil terbaik.

Seterusnya, pemboleh ubah penyejatan menunjukkan peratus berapa feromon menguap dalam setiap lelaran, sedangkan Q memberikan maklumat mengenai jumlah feromon yang tersisa di jejak oleh setiap Ant , dan antFactor memberitahu kita berapa semut yang akan kita gunakan setiap bandar.

Akhirnya, kita perlu mempunyai sedikit kekacauan dalam simulasi kita, dan ini diliputi oleh randomFactor .

3.2. Buat Semut

Setiap Semut dapat mengunjungi kota tertentu, mengingat semua bandar yang dikunjungi, dan mengawasi panjang jalan:

public void visitCity(int currentIndex, int city) { trail[currentIndex + 1] = city; visited[city] = true; } public boolean visited(int i) { return visited[i]; } public double trailLength(double graph[][]) { double length = graph[trail[trailSize - 1]][trail[0]]; for (int i = 0; i < trailSize - 1; i++) { length += graph[trail[i]][trail[i + 1]]; } return length; } 

3.3. Menyiapkan Semut

Pada awalnya, kita perlu menginisialisasi pelaksanaan kod ACO kita dengan menyediakan matriks jejak dan semut:

graph = generateRandomMatrix(noOfCities); numberOfCities = graph.length; numberOfAnts = (int) (numberOfCities * antFactor); trails = new double[numberOfCities][numberOfCities]; probabilities = new double[numberOfCities]; ants = new Ant[numberOfAnts]; IntStream.range(0, numberOfAnts).forEach(i -> ants.add(new Ant(numberOfCities)));

Seterusnya, kita perlu menyediakan matriks semut untuk memulakan dengan bandar rawak:

public void setupAnts() { IntStream.range(0, numberOfAnts) .forEach(i -> { ants.forEach(ant -> { ant.clear(); ant.visitCity(-1, random.nextInt(numberOfCities)); }); }); currentIndex = 0; }

Untuk setiap lelaran gelung, kami akan melakukan operasi berikut:

IntStream.range(0, maxIterations).forEach(i -> { moveAnts(); updateTrails(); updateBest(); });

3.4. Pindahkan Semut

Mari kita mulakan dengan kaedah moveAnts () . Kita perlu memilih kota berikutnya untuk semua semut, ingat bahawa setiap semut berusaha mengikuti jejak semut lain:

public void moveAnts() { IntStream.range(currentIndex, numberOfCities - 1).forEach(i -> { ants.forEach(ant -> { ant.visitCity(currentIndex, selectNextCity(ant)); }); currentIndex++; }); }

Bahagian yang paling penting adalah memilih bandar seterusnya yang mesti dikunjungi. Kita harus memilih bandar seterusnya berdasarkan logik kebarangkalian. Pertama, kita dapat memeriksa sama ada Semut mesti mengunjungi bandar rawak:

int t = random.nextInt(numberOfCities - currentIndex); if (random.nextDouble()  i == t && !ant.visited(i)) .findFirst(); if (cityIndex.isPresent()) { return cityIndex.getAsInt(); } }

Sekiranya kita tidak memilih mana-mana bandar yang rawak, kita perlu mengira kebarangkalian untuk memilih bandar berikutnya, mengingat semut lebih suka mengikuti jalan yang lebih kuat dan lebih pendek. Kami dapat melakukan ini dengan menyimpan kemungkinan berpindah ke setiap bandar dalam array:

public void calculateProbabilities(Ant ant) { int i = ant.trail[currentIndex]; double pheromone = 0.0; for (int l = 0; l < numberOfCities; l++) { if (!ant.visited(l)){ pheromone += Math.pow(trails[i][l], alpha) * Math.pow(1.0 / graph[i][l], beta); } } for (int j = 0; j < numberOfCities; j++) { if (ant.visited(j)) { probabilities[j] = 0.0; } else { double numerator = Math.pow(trails[i][j], alpha) * Math.pow(1.0 / graph[i][j], beta); probabilities[j] = numerator / pheromone; } } } 

Setelah kami mengira kebarangkalian, kami dapat memutuskan ke bandar mana yang akan dikunjungi dengan menggunakan:

double r = random.nextDouble(); double total = 0; for (int i = 0; i = r) { return i; } }

3.5. Kemas kini Laluan

Dalam langkah ini, kita harus mengemas kini jejak dan feromon kiri:

public void updateTrails() { for (int i = 0; i < numberOfCities; i++) { for (int j = 0; j < numberOfCities; j++) { trails[i][j] *= evaporation; } } for (Ant a : ants) { double contribution = Q / a.trailLength(graph); for (int i = 0; i < numberOfCities - 1; i++) { trails[a.trail[i]][a.trail[i + 1]] += contribution; } trails[a.trail[numberOfCities - 1]][a.trail[0]] += contribution; } }

3.6. Update the Best Solution

This is the last step of each iteration. We need to update the best solution in order to keep the reference to it:

private void updateBest() { if (bestTourOrder == null) { bestTourOrder = ants[0].trail; bestTourLength = ants[0].trailLength(graph); } for (Ant a : ants) { if (a.trailLength(graph) < bestTourLength) { bestTourLength = a.trailLength(graph); bestTourOrder = a.trail.clone(); } } }

After all iterations, the final result will indicate the best path found by ACO. Please note that by increasing the number of cities, the probability of finding the shortest path decreases.

4. Conclusion

This tutorial introduces the Ant Colony Optimization algorithm. You can learn about genetic algorithms without any previous knowledge of this area, having only basic computer programming skills.

The complete source code for the code snippets in this tutorial is available in the GitHub project.

For all articles in the series, including other examples of genetic algorithms, check out the following links:

  • Cara Merangka Algoritma Genetik di Jawa
  • Masalah Penjual Perjalanan di Jawa
  • Pengoptimuman Koloni Semut (ini)