Algoritmo de Warnsdorff para el problema del recorrido de Knight

Problema: Se coloca un caballo en el primer bloque de un tablero vacío y, moviéndose según las reglas del ajedrez, debe visitar cada casilla exactamente una vez. 

A continuación se muestra un ejemplo de la ruta seguida por Knight para cubrir todas las celdas. La siguiente cuadrícula representa un tablero de ajedrez con 8 x 8 celdas. Los números en las celdas indican el número de movimiento del Caballo. 
 

knight-tour-problem

Hemos discutido el algoritmo de retroceso para la solución del recorrido de Knight . En este post se discute  la heurística de Warnsdorff .
Regla de Warnsdorff: 

  1. Podemos partir de cualquier posición inicial del caballo en el tablero.
  2. Siempre nos movemos a una casilla adyacente no visitada con un grado mínimo (número mínimo de adyacentes no visitados).

Este algoritmo también puede aplicarse de manera más general a cualquier gráfico. 

Algunas definiciones:  

  • Se puede acceder a una posición Q desde una posición P si P puede moverse a Q con un solo movimiento de caballo y Q aún no ha sido visitada.
  • La accesibilidad de una posición P es el número de posiciones accesibles desde P.

Algoritmo:  

  1. Establecer P para que sea una posición inicial aleatoria en el tablero
  2. Marque el tablero en P con el número de movimiento «1»
  3. Haz lo siguiente para cada número de movimiento del 2 al número de casillas en el tablero: 
    • Sea S el conjunto de posiciones accesibles desde P.
    • Establezca P para que sea la posición en S con accesibilidad mínima
    • Marque el tablero en P con el número de movimiento actual
  4. Devuelva el tablero marcado: cada casilla se marcará con el número de movimiento en el que se visita.

A continuación se muestra la implementación del algoritmo anterior.  

C++

// C++ program to for Knight's tour problem using
// Warnsdorff's algorithm
#include <bits/stdc++.h>
#define N 8
 
// Move pattern on basis of the change of
// x coordinates and y coordinates respectively
static int cx[N] = {1,1,2,2,-1,-1,-2,-2};
static int cy[N] = {2,-2,1,-1,2,-2,1,-1};
 
// function restricts the knight to remain within
// the 8x8 chessboard
bool limits(int x, int y)
{
    return ((x >= 0 && y >= 0) && (x < N && y < N));
}
 
/* Checks whether a square is valid and empty or not */
bool isempty(int a[], int x, int y)
{
    return (limits(x, y)) && (a[y*N+x] < 0);
}
 
/* Returns the number of empty squares adjacent
   to (x, y) */
int getDegree(int a[], int x, int y)
{
    int count = 0;
    for (int i = 0; i < N; ++i)
        if (isempty(a, (x + cx[i]), (y + cy[i])))
            count++;
 
    return count;
}
 
// Picks next point using Warnsdorff's heuristic.
// Returns false if it is not possible to pick
// next point.
bool nextMove(int a[], int *x, int *y)
{
    int min_deg_idx = -1, c, min_deg = (N+1), nx, ny;
 
    // Try all N adjacent of (*x, *y) starting
    // from a random adjacent. Find the adjacent
    // with minimum degree.
    int start = rand()%N;
    for (int count = 0; count < N; ++count)
    {
        int i = (start + count)%N;
        nx = *x + cx[i];
        ny = *y + cy[i];
        if ((isempty(a, nx, ny)) &&
           (c = getDegree(a, nx, ny)) < min_deg)
        {
            min_deg_idx = i;
            min_deg = c;
        }
    }
 
    // IF we could not find a next cell
    if (min_deg_idx == -1)
        return false;
 
    // Store coordinates of next point
    nx = *x + cx[min_deg_idx];
    ny = *y + cy[min_deg_idx];
 
    // Mark next move
    a[ny*N + nx] = a[(*y)*N + (*x)]+1;
 
    // Update next point
    *x = nx;
    *y = ny;
 
    return true;
}
 
/* displays the chessboard with all the
  legal knight's moves */
void print(int a[])
{
    for (int i = 0; i < N; ++i)
    {
        for (int j = 0; j < N; ++j)
            printf("%d\t",a[j*N+i]);
        printf("\n");
    }
}
 
/* checks its neighbouring squares */
/* If the knight ends on a square that is one
   knight's move from the beginning square,
   then tour is closed */
bool neighbour(int x, int y, int xx, int yy)
{
    for (int i = 0; i < N; ++i)
        if (((x+cx[i]) == xx)&&((y + cy[i]) == yy))
            return true;
 
    return false;
}
 
/* Generates the legal moves using warnsdorff's
  heuristics. Returns false if not possible */
bool findClosedTour()
{
    // Filling up the chessboard matrix with -1's
    int a[N*N];
    for (int i = 0; i< N*N; ++i)
        a[i] = -1;
 
    // Random initial position
    int sx = rand()%N;
    int sy = rand()%N;
 
    // Current points are same as initial points
    int x = sx, y = sy;
    a[y*N+x] = 1; // Mark first move.
 
    // Keep picking next points using
    // Warnsdorff's heuristic
    for (int i = 0; i < N*N-1; ++i)
        if (nextMove(a, &x, &y) == 0)
            return false;
 
    // Check if tour is closed (Can end
    // at starting point)
    if (!neighbour(x, y, sx, sy))
        return false;
 
    print(a);
    return true;
}
 
