Penjelasan Rekursif pada Tower of Hanoi

 


Apa itu Rekursif ?

    Singkatnya, rekursif adalah sebutan suatu proses dimana suatu fungsi memanggil dirinya sendiri. Rekursif biasanya digunakan untuk memecah masalah menjadi masalah yang lebih simpel. Untuk penjelasan lebih lengkap mengenai fungsi rekursif dapat dilihat pada link berikut.

Implementasi Rekursif pada Tower of Hanoi

    Untuk penyelesaian masalah secara rekursif, dapat dipecah menjadi tiga langkah, yaitu :
  1. Pastikan f(1) dapat diselesaikan, dengan f(1) merupakan kasus dasar (Base Case)
  2. Pastikan bahwa f(n-1) dapat diselesaikan
  3. Buat hubungan f(n) dengan f(1) menggunakan fungsi f(n-1)
    1. Memastikan f(1) dapat diselesaikan
    Contohnya pada Tower of Hanoi, kasus f(1) merupakan kasus dimana tower 1 hanya memiliki 1 disk. Kasus ini dapat diselesaikan dengan memindahkan disk pada tower 1 ke tower 3. 

    2. Pastikan f(n-1) dapat diselesaikan
    Kita dapat anggap bahwa penyelesaian kasus n-1 dari banyak disk n, dapat diselesaikan dengan memindahkan sebanyak n-1 disk dari tower 1 menuju tower 3, dan berlaku seterusnya.

    3. Buat hubungan f(n) dengan f(1) dengan menggunakan fungsi f(n-1)
    Jika kita lihat dari langkah kedua, jika sebanyak n-1 disk dipindah terus menerus dari tower 1 ke 3, akan terjadi dimana tower 1 hanya memiliki 1 disk dan kita tau bahwa kasus ini merupakan kasus dasar.

    Setelah menelaah ketiga langkah rekursif diatas, kita mendapatkan penyelesaian pada masalah Tower of Hanoi, seperti pada source code di bawah.

public void moveDisks(int n, DiskStack start, DiskStack end, DiskStack other, int a, int c, int b) {
if(n == 0) {
return;
}
moveDisks(n-1, start, other, end, a, b, c);
end.push(start.pop());
moveDisks(n-1, other, end, start, b, c, a);
}
view raw moveDisk.java hosted with ❤ by GitHub
    
    Dengan method ini penyelesaian pada masalah Tower of Hanoi dapat ditemukan. Agar lebih jelas dalam memahaminya, terdapat source code di bawah sebagai GUI agar mendapatkan gambarannya.

