八数码游戏分析+源码——启发式搜索(二)

 大家通过阅读 八数码游戏分析——启发式搜索(一) 应该对解决八数码的启发式搜索算法有了一个大致的印象了,那么我就开始介绍基于 八数码游戏分析——启发式搜索(一)的一种改进的算法。

改进的启发式搜索策略:八数码的下一个格局中每个棋子移动到正确位置所需要的步数要少于当前格局中每个棋子移动到正确位置所需要的步数。

首先来解释一下什么是棋子移动到正确位置的步数。

      我们在 八数码游戏分析——启发式搜索(一) 中提到每个数码的位置与最终格局的对比,如果位置相同,则说明此数码在正确位置。

还是拿那个图来说明:

 

                                                 图2.1

假设图2.1右边的格局是最终状态,那么可以看出左边格局中正确位置的数码就是红色字体标识的数码,分别是3,4,5,7。

我们把初始数码中的每个未移动到正确位置的数码拿出来单独分析

我们拿数码2来分析,如下图:

                                                 图2.2

 

可以看出数码2移动到正确位置需要一步,我们再拿数码8来分析:

                                                                 图2.3

从图2.3可以看出,数码8移动到正确位置为两步

因此,一次可以得出数码1和数码6移动到正确位置都为一步,所以当前状态的每个棋子移动到正确位置的步数总和为:5步

那么当前状态的下一状态如下图所示:

                                                                                            图2.4

图2.4中,红色字体标识的是在正确位置的数码,八数码旁边的(n)表示当前数码格局中每个棋子移动到正确位置的步数总和为n步。

由此可以看出图2.4的左下方格局满足条件(一共要4步),所以左下方的格局为下一步格局。可能现在大家还没有看出此算法相对于前一个算法的优越性,

好,我们再向后继续推:

 

                                                                               图2.5

细心的朋友一定还记得,在 八数码游戏分析——启发式搜索(一)中,推到这一步的时候有两个一样的格局可以选择,而此算法在下一步只有右下方的格局总步数(3步)少于当前格局总步数(4步)。

不仅如此,经过大量的测试证明,八数码问题运用此算法到达最终格局所用的步数也是最少的!

下面贴一个测试c程序:

/*
程序名:八数码问题
作者:侯青青       完成时间:2010.7.5
描述:
主要函数列表:
      1.show()     显示当前待调整数码矩阵
	  2.exchange() 交换数码中的 begin[row_one][column_one] 与 begin[row_two][column_two] 这两个数
	  3.judge()    判断待调整的数码与最终数码相比正确位置数码的个数
      4.yidong()   将待调整数码从开始位置移动到终止位置,并将其过程输出
	  5.shuru()    有用户输入待调整的数码矩阵最初状态的数,并将其存入到begin[][]数组中
其它说明:此程序运用到启发式搜索的策略,
          (1):将空格的地方存储零,这样便于操作
		  (2):每次交换0上下左右4个方向元素的位置,八数码的下一个格局中每个棋子移动到正确位置所需要的步数要少于
		          当前格局中每个棋子移动到正确位置所需要的步数则算成功,则重复步骤2,失败的话则跳回上一轮交换。
		  (3):当交换到最终所有的数码的位置都正确时结束
*/

#include"stdio.h"
#include"math.h"
#define num 3 //宏定义数码的行列数为3

/*显示当前待调整数码矩阵*/
void show(int begin[num][num])  
{
	for(int i = 0; i < num; i++)
	{
		for(int j = 0; j < num; j++)
			printf("%d ", begin[i][j]);
		printf("\n");
	}
	printf("\n");
}

/*交换数码中的 begin[row_one][column_one] 与 begin[row_two][column_two] 这两个数*/
void exchange(int begin[num][num], int row_one, int column_one, int row_two, int column_two)  
{
	int temp;
	temp = begin[row_two][column_two] ;
	begin[row_two][column_two] = begin[row_one][column_one];
	begin[row_one][column_one] = temp;
}

/*判断待调整的数码与最终数码相比每个棋子要移动到正确位置需要的步数*/
int judge(int begin[num][num], int end[num][num]) 
{
	int count=0;           //count记录所有棋子移动到正确位置需要的步数
	for(int i = 0; i < num; i++)   //检查当前图形的正确度
		for(int j = 0; j < num; j++)
		{
			if(begin[i][j] == 0)
				continue;
			else if(begin[i][j] != end[i][j])
			{
				for(int k=0; k 0 && biaoji != 0)             //存储0的位置不是在第一行
	{
		exchange(begin, row - 1, column, row , column);  //将0与其上面的元素交换存储位置
		temp = judge(begin, end);
	    if(temp >= right)   //如果交换后步数大于等于原来的步数
	    	exchange(begin, row - 1, column, row , column); //再将其交换回来		
		else if(temp < right)          //如果交换后步数小于原来的步数
		{
			temp_zhi = yidong(begin, end, temp, jishu+1, ji_shu, 2, row-1, column);
     		if( temp_zhi == 1)  //进行下一步的移动
	     	    return 1;
			exchange(begin, row - 1, column, row , column); //再将其交换回来
		}
	}
	if(column > 0 && biaoji != 1)
	{
	   	exchange(begin, row, column - 1, row , column); //将0与其左边的元素交换存储位置
		temp = judge(begin, end);		
		if(temp >= right)   
   		   exchange(begin, row, column - 1, row , column);			
		else if(temp < right)
		{
			temp_zhi = yidong(begin, end, temp, jishu+1, ji_shu ,3, row, column - 1);
		    if(temp_zhi == 1)  
                 return 1;
			exchange(begin, row, column - 1, row , column);
		}
	}

	if(row < num-1 && biaoji != 2)
	{
	    exchange(begin, row + 1, column, row , column); //将0与其下面的元素交换存储位置
		temp = judge(begin, end);	
		if(temp >= right) 
			exchange(begin, row + 1, column, row , column);
		else if(temp < right)
		{
			temp_zhi =yidong(begin, end, temp, jishu+1, ji_shu, 0, row+1, column);
		   if(temp_zhi == 1)  
		      return 1;
		   exchange(begin, row + 1, column, row , column);
		}
	}
	if(column < num-1 && biaoji != 3)
	{
	    exchange(begin, row, column + 1, row , column); //将0与其右边的元素交换存储位置
		temp = judge(begin, end);	
		if(temp >= right)   
		    exchange(begin, row, column + 1, row , column);		
	    else if(temp < right)  
		{
			temp_zhi = yidong(begin, end, temp, jishu+1, ji_shu, 1, row, column+1);
			if(temp_zhi == 1)  
		       return 1;
			exchange(begin, row, column + 1, row , column);	
		}
	}
	return 0;   //移动失败,返回0
}

/*有用户输入待调整的数码矩阵最初状态的数,并将其存入到begin[][]数组中*/
void shuru(int begin[][num],int blank[])  
{
	int temp, node, zero = 0;
	for (int i = 0; i < num; i++)
		for(int j = 0; j < num; j++)
		{
			node = 1;
			printf("请输入第%d行,第%d列的元素的值:", i+1, j+1);
		    scanf("%d", &temp);
			for (int q = 0; q <= i && node == 1; q++)  //当输入的值有重复的,提示重新输入
				for (int w = 0; w < j; w++)
					if(temp == begin[q][w])
					{
						printf("输入重复,请重新输入\n");
						node = 0;
							j--;
						break;
					}
			if(temp < 0 || temp > num*num-1)   //当输入的值不是在数码的区间范围内时,提示重新输入
			{
				printf("请输入从%d到%d的数\n", zero, num*num-1);
				node = 0;
			    j--;
			}
			if(node == 1)   //如果输入满足条件	
			{
				if(temp == 0) //如果输入的值为零,由blank[0]记录行号,blank[1]记录列号
				{
					blank[0] = i;
					blank[1] = j;
				}
				begin[i][j] = temp;//将满足条件的值存储起来
			}
		}
}

int main()
{
	int jishu = 0, ji_shu[50][3][3];//jishu存储已经遍历过的八数码图形的个数,jishu[][][]存储已经遍历过的八数码图形的形状
    int row;     //存储数字零的行数
    int column;  //存储数字零的列数
	int begin[num][num], blank[2],count=1; 	
	int end[num][num] = {1, 2, 3, 8, 0, 4, 7, 6, 5};  //给最终状态的数码矩阵赋值
    printf ("-------%d数码游戏开始!--------\n", num);
    shuru(begin, blank);   //输入带调整状态的数码矩阵的值
	row = blank[0];
	column = blank[1];
	if(yidong (begin, end,judge(begin,end),jishu,ji_shu,4,row,column) == 0)  
	   printf("\n此8数码的问题可能无解!");
	getchar();getchar();
	return 0;
}


 

下面是运行结果示例:

 

 

欢迎大家转载,如有转载请注明文章来自:   http://blog.csdn.net/q345852047

 

你可能感兴趣的:(算法)