// Driver code
int main()
{
    // To make sure that different random
    // initial positions are picked.
    srand(time(NULL));
 
    // While we don't get a solution
    while (!findClosedTour())
    {
    ;
    }
 
    return 0;
}

Java

// Java program to for Knight's tour problem using
// Warnsdorff's algorithm
import java.util.concurrent.ThreadLocalRandom;
 
class GFG
{
    public static final int N = 8;
 
    // Move pattern on basis of the change of
    // x coordinates and y coordinates respectively
    public static final int cx[] = {1, 1, 2, 2, -1, -1, -2, -2};
    public static final int cy[] = {2, -2, 1, -1, 2, -2, 1, -1};
 
    // function restricts the knight to remain within
    // the 8x8 chessboard
    boolean limits(int x, int y)
    {
        return ((x >= 0 && y >= 0) &&
                 (x < N && y < N));
    }
 
    /* Checks whether a square is valid and
    empty or not */
    boolean isempty(int a[], int x, int y)
    {
        return (limits(x, y)) && (a[y * N + x] < 0);
    }
 
    /* Returns the number of empty squares
    adjacent to (x, y) */
    int getDegree(int a[], int x, int y)
    {
        int count = 0;
        for (int i = 0; i < N; ++i)
            if (isempty(a, (x + cx[i]),
                           (y + cy[i])))
                count++;
 
        return count;
    }
 
    // Picks next point using Warnsdorff's heuristic.
    // Returns false if it is not possible to pick
    // next point.
    Cell nextMove(int a[], Cell cell)
    {
        int min_deg_idx = -1, c,
            min_deg = (N + 1), nx, ny;
 
        // Try all N adjacent of (*x, *y) starting
        // from a random adjacent. Find the adjacent
        // with minimum degree.
        int start = ThreadLocalRandom.current().nextInt(1000) % N;
        for (int count = 0; count < N; ++count)
        {
            int i = (start + count) % N;
            nx = cell.x + cx[i];
            ny = cell.y + cy[i];
            if ((isempty(a, nx, ny)) &&
                (c = getDegree(a, nx, ny)) < min_deg)
            {
                min_deg_idx = i;
                min_deg = c;
            }
        }
 
        // IF we could not find a next cell
        if (min_deg_idx == -1)
            return null;
 
        // Store coordinates of next point
        nx = cell.x + cx[min_deg_idx];
        ny = cell.y + cy[min_deg_idx];
 
        // Mark next move
        a[ny * N + nx] = a[(cell.y) * N +
                           (cell.x)] + 1;
 
        // Update next point
        cell.x = nx;
        cell.y = ny;
 
        return cell;
    }
 
    /* displays the chessboard with all the
    legal knight's moves */
    void print(int a[])
    {
        for (int i = 0; i < N; ++i)
        {
            for (int j = 0; j < N; ++j)
                System.out.printf("%d\t", a[j * N + i]);
            System.out.printf("\n");
        }
    }
 
    /* checks its neighbouring squares */
    /* If the knight ends on a square that is one
    knight's move from the beginning square,
    then tour is closed */
    boolean neighbour(int x, int y, int xx, int yy)
    {
        for (int i = 0; i < N; ++i)
            if (((x + cx[i]) == xx) &&
                ((y + cy[i]) == yy))
                return true;
 
        return false;
    }
 
    /* Generates the legal moves using warnsdorff's
    heuristics. Returns false if not possible */
    boolean findClosedTour()
    {
        // Filling up the chessboard matrix with -1's
        int a[] = new int[N * N];
        for (int i = 0; i < N * N; ++i)
            a[i] = -1;
 
        // initial position
        int sx = 3;
        int sy = 2;
 
        // Current points are same as initial points
        Cell cell = new Cell(sx, sy);
 
        a[cell.y * N + cell.x] = 1; // Mark first move.
 
        // Keep picking next points using
        // Warnsdorff's heuristic
        Cell ret = null;
        for (int i = 0; i < N * N - 1; ++i)
        {
            ret = nextMove(a, cell);
            if (ret == null)
                return false;
        }
 
        // Check if tour is closed (Can end
        // at starting point)
        if (!neighbour(ret.x, ret.y, sx, sy))
            return false;
 
        print(a);
        return true;
    }
 
    // Driver Code
    public static void main(String[] args)
    {
        // While we don't get a solution
        while (!new GFG().findClosedTour())
        {
            ;
        }
    }
}
 
class Cell
{
    int x;
    int y;
 
    public Cell(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}
 
// This code is contributed by SaeedZarinfam

C#

//C# program for Knight’s tour 
//problem using Warnsdorff’salgorithm
using System;
using System.Collections;
using System.Collections.Generic;
 
public class GFG{
     
    public static int N = 8;
  