/**
* Home Frame for Tower of Hanoi Application
*
* @author Anggito Anju
* @version 1.0 - 05 June 2021
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class HomeFrame implements ActionListener
{
/* ===== PRIVATE VARIABLE FOR HOME FRAME ===== */
// Frame
public JFrame homeFrame = new JFrame("Tower of Hanoi");
// Home Panel
private JPanel homePanel = new JPanel();
// Labels
private JLabel versionHomePanel = new JLabel("VER 1.0");
private JLabel titleHomePanel = new JLabel("Tower of Hanoi");
private JLabel smallTitleHomePanel = new JLabel("Art of Recursion");
// Buttons
private JButton playButton = new JButton("PLAY");
// Author Panel
private JPanel authorPanel = new JPanel();
// Labels
private JLabel authorCredit = new JLabel("Designed and Coded by Anggito Anju");
/* ===== PRIMARY VARIABLE ===== */
public static int diskAmount;
/* ===== UTILITY ===== */
private Font f1 = new Font(Font.SANS_SERIF, Font.BOLD, 30);
private Font f2 = new Font(Font.SERIF, Font.BOLD, 16);
private Font f3 = new Font(Font.SERIF, Font.BOLD, 18);
private Font f4 = new Font(Font.SERIF, Font.BOLD, 14);
private Font f5 = new Font(Font.SERIF, Font.PLAIN, 13);
private Color c1 = new Color(42, 107, 244);
private Color c2 = new Color(104, 164, 244);
private Color c3 = new Color(5, 29, 61);
// Constructur for Home Frame
public HomeFrame() {
initComponents();
}
// Components Method
private void initComponents() {
/* ========== CONTAINER SETTINGS ========== */
homeFrame.setSize(480, 600);
homeFrame.setLocationRelativeTo(null);
homeFrame.setLayout(new BorderLayout());
homeFrame.setResizable(false);
homeFrame.setVisible(true);
homeFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
/* ===== ADD PANELS ===== */
homeFrame.add(homePanel, BorderLayout.PAGE_START);
homeFrame.add(authorPanel, BorderLayout.PAGE_END);
/* ========== END CONTAINER SETTINGS ========== */
/* ========== COMPONENTS SETTINGS ========== */
/* ===== HOME PANEL ===== */
homePanel.setBackground(c1);
homePanel.setPreferredSize(new Dimension(480, 520));
homePanel.setLayout(null);
versionHomePanel.setBounds(398, 10, 68, 22);
versionHomePanel.setFont(f4);
versionHomePanel.setForeground(Color.BLACK);
titleHomePanel.setBounds(125, 200, 225, 30);
titleHomePanel.setFont(f1);
titleHomePanel.setForeground(Color.WHITE);
smallTitleHomePanel.setBounds(168, 240, 140, 24);
smallTitleHomePanel.setFont(f3);
smallTitleHomePanel.setForeground(Color.BLACK);
playButton.setBounds(185, 300, 100, 30);
playButton.setBackground(c2);
playButton.setFont(f2);
playButton.setForeground(c3);
// PLAY BUTTON EVENT
playButton.addActionListener(this);
homePanel.add(versionHomePanel);
homePanel.add(titleHomePanel);
homePanel.add(smallTitleHomePanel);
homePanel.add(playButton);
/* ===== END HOME PANEL ===== */
/* ===== AUTHOR PANEL ===== */
authorPanel.setBackground(c2);
authorPanel.setPreferredSize(new Dimension(480, 45));
authorPanel.setLayout(null);
authorCredit.setBounds(250, 12, 200, 20);
authorCredit.setFont(f5);
authorCredit.setForeground(Color.BLACK);
authorPanel.add(authorCredit);
/* ===== END AUTHOR PANEL ===== */
}
public static void diskInput(int num) {
if(num <= 5 && diskAmount > 0) {
for(int i = 0; i < HomeFrame.diskAmount; i++) {
PlayFrame.towerPane.tower1.push(new Disk(55-(8*i)));
}
new PlayFrame();
}
else {
JOptionPane.showMessageDialog(null, "Must be integer 1-5", "Wrong Input", JOptionPane.ERROR_MESSAGE);
new HomeFrame();
}
}
public static void main(String[] args) {
new HomeFrame();
}
@Override
public void actionPerformed(ActionEvent e) {
homeFrame.dispose();
String inputUser = JOptionPane.showInputDialog("Masukkan banyak disk (1-5)");
diskAmount = Integer.parseInt(inputUser);
diskInput(diskAmount);
}
}
/**
* Play Frame for Tower of Hanoi Application
*
* @author Anggito Anju
* @version 1.0 - 05 June 2021
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PlayFrame implements ActionListener, Runnable
{
/* ===== PRIMARY USER VARIABLE ===== */
public static int movesCounter = 1;
/* ===== PRIVATE VARIABLE FOR PLAY FRAME ===== */
// Creating frame
public static JFrame playFrame = new JFrame("Tower of Hanoi");
// Upper Panel
private JPanel upperPanel = new JPanel();
// Buttons
private JButton animateButton = new JButton("Animate");
private JButton resetButton = new JButton("Reset");
// Play Panel
private JPanel playPanel = new JPanel();
// Inside Panel
static towerPanel towerPane = new towerPanel();
// Buttons
private JButton inputButton = new JButton("INPUT DISK");
// Labels
private JLabel playPanelTitle = new JLabel("Tower of Hanoi");
// Graphics
Graphics g;
// Answer Panel
private JPanel ansPanel = new JPanel();
// Labels
public static JLabel ansCounter = new JLabel("Moves : 0");
// Text Fields
public static JTextArea ansCounterField = new JTextArea();
// Buttons
private JButton backToHomeButton = new JButton("Home");
/* ===== END PRIVATE VARIABLE FOR PLAY FRAME ===== */
/* ===== UTILITY ===== */
private Font f1 = new Font(Font.SANS_SERIF, Font.BOLD, 18);
private Font f2 = new Font(Font.SERIF, Font.BOLD, 16);
private Font f3 = new Font(Font.SERIF, Font.BOLD, 14);
private Color c1 = new Color(104, 164, 244);
private Color c2 = new Color(5, 29, 61);
private Color c3 = new Color(42, 107, 244);
// Constructur for Play Frame
public PlayFrame() {
initComponents();
}
private void initComponents() {
/* ========== CONTAINER SETTINGS ========== */
playFrame.setSize(480, 600);
playFrame.setLocationRelativeTo(null);
playFrame.setLayout(null);
playFrame.setResizable(false);
playFrame.setVisible(true);
playFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
/* ===== ADD PANELS ===== */
playFrame.add(upperPanel);
playFrame.add(playPanel);
playFrame.add(ansPanel);
/* ========== END CONTAINER SETTINGS ========== */
/* ========== COMPONENTS SETTINGS ========== */
/* ===== UPPER PANEL ===== */
upperPanel.setBounds(0, 0, 480, 120);
upperPanel.setBackground(c2);
upperPanel.setLayout(null);
animateButton.setBounds(100, 50, 100, 30);
animateButton.setFont(f2);
animateButton.setForeground(c2);
animateButton.setBackground(c1);
// Button Event
animateButton.addActionListener(this);
animateButton.setActionCommand("ANIMATE");
resetButton.setBounds(280, 50, 100, 30);
resetButton.setFont(f2);
resetButton.setForeground(c2);
resetButton.setBackground(c1);
// Button Event
resetButton.addActionListener(this);
resetButton.setActionCommand("RESET");
upperPanel.add(animateButton);
upperPanel.add(resetButton);
/* ===== END UPPER PANEL ===== */
/* ===== PLAY PANEL ===== */
playPanel.setBounds(0, 120, 320, 480);
playPanel.setBackground(c1);
playPanel.setLayout(null);
inputButton.setBounds(90, 20, 140, 30);
inputButton.setFont(f2);
inputButton.setForeground(c2);
inputButton.setBackground(c3);
// Button Event
inputButton.addActionListener(this);
inputButton.setActionCommand("INPUT");
playPanelTitle.setBounds(90, 60, 140, 30);
playPanelTitle.setFont(f1);
playPanelTitle.setForeground(Color.WHITE);
towerPane.setBounds(20, 100, 280, 280);
towerPane.setBackground(c1);
playPanel.add(playPanelTitle);
playPanel.add(inputButton);
playPanel.add(towerPane);
/* ===== END PLAY PANEL ===== */
/* ===== ANS PANEL ===== */
ansPanel.setBounds(320, 120, 160, 480);
ansPanel.setBackground(c3);
ansPanel.setLayout(null);
ansCounter.setBounds(40, 20, 80, 30);
ansCounter.setFont(f3);
ansCounter.setForeground(c2);
ansCounterField.setBounds(10, 60, 125, 320);
backToHomeButton.setBounds(10, 400, 125, 30);
// Button Event
backToHomeButton.addActionListener(this);
backToHomeButton.setActionCommand("Home");
ansPanel.add(ansCounter);
ansPanel.add(ansCounterField);
ansPanel.add(backToHomeButton);
/* ===== END ANS PANEL ===== */
}
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if(command == "Home") {
playFrame.dispose();
while(towerPane.tower1.count() > 0) {
towerPane.tower1.pop();
//System.out.println(towerPanel.tower1.count());
}
while(towerPane.tower2.count() > 0) {
towerPane.tower2.pop();
//System.out.println(towerPanel.tower2.count());
}
while(towerPane.tower3.count() > 0) {
towerPane.tower3.pop();
//System.out.println(towerPanel.tower3.count());
}
ansCounterField.setText(null);
movesCounter = 1;
new HomeFrame();
}
else if(command == "RESET") {
while(towerPane.tower1.count() > 0) {
towerPane.tower1.pop();
//System.out.println(towerPanel.tower1.count());
}
while(towerPane.tower2.count() > 0) {
towerPane.tower2.pop();
//System.out.println(towerPanel.tower2.count());
}
while(towerPane.tower3.count() > 0) {
towerPane.tower3.pop();
//System.out.println(towerPanel.tower3.count());
}
ansCounterField.setText(null);
movesCounter = 1;
for(int i = 0; i < HomeFrame.diskAmount; i++) {
PlayFrame.towerPane.tower1.push(new Disk(55-(8*i)));
}
towerPane.repaint();
}
else if(command == "INPUT") {
while(towerPane.tower1.count() > 0) {
towerPane.tower1.pop();
//System.out.println(towerPanel.tower1.count());
}
while(towerPane.tower2.count() > 0) {
towerPane.tower2.pop();
//System.out.println(towerPanel.tower2.count());
}
while(towerPane.tower3.count() > 0) {
towerPane.tower3.pop();
//System.out.println(towerPanel.tower3.count());
}
ansCounterField.setText("");
movesCounter = 1;
while(true) {
String inputUser = JOptionPane.showInputDialog("Masukkan banyak disk (1-5)");
HomeFrame.diskAmount = Integer.parseInt(inputUser);
if(HomeFrame.diskAmount > 0 && HomeFrame.diskAmount <= 5) {
for(int i = 0; i < HomeFrame.diskAmount; i++) {
PlayFrame.towerPane.tower1.push(new Disk(55-(8*i)));
}
towerPane.repaint();
break;
}
else {
JOptionPane.showMessageDialog(null, "Must be integer 1-5", "Wrong Input", JOptionPane.ERROR_MESSAGE);
}
}
}
else if(command == "ANIMATE") {
Thread t1 = new Thread(this);
t1.start();
}
}
@Override
public void run() {
towerPane.moveDisks(HomeFrame.diskAmount, towerPane.tower1, towerPane.tower3, towerPane.tower2, 1, 3, 2);
try { Thread.sleep(750); } catch(Exception e) {}
}
}
import javax.swing.JPanel;
/**
* Tower of Hanoi Panel
*
* @author Anggito Anju
* @version 1.1 - 07 June 21
*/
import java.awt.*;
public class towerPanel extends JPanel
{
/* ===== UTILITY ===== */
private static Color c1 = new Color(193, 50, 0);
private static Color c2 = new Color(245, 121, 0);
private static Color c3 = new Color(42, 107, 244);
// Stacks
public DiskStack tower1 = new DiskStack();
public DiskStack tower2 = new DiskStack();
public DiskStack tower3 = new DiskStack();
// Constructur to Tower Panel
towerPanel() {
}
// PRIMARY VARIABLE
static final int diskWidth = 55;
static final int diskHeight = 15;
// Draw shapes
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g;
// Poles
g2D.setPaint(c2);
g2D.fillRect(54, 52, 20, 200);
g2D.fillRect(130, 52, 20, 200);
g2D.fillRect(206, 52, 20, 200);
// Tables
g2D.setPaint(c1);
g2D.fillRect(0, 230, 280, 50);
Disk current = tower1.getHead();
int i = 0;
while(current != null) {
g2D.setPaint(c3);
switch (current.getLength()) {
case 55 :
g2D.fillRect(37+(4*0), 215-(diskHeight*i), current.getLength(), 15);
break;
case 47 :
g2D.fillRect(37+(4*1), 215-(diskHeight*i), current.getLength(), 15);
break;
case 39 :
g2D.fillRect(37+(4*2), 215-(diskHeight*i), current.getLength(), 15);
break;
case 31 :
g2D.fillRect(37+(4*3), 215-(diskHeight*i), current.getLength(), 15);
break;
case 23 :
g2D.fillRect(37+(4*4), 215-(diskHeight*i), current.getLength(), 15);
break;
}
g2D.setPaint(Color.BLACK);
switch (current.getLength()) {
case 55 :
g2D.drawRect(37+(4*0), 215-(diskHeight*i), current.getLength(), 15);
break;
case 47 :
g2D.drawRect(37+(4*1), 215-(diskHeight*i), current.getLength(), 15);
break;
case 39 :
g2D.drawRect(37+(4*2), 215-(diskHeight*i), current.getLength(), 15);
break;
case 31 :
g2D.drawRect(37+(4*3), 215-(diskHeight*i), current.getLength(), 15);
break;
case 23 :
g2D.drawRect(37+(4*4), 215-(diskHeight*i), current.getLength(), 15);
break;
}
i++;
current = current.getNext();
}
current = tower2.getHead();
i = 0;
while(current != null) {
g2D.setPaint(c3);
switch (current.getLength()) {
case 55 :
g2D.fillRect(113+(4*0), 215-(diskHeight*i), current.getLength(), 15);
break;
case 47 :
g2D.fillRect(113+(4*1), 215-(diskHeight*i), current.getLength(), 15);
break;
case 39 :
g2D.fillRect(113+(4*2), 215-(diskHeight*i), current.getLength(), 15);
break;
case 31 :
g2D.fillRect(113+(4*3), 215-(diskHeight*i), current.getLength(), 15);
break;
case 23 :
g2D.fillRect(113+(4*4), 215-(diskHeight*i), current.getLength(), 15);
break;
}
g2D.setPaint(Color.BLACK);
switch (current.getLength()) {
case 55 :
g2D.drawRect(113+(4*0), 215-(diskHeight*i), current.getLength(), 15);
break;
case 47 :
g2D.drawRect(113+(4*1), 215-(diskHeight*i), current.getLength(), 15);
break;
case 39 :
g2D.drawRect(113+(4*2), 215-(diskHeight*i), current.getLength(), 15);
break;
case 31 :
g2D.drawRect(113+(4*3), 215-(diskHeight*i), current.getLength(), 15);
break;
case 23 :
g2D.drawRect(113+(4*4), 215-(diskHeight*i), current.getLength(), 15);
break;
}
i++;
current = current.getNext();
}
current = tower3.getHead();
i = 0;
while(current != null) {
g2D.setPaint(c3);
switch (current.getLength()) {
case 55 :
g2D.fillRect(189+(4*0), 215-(diskHeight*i), current.getLength(), 15);
break;
case 47 :
g2D.fillRect(189+(4*1), 215-(diskHeight*i), current.getLength(), 15);
break;
case 39 :
g2D.fillRect(189+(4*2), 215-(diskHeight*i), current.getLength(), 15);
break;
case 31 :
g2D.fillRect(189+(4*3), 215-(diskHeight*i), current.getLength(), 15);
break;
case 23 :
g2D.fillRect(189+(4*4), 215-(diskHeight*i), current.getLength(), 15);
break;
}
g2D.setPaint(Color.BLACK);
switch (current.getLength()) {
case 55 :
g2D.drawRect(189+(4*0), 215-(diskHeight*i), current.getLength(), 15);
break;
case 47 :
g2D.drawRect(189+(4*1), 215-(diskHeight*i), current.getLength(), 15);
break;
case 39 :
g2D.drawRect(189+(4*2), 215-(diskHeight*i), current.getLength(), 15);
break;
case 31 :
g2D.drawRect(189+(4*3), 215-(diskHeight*i), current.getLength(), 15);
break;
case 23 :
g2D.drawRect(189+(4*4), 215-(diskHeight*i), current.getLength(), 15);
break;
}
i++;
current = current.getNext();
}
}
public void moveDisks(int n, DiskStack start, DiskStack end, DiskStack other, int a, int c, int b) {
if(n == 0) {
return;
}
moveDisks(n-1, start, other, end, a, b, c);
end.push(start.pop());
PlayFrame.ansCounterField.append(a + " -> " + c + "\n");
PlayFrame.ansCounter.setText("Moves : " + PlayFrame.movesCounter++);
repaint();
try {
Thread.sleep(750);
}
catch(Exception e) {}
moveDisks(n-1, other, end, start, b, c, a);
}
}
/**
* Stack with methods for disks
*
* @author Anggito Anju
* @version ver 1.0 - 9 June 21
*/
public class DiskStack
{
private Disk headDisk;
public Disk getHead() {
return headDisk;
}
// Add Disk
public void push(Disk nextDisk) {
if(headDisk == null) {
headDisk = nextDisk;
}
else {
Disk currDisk = headDisk;
while(currDisk.getNext() != null) {
currDisk = currDisk.getNext();
}
currDisk.setNext(nextDisk);
}
}
public Disk pop() {
Disk popDisk = null;
if(count() == 1) {
popDisk = headDisk;
headDisk = null;
}
else if(count() > 1) {
Disk curr = headDisk;
while(curr.getNext().getNext() != null) {
curr = curr.getNext();
}
popDisk = curr.getNext();
curr.setNext(null);
}
return popDisk;
}
public int count() {
int counter = 0;
Disk curr = headDisk;
while(curr != null) {
counter++;
curr = curr.getNext();
}
return counter;
}
}
/**
* Disks Class
*
* @author Anggito Anju
* @version ver 1.0 - 9 June 21
* REFERENCES : https://www.youtube.com/watch?v=39ZbYEV8fU8
*/
public class Disk
{
private int length;
private Disk next;
public Disk(int length) {
super();
this.length = length;
this.next = null;
}
public int getLength() {
return length;
}
public Disk getNext() {
return next;
}
public void setLength(int diskLength) {
this.length = diskLength;
}
public void setNext(Disk nextDisk) {
this.next = nextDisk;
}
}

Di bawah ini merupakan video hasil keluaran setelah program di run




Semua source code di atas dapat diakses pada link github di bawah ini

Referensi :

Komentar

Postingan populer dari blog ini

Dokumentasi ETS PWEB 2022

Programming in Java : Mengubah Ekspresi Infix menjadi Postfix

Implementasi Stack di Java