骑士的巡回算法

时间:2013-12-05 20:23:50

标签: c algorithm optimization

所以我必须编写这个算法(这个版本不需要在骑士开始的同一个地方结束),我让它工作,但它太慢了。对于size = 8,它适用于起始位置x = 0和y = 0,但如果我尝试将x和y操作为例如2和4,则它在几分钟后不会结束。有人可以帮忙吗?

#include <stdio.h>
#define size 8
int ruch_x[]={ -1,  1, -2,  2, -2,  2, -1,  1}; //arrays of possible knight moves, for example 2nd move is [1,2]//
int ruch_y[]={  2,  2,  1,  1, -1, -1, -2, -2};

int done(int szach[][size])
{
   for(int i=0;i<size;i++)
   {
      for(int j=0;j<size;j++)
      {
         if(szach[i][j]==0)
            return 0;
      }
   }
   return 1;
}
//licz - variable for counting knights moves//
void konik(int szach[][size], int x, int y, int licz)
{
    szach[x][y]=licz;
    if(licz<size*size){
        for(int i=0;i<=7;i++){ //loop that checks every possible knight move//
            if(szach[x+ruch_x[i]][y+ruch_y[i]]==0&&x+ruch_x[i]>=0&&x+ruch_x[i]<size&&y+ruch_y[i]>=0&&y+ruch_y[i]<size)
// checking if candidat was already visited and if it's not outside of the board//
            {
                konik(szach, x+ruch_x[i], y+ruch_y[i], licz+1);}}}

    if(done(szach)==1) return; //checking if whole board is filled with numbers, if yes then skip zeroing current move//
    szach[x][y]=0;
}

int main()
{
    int i, j, x,y;
    printf("set x\n");
    scanf("%d", &x);
    printf("set y\n");
    scanf("%d", &y);

    int szach[size][size];
    for(i=0;i<size;i++) {  //zeroing array//
            for(j=0;j<size; j++) {
                    szach[i][j]=0; }}
    konik(szach, x, y, 1);

    for(int i=0;i<size;i++) {
            for(int j=0;j<size; j++) {
                    printf("%d\t",szach[j][i]); }
            printf("\n\n");}
    printf("\n");
    return 0;
}

3 个答案:

答案 0 :(得分:1)

简单强暴可能是一个坏主意,因为对于8x8电路板,可能的序列数量可能会超过10 ^ 50。

关于维基百科的

Knight's tour文章提供了关于这个问题的正确信息,我建议从那里开始。

查找汉密尔顿路径的最着名的启发式方法之一是:从任何节点u按照图表中的度数按非递减顺序排列邻居。让我们说从u开始,骑士可以移动到pq(即u有两个邻居),然后在你的递归中考虑最初p是否有比q更少的可用邻居。这通常会导致显着的优化,特别是如果图表中有很多汉密尔顿路径,就像这种情况一样。

关于你的代码。您不需要每次都调用done()。如果在任何时候licz >= size*size,那么您就知道您找到了答案。例如,您可以存储全局boolean done。这应该有所帮助,但在所有情况下都无济于事。

P.S。如果您的项目需要任何单一解决方案,我建议存储单个Hamilton 周期,对于任何x,y,只需简单地移动序列即可获得有效的解决方案。

答案 1 :(得分:0)

这绝不是一个答案,只是一个可能有用的想法。

我记得,对于某些尺寸,简单的螺旋图案将填充电路板的外边缘,并将问题简化为更简单的问题,size缩小4。

您可以使用此提示来简化您的问题。例如,填充8x3和8x5板而不是8x8板(您必须更新算法以寻找合适的结束位置)。

答案 2 :(得分:0)

ile的建议似乎很好。当我实施时只考虑graph中最低(可能从位置移动的数量)的候选者(近似为四角中最近的一个),找到起始位置 x = 2 y = 4 的解决方案的运行时间从10个半小时减少到1分15秒。计划的变化是:

// arrays of possible knight moves, for example 2nd move is [1,2]
// rearranged so that adjacent moves differ by 45 angular degrees
int ruch_x[]={  2,  1, -1, -2, -2, -1,  1,  2};
int ruch_y[]={  1,  2,  2,  1, -1, -2, -2, -1};

#include <stdlib.h>

//licz - variable for counting knights moves//
void konik(int szach[][size], int x, int y, int licz)
{
    szach[x][y]=licz;
    if (licz<size*size)
    {   static char firstmove[2][2][2] = { 1, 0, 6, 7, 2, 3, 5, 4 };
        int m = firstmove[x<size/2][y<size/2][abs(size-1-x*2)<abs(size-1-y*2)];
        for (int s=0; s<=7; s++) //loop that checks every possible knight move//
        {   static int ply[8] = { 0, -1, 1, -2, 2, -3, 3, -4 };
            int i = m+ply[s];   // firstmove towards corner, then back and forth
            if (i < 0) i += 8;
            else
            if (i > 7) i -= 8;
            // checking if candidate ...
            if (x+ruch_x[i]>=0&&x+ruch_x[i]<size    // not outside of the board
             && y+ruch_y[i]>=0&&y+ruch_y[i]<size
             && szach[x+ruch_x[i]][y+ruch_y[i]]==0  // not already visited
               )
                konik(szach, x+ruch_x[i], y+ruch_y[i], licz+1);
        }
    }

    if (done(szach)==1) return; //checking if whole board is filled with numbers
                                //if yes then skip zeroing current move//
    szach[x][y]=0;
}

顺便说一下,原始程序的行为没有严格定义,因为在szach[x+ruch_x[i]][y+ruch_y[i]]x+ruch_x[i]不在板阵列之外的检查之前检查了y+ruch_y[i]