Matlab利用宽度和深度优先搜索解决八数码问题

广度(宽度)优先搜索

例子为:
在这里插入图片描述

原理介绍

在宽度优先搜索算法中,树上节点的扩展是沿着节点深度的“断层”进行的,也就是说,节点的扩展是按照它们接近起始节点的程度依次进行的。
宽度优先搜索法在对深度为n+1的任一节点进行搜索之前,必须先考虑深度为n的每种可能的节点。
因此,尽管这种搜索可能是一种非常长的搜索,但如果存在有任何解答的话,它能够保证最终找到最短(层次深度意义上)的解答序列。
之所以称之为宽度优先算法,是因为算法自始至终一直通过已找到和未找到顶点之间的边界向外扩展,就是说,算法首先搜索和根S距离为k的所有顶点,然后再去搜索和根S距离为k+l的其他顶点

算法理论流程图

图片来自老师的ppt,具体网站不清楚

本次代码的算法步骤

1, 将空格位置由0代替
2, 找到0的位置,确定可以移动的方向
3, 遍历所在层的所有列表,通过dir返回的四位二进制值,确定move移动操作,将move移动后列表,转移到下一层列表,即layer进行自加。
4, 查找产生新的节点,是否存在目标节点。若无则转到2,若有则转到5
5, 停止计算,输出目标节点。

设计思路

结合宽度优先的理论,在所有的节点画出后可以得到一个二叉树,故此结合二叉树的形状设计了parents.date的节点,储存所有的情况,也方便了数据的储存和编程的难度。在后期查看和调试时可以简单明了的看到所有的节点的情况。
在宽度优先搜索的理论中有删除重复的一个操作,在此编程中,经过实际的操作发现,在搜索次数较少的情况下,运行的时间,没有删除的操作的时间要远远小于有删除重复节点的操作的时间。所以在程序中没有加入删除节点的操作,但是保留了程序。

direction(移动方向判定)

function dir = direction(r,c)
% 上
dir = zeros(1 ,4);
if r - 1 < 1
    dir(1) = 1;
end

%if r + 1 > 3
	dir(2) = 1;
end

%if c - 1 <1
	dir(3) = 1;
end

%if c + 1 > 3
	dir(4) = 1;
end
end

为了对所要求移动的目标位置进行移动可能性的判定,采用了一个1x4的“dir”初始化为0的数组,当对空白目标区域所在位置判定之后,在数组中相对应的位置上置1,这样就可以获得所有的可能,方便之后的移动。

move(移动目标数据)

function newMatrix = move(oldMatrix,direction)
%  % n=1上,n=2下,n=3左,n=4右
n = direction;
[rows ,cols] = find(oldMatrix == 0);

if n == 1
    matrix1=oldMatrix;
	buff = matrix1(rows-1,cols);
    matrix1(rows-1,cols)=0;
	matrix1(rows,cols)=buff;
end

if n == 2
	matrix1=oldMatrix;
	buff = matrix1(rows+1,cols);
    matrix1(rows+1,cols)=0;
	matrix1(rows ,cols)=buff;
end

if n == 3
	matrix1=oldMatrix;
	buff = matrix1(rows,cols-1);
    matrix1(rows,cols-1)=0;
	matrix1(rows ,cols)=buff;
end

if n == 4
    matrix1=oldMatrix;
	buff = matrix1(rows,cols+1);
    matrix1(rows,cols+1)=0;
	matrix1(rows ,cols)=buff;
end
	
   newMatrix = matrix1;

end

这个就是很简单的重复操作,根据要求对空格部分进行移动,传出一个新的数组。

主执行部分

clear ,clc;
sign = [2 8 3;
        1 6 4;
        7 0 5];
target = [1 2 3;
         7 8 4;
         0 6 5];

past = 1;     
max = 5;
layer = 1; 	
NO = 1; 	
parents(layer).date(NO).c = sign; 	
flag=0;		
search(layer)=1; 	
iteration=0;   % 记录循环次数
step = 0;

while flag==0 && iteration<10   % 防止循环超过10次死机
    %遍历上一层计算出来的节点,进行计算
	for i = 1 : past
        step = step + 1;
        search(layer) = i;
        
        matrix = parents(layer).date(i).c;
        [rows ,cols] = find(matrix==0);
        
        dir = direction(rows ,cols);		%获得可以移动的方向
        
		%写入移动之后得状态
        for t = 1 : 4
            if dir(t) == 0
                parents(layer+1).date(NO).c = move(matrix ,t);
                NO = NO + 1;
            end
        end
        
		%判别网络
        for n = 1:NO-1
            A = parents(layer+1).date(n).c;
            if isequal(A, target) == 1
                flag = 1;
                search(layer + 1) = n;
                break;
            end
%             %删除重复
%             if layer >= 2
%                 for m = 1:past-1
%                     if parents(layer).date(m).c == parents(layer+1).date(n).c
%                         parents(layer+1).date(n).c = parents(layer+1).date(n+1).c;
%                         
%                         if n == NO
%                             parents(layer+1).date(n).c=zeros(4 ,4);
%                         end
%                     end
%                 end
%             end
        end
        
        if flag==1
            fprintf("实验成功");
            break;
        end
        
    end
    layer = layer + 1;
	past = NO-1;
	NO = 1;
	iteration = iteration+1;
end

具体代码的内容就不进行解释了,总结以下就只是不断打查找之后进行不断的迭代。

使用模块及变量

layer :层数
date :一层中从左到右的个数
NO:可能性
result判断结果
A 目标节点
parents(layer).date(NO).c 节点
flag:退出标志位
search(layer) 移动路径记录
iteration:记录循环次数
step :步数

程序具体流程

在这里插入图片描述

最后可以通过parents看到所有经历过的“枝叶”。

深度优先搜索

目标和初始均和广度搜索一致。

原理介绍

  1. 把起始节点放在OPEN表中(如果该起始节点为一目标节点,则求得一个解答)
  2. 如果OPEN是个空表,则没有解,失败退出;否则继续
  3. 把第一个节点(节点n)从OPEN表移出,并把它放入CLOSED的扩展节点表中
  4. 扩展节点n。如果没有后续节点,则转向(2)
  5. 把n的所有后续节点放到OPEN表的前端,并提供从这些后续节点回到n的指针;如果没有后续节点,则转(2)
  6. 如果n的任一个后续节点是个目标节点,则找到一个解答,成功退出;否则转向(2)。

理论流程图

有老师ppt提供

算法思路

将初始节点放入暂存节点,找点节点中0的位置,根据0的位置确定0的移动方向dir。如果.分别对0进行移动。移动后判断移动的可能性,如果可以则层数不变,位数加一。逐层进行判断,确认得到的节点是否与目标节点相同。若都不同则层数加一,循环操作,至找到节点

设计思路

结合深度搜索的理论可得到,深度优先首先会沿着一个节点搜索,当遇到无法再继续的节点或者达到搜索的深度之后,会从另一个节点继续搜索。但由于八数码问题理论上是可以沿着一条路径经过无限次的移动来达到目标节点(排除无解的情况)。故此,没有选择设置深度搜索的最大深度。
深度优先搜索和宽度优先搜索的最大的区别就在于搜索的方式,所以深度搜索的代码格式和宽度优先搜索没有太大区别,只对判别移动的网络进行修改就可以达到要求。
为达到沿着一条支路进行搜索,所以就对方向的优先级进行一个规定,并且对上一次的移动方向进行禁止,在经过有限次的移动之后,就达到了目标节点的位置。

direction和move函数和广度搜索一致

主执行部分

clear ,clc;
sign = [2 8 3;
        1 0 4;
        7 6 5];
target = [1 2 3;
         7 8 4;
         0 6 5];

past = 1;     
max = 5;
layer = 1; 	
date = 1;
NO = 1; 	
result = 0;

parents(layer).date(NO).c = sign; 	
flag=0;		
search(layer)=1; 	
iteration=0;   % 记录循环次数
step = 0;

while flag==0 && iteration<100   % 防止循环超过100次死机
    for i = 1 : past
        step = step + 1;
        search(NO) = result;
        
        matrix = parents(layer).date(i).c;
        [rows ,cols] = find(matrix==0);
        
		%再没有限制得移动和排除上一次得移动情况,防止回到上一个状态
        if date == 1
            dir = direction(rows ,cols);
        else 
            dir = direction(rows ,cols);
            if result == 1
                dir(2) = 1;
            end 
            if result == 2
                dir(1) = 1;
            end
            if result == 3
                dir(4) = 1;
            end
            if result == 4
                dir(3) = 1;
            end
        end
        
        
        %这里设置一下优先级,可以使得搜索可以有序的进行,沿着一个分支进行
        if dir(1) == 0
            parents(layer).date(NO).c = move(matrix ,1);
            NO = NO + 1;
            result = 1;
        elseif dir(2) == 0
            parents(layer).date(NO).c = move(matrix ,2);
            NO = NO + 1;
            result = 2;
        elseif dir(3) == 0
            parents(layer).date(NO).c = move(matrix ,3);
            NO = NO + 1;
            result = 3;
        elseif dir(4) == 0
            parents(layer).date(NO).c = move(matrix ,4);
            NO = NO + 1;
            result = 4;
        end
        
            %判别网络,退出判定
            A = parents(layer).date(NO-1).c;
            if isequal(A, target)
                flag = 1;
                fprintf("实验成功!");
                break;
            end
            
            %判别重复网络
            for m = 1:past-1
                %瞧瞧重没重复
                if parents(layer).date(m).c == parents(layer).date(NO-1).c    
                      NO = NO - 2;
                      date = 2;   
                else date = 1;  %强行切换变换状态       
                end
            end
        
        if flag==1%强制退出,再加一层保险
            break;
        end
        
        search(NO) = result;%看下移动的轨迹
        
    end
    
    if isequal(dir ,ones(1 ,4))             %防止没有道路可走
        layer = layer + 1;
    end
    
	past = NO;                              %理论上没有啥用

	iteration = iteration+1;
end

程序设计流程图

在这里插入图片描述

使用模块及变量

layer :层数
date :一层中从左到右的个数
NO:可能性
result判断结果

parents(layer).date(NO).c 节点
flag:退出标志位
search(layer) 移动路径记录
iteration:记录循环次数
step :步数

以上的代码可能在大神看来漏洞百出,但是这个方式也算是给其他需要的人一种借鉴,希望对看文章的你有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值