    // Move pattern on basis of the change of
    // x coordinates and y coordinates respectively
    public int[] cx = new int[] {1, 1, 2, 2, -1, -1, -2, -2};
    public int[] cy = new int[] {2, -2, 1, -1, 2, -2, 1, -1};
  
    // function restricts the knight to remain within
    // the 8x8 chessboard
    bool limits(int x, int y)
    {
        return ((x >= 0 && y >= 0) && (x < N && y < N));
    }
  
    /* Checks whether a square is valid and
    empty or not */
    bool isempty(int[] a, int x, int y)
    {
        return ((limits(x, y)) && (a[y * N + x] < 0));
    }
 
    /* Returns the number of empty squares
    adjacent to (x, y) */
    int getDegree(int[] a, int x, int y)
    {
        int count = 0;
        for (int i = 0; i < N; ++i)
            if (isempty(a, (x + cx[i]),
                           (y + cy[i])))
                count++;
  
        return count;
    }
     
    // Picks next point using Warnsdorff's heuristic.
    // Returns false if it is not possible to pick
    // next point.
    Cell nextMove(int[] a, Cell cell)
    {
        int min_deg_idx = -1, c,
            min_deg = (N + 1), nx, ny;
  
        // Try all N adjacent of (*x, *y) starting
        // from a random adjacent. Find the adjacent
        // with minimum degree.
        Random random = new Random();
        int start=random.Next(0, 1000);
        for (int count = 0; count < N; ++count)
        {
            int i = (start + count) % N;
            nx = cell.x + cx[i];
            ny = cell.y + cy[i];
            if ((isempty(a, nx, ny)) &&
                (c = getDegree(a, nx, ny)) < min_deg)
            {
                min_deg_idx = i;
                min_deg = c;
            }
        }
  
        // IF we could not find a next cell
        if (min_deg_idx == -1)
            return null;
  
        // Store coordinates of next point
        nx = cell.x + cx[min_deg_idx];
        ny = cell.y + cy[min_deg_idx];
  
        // Mark next move
        a[ny * N + nx] = a[(cell.y) * N +
                           (cell.x)] + 1;
  
        // Update next point
        cell.x = nx;
        cell.y = ny;
  
        return cell;
    }
  
    /* displays the chessboard with all the
    legal knight's moves */
    void print(int[] a)
    {
        for (int i = 0; i < N; ++i)
        {
            for (int j = 0; j < N; ++j)
                Console.Write(a[j * N + i]+"\t");
            Console.Write("\n");
        }
    }
     
    /* checks its neighbouring squares */
    /* If the knight ends on a square that is one
    knight's move from the beginning square,
    then tour is closed */
    bool neighbour(int x, int y, int xx, int yy)
    {
        for (int i = 0; i < N; ++i)
            if (((x + cx[i]) == xx) &&
                ((y + cy[i]) == yy))
                return true;
  
        return false;
    }
  
    /* Generates the legal moves using warnsdorff's
    heuristics. Returns false if not possible */
    bool findClosedTour()
    {
        // Filling up the chessboard matrix with -1's
        int[] a = new int[N * N];
        for (int i = 0; i < N * N; ++i)
            a[i] = -1;
  
        // initial position
        int sx = 3;
        int sy = 2;
  
        // Current points are same as initial points
        Cell cell = new Cell(sx, sy);
  
        a[cell.y * N + cell.x] = 1; // Mark first move.
  
        // Keep picking next points using
        // Warnsdorff's heuristic
        Cell ret = null;
        for (int i = 0; i < N * N - 1; ++i)
        {
            ret = nextMove(a, cell);
            if (ret == null)
                return false;
        }
  
        // Check if tour is closed (Can end
        // at starting point)
        if (!neighbour(ret.x, ret.y, sx, sy))
            return false;
  
        print(a);
        return true;
    }
     
    static public void Main (){
        // While we don't get a solution
        while (!new GFG().findClosedTour())
        {
            ;
        }
    }
}
 
class Cell
{
    public int x;
    public int y;
  
    public Cell(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}
 
//This code is contributed by shruti456rawal

Producción: 

59    14    63    32    1    16    19    34    
62    31    60    15    56    33    2    17    
13    58    55    64    49    18    35    20    
30    61    42    57    54    51    40    3    
43    12    53    50    41    48    21    36    
26    29    44    47    52    39    4    7    
11    46    27    24    9    6    37    22    
28    25    10    45    38    23    8    5    

El problema del camino hamiltoniano es NP-difícil en general. En la práctica, la heurística de Warnsdorff encuentra con éxito una solución en tiempo lineal.

¿Lo sabías?  
“En un tablero de 8 × 8, hay exactamente 26 534 728 821 064 recorridos cerrados dirigidos (es decir, dos recorridos a lo largo del mismo camino que viajan en direcciones opuestas se cuentan por separado, al igual que las rotaciones y los reflejos). ¡La cantidad de recorridos cerrados no dirigidos es la mitad de este número, ya que cada recorrido se puede rastrear al revés!

Este artículo es una contribución de Uddalak Bhaduri . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.org o enviar tu artículo por correo a review-team@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.
Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.
 

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *