Contoh Algoritma Mendaki Bukit di Jawa

1. Gambaran keseluruhan

Dalam tutorial ini, kami akan menunjukkan algoritma Hill-Climbing dan pelaksanaannya. Kami juga akan melihat kebaikan dan kekurangannya. Sebelum langsung masuk ke dalamnya, mari kita bincangkan pendekatan algoritma menjana dan menguji secara ringkas.

2. Menghasilkan Algoritma Uji Dan

Ini adalah teknik yang sangat mudah yang membolehkan kita membuat algoritma mencari penyelesaian:

  1. Tentukan keadaan semasa sebagai keadaan awal
  2. Gunakan kemungkinan operasi pada keadaan semasa dan hasilkan penyelesaian yang mungkin
  3. Bandingkan penyelesaian yang baru dihasilkan dengan keadaan matlamat
  4. Sekiranya matlamat itu tercapai atau tidak ada keadaan baru yang dapat dibuat, berhenti. Jika tidak, kembali ke langkah 2

Ia berfungsi dengan baik dengan masalah sederhana. Oleh kerana itu adalah pencarian yang menyeluruh, tidak layak untuk mempertimbangkannya ketika berhadapan dengan ruang masalah yang besar. Ia juga dikenali sebagai algoritma Muzium Britain (cuba mencari artifak di Muzium British dengan menerokainya secara rawak).

Ini juga merupakan idea utama di sebalik Hill-Climbing Attack dalam dunia biometrik. Pendekatan ini boleh digunakan untuk menghasilkan data biometrik sintetik.

3. Pengenalan Algoritma Pendakian Bukit yang Sederhana

Dalam teknik Hill-Climbing, bermula di dasar bukit, kami berjalan ke atas hingga sampai di puncak bukit. Dengan kata lain, kita mulai dengan keadaan awal dan kita terus memperbaiki penyelesaiannya sehingga optimum.

Ini adalah variasi algoritma menghasilkan-dan-ujian yang membuang semua keadaan yang tidak kelihatan menjanjikan atau nampaknya tidak mungkin membawa kita ke keadaan tujuan. Untuk mengambil keputusan seperti itu, ia menggunakan heuristik (fungsi penilaian) yang menunjukkan seberapa dekat keadaan semasa dengan keadaan tujuan.

Dengan kata mudah, Mendaki Bukit = menghasilkan-dan-menguji + heuristik

Mari lihat algoritma pendakian Bukit Sederhana:

  1. Tentukan keadaan semasa sebagai keadaan awal
  2. Gelung sehingga keadaan matlamat tercapai atau tidak ada lagi pengendali yang dapat digunakan pada keadaan semasa:
    1. Terapkan operasi ke keadaan semasa dan dapatkan keadaan baru
    2. Bandingkan keadaan baru dengan matlamat
    3. Berhenti jika keadaan matlamat tercapai
    4. Nilai keadaan baru dengan fungsi heuristik dan bandingkan dengan keadaan semasa
    5. Sekiranya keadaan baru lebih dekat dengan matlamat berbanding keadaan semasa, kemas kini keadaan semasa

Seperti yang kita lihat, ia mencapai keadaan sasaran dengan peningkatan berulang. Dalam algoritma Hill-Climbing, mencari matlamat sama dengan mencapai puncak bukit.

4. Contoh

Algoritma Mendaki Bukit dapat dikategorikan sebagai carian yang tepat. Oleh itu, kita dapat melaksanakan sebarang carian berdasarkan masalah atau masalah seperti masalah n-queens menggunakannya. Untuk memahami konsep dengan mudah, kita akan mengambil contoh yang sangat mudah.

Mari lihat gambar di bawah:

Perkara utama semasa menyelesaikan masalah pendakian bukit adalah memilih fungsi heuristik yang sesuai.

Mari kita tentukan fungsi seperti itu :

h (x) = +1 untuk semua blok dalam struktur sokongan jika blok tersebut diletakkan dengan betul sebaliknya -1 untuk semua blok dalam struktur sokongan.

Di sini, kita akan memanggil mana-mana blok yang diletakkan dengan betul jika mempunyai struktur sokongan yang sama dengan keadaan tujuan. Mengikut prosedur pendakian bukit yang telah dibincangkan sebelumnya, mari kita lihat semua lelaran dan heuristiknya untuk mencapai keadaan sasaran:

5. Pelaksanaan

Sekarang, mari kita laksanakan contoh yang sama menggunakan algoritma Hill-Climbing.

Pertama sekali, kita memerlukan kelas Negeri yang akan menyimpan senarai timbunan yang mewakili kedudukan blok di setiap negeri. Ia juga akan menyimpan heuristik untuk keadaan tertentu:

public class State { private List
    
      state; private int heuristics; // copy constructor, setters, and getters }
    

Kita juga memerlukan kaedah yang akan menghitung nilai heuristik negara.

public int getHeuristicsValue( List
    
      currentState, Stack goalStateStack) { Integer heuristicValue; heuristicValue = currentState.stream() .mapToInt(stack -> { return getHeuristicsValueForStack( stack, currentState, goalStateStack); }).sum(); return heuristicValue; } public int getHeuristicsValueForStack( Stack stack, List
     
       currentState, Stack goalStateStack) { int stackHeuristics = 0; boolean isPositioneCorrect = true; int goalStartIndex = 0; for (String currentBlock : stack) { if (isPositioneCorrect && currentBlock.equals(goalStateStack.get(goalStartIndex))) { stackHeuristics += goalStartIndex; } else { stackHeuristics -= goalStartIndex; isPositioneCorrect = false; } goalStartIndex++; } return stackHeuristics; } 
     
    

Tambahan pula, kita perlu menentukan kaedah pengendali yang akan menjadikan kita keadaan baru. Sebagai contoh, kami akan menentukan dua kaedah berikut:

  1. Masukkan blok dari tumpukan dan tolak ke timbunan baru
  2. Masukkan blok dari timbunan dan tolak ke salah satu timbunan yang lain
private State pushElementToNewStack( List
    
      currentStackList, String block, int currentStateHeuristics, Stack goalStateStack) { State newState = null; Stack newStack = new Stack(); newStack.push(block); currentStackList.add(newStack); int newStateHeuristics = getHeuristicsValue(currentStackList, goalStateStack); if (newStateHeuristics > currentStateHeuristics) { newState = new State(currentStackList, newStateHeuristics); } else { currentStackList.remove(newStack); } return newState; }
    
private State pushElementToExistingStacks( Stack currentStack, List
    
      currentStackList, String block, int currentStateHeuristics, Stack goalStateStack) { return currentStackList.stream() .filter(stack -> stack != currentStack) .map(stack -> { return pushElementToStack( stack, block, currentStackList, currentStateHeuristics, goalStateStack); }) .filter(Objects::nonNull) .findFirst() .orElse(null); } private State pushElementToStack( Stack stack, String block, List
     
       currentStackList, int currentStateHeuristics, Stack goalStateStack) { stack.push(block); int newStateHeuristics = getHeuristicsValue(currentStackList, goalStateStack); if (newStateHeuristics > currentStateHeuristics) { return new State(currentStackList, newStateHeuristics); } stack.pop(); return null; }
     
    

Sekarang kita mempunyai kaedah penolong, mari tulis kaedah untuk menerapkan teknik mendaki bukit.

Di sini, kami terus mengira keadaan baru yang lebih dekat dengan tujuan daripada pendahulunya. Kami terus menambahnya sehingga kami berjaya mencapai matlamat.

Sekiranya kita tidak menemui keadaan baru, algoritma akan berakhir dengan mesej ralat:

public List getRouteWithHillClimbing( Stack initStateStack, Stack goalStateStack) throws Exception { // instantiate initState with initStateStack // ... List resultPath = new ArrayList(); resultPath.add(new State(initState)); State currentState = initState; boolean noStateFound = false; while ( !currentState.getState().get(0).equals(goalStateStack) || noStateFound) { noStateFound = true; State nextState = findNextState(currentState, goalStateStack); if (nextState != null) { noStateFound = false; currentState = nextState; resultPath.add(new State(nextState)); } } return resultPath; }

Sebagai tambahan kepada ini, kami juga memerlukan kaedah findNextState yang menerapkan semua kemungkinan operasi pada keadaan semasa untuk mendapatkan keadaan seterusnya:

public State findNextState(State currentState, Stack goalStateStack) { List
    
      listOfStacks = currentState.getState(); int currentStateHeuristics = currentState.getHeuristics(); return listOfStacks.stream() .map(stack -> { return applyOperationsOnState( listOfStacks, stack, currentStateHeuristics, goalStateStack); }) .filter(Objects::nonNull) .findFirst() .orElse(null); } public State applyOperationsOnState( List
     
       listOfStacks, Stack stack, int currentStateHeuristics, Stack goalStateStack) { State tempState; List
      
        tempStackList = new ArrayList(listOfStacks); String block = stack.pop(); if (stack.size() == 0) tempStackList.remove(stack); tempState = pushElementToNewStack( tempStackList, block, currentStateHeuristics, goalStateStack); if (tempState == null) { tempState = pushElementToExistingStacks( stack, tempStackList, block, currentStateHeuristics, goalStateStack); stack.push(block); } return tempState; }
      
     
    

6. Algoritma Pendakian Mendaki Bukit

Steepest-Ascent Hill-Climbing algorithm (gradient search) is a variant of Hill Climbing algorithm. We can implement it with slight modifications in our simple algorithm. In this algorithm, we consider all possible states from the current state and then pick the best one as successor, unlike in the simple hill climbing technique.

In other words, in the case of hill climbing technique we picked any state as a successor which was closer to the goal than the current state whereas, in Steepest-Ascent Hill Climbing algorithm, we choose the best successor among all possible successors and then update the current state.

7. Disadvantages

Hill Climbing is a short sighted technique as it evaluates only immediate possibilities. So it may end up in few situations from which it can not pick any further states. Let's look at these states and some solutions for them:

  1. Local maximum: It's a state which is better than all neighbors, but there exists a better state which is far from the current state; if local maximum occurs within sight of the solution, it is known as “foothills”
  2. Plateau: In this state, all neighboring states have same heuristic values, so it's unclear to choose the next state by making local comparisons
  3. Ridge: It's an area which is higher than surrounding states, but it can not be reached in a single move; for example, we have four possible directions to explore (N, E, W, S) and an area exists in NE direction

There are few solutions to overcome these situations:

  1. We can backtrack to one of the previous states and explore other directions
  2. We can skip few states and make a jump in new directions
  3. We can explore several directions to figure out the correct path

8. Conclusion

Even though hill climbing technique is much better than exhaustive search, it's still not optimal in large problem spaces.

Kita selalu dapat menyandikan maklumat global ke dalam fungsi heuristik untuk membuat keputusan yang lebih bijak, tetapi kemudian kerumitan komputasi akan jauh lebih tinggi daripada sebelumnya.

Algoritma pendakian bukit boleh sangat bermanfaat apabila digabungkan dengan teknik lain. Seperti biasa, kod lengkap untuk semua contoh boleh didapati di GitHub.