通过Astar算法实现8数码问题(C语言)

    关于A*(Astar)算法的介绍可以参考另一篇博文: A*(Astar)搜索算法的实现(C语言)

    1.问题描述

        在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。

    2.问题可解性

        八数码问题的一个状态实际上是0~9的一个排列,对于任意给定的初始状态和目标,不一定有解,也就是说从初始状态不一定能到达目标状态。因为排列有奇排列和偶排列两类,从奇排列不能转化成偶排列或相反。
        如果一个数字0~8的随机排列871526340,用F(X)表示数字X前面比它小的数的个数,全部数字的F(X)之和为Y=∑(F(X)),如果Y为奇数则称原数字的排列是奇排列,如果Y为偶数则称原数字的排列是偶排列。
        例如871526340这个排列的
        Y=0+0+0+1+1+3+2+3+0=10
        10是偶数,所以他偶排列。871625340
        Y=0+0+0+1+1+2+2+3+0=9
        9是奇数,所以他奇排列。
        因此,可以在运行程序前检查初始状态和目标状态的窘是否相同,相同则问题可解,应当能搜索到路径。否则无解。

    3.求解步骤

        1)建立一个队列,计算初始结点的估价函数f,并将初始结点入队,设置队列头和尾指针。
        2)取出队列头(队列头指针所指)的结点,如果该结点是目标结点,则输出路径,程序结束。否则对结点进行扩展。
        3)检查扩展出的新结点是否与队列中的结点重复,若与不能再扩展的结点重复(位于队列头指针之前),则将它抛弃;若新结点与待扩展的结点重复(位于队列头指针之后),则比较两个结点的估价函数中g的大小,保留较小g值的结点。跳至第五步。
        4)如果扩展出的新结点与队列中的结点不重复,则按照它的估价函数f大小将它插入队列中的头结点后待扩展结点的适当位置,使它们按从小到大的顺序排列,最后更新队列尾指针。
        5)如果队列头的结点还可以扩展,直接返回第二步。否则将队列头指针指向下一结点,再返回第二步。
        

    4.代码实现

        二叉堆代码就不贴出来了,在另一篇博文  A*(Astar)搜索算法的实现(C语言) 中已经给出,在本文最后有资源下载的链接。可以拿到完整的代码
 
astar_8.h
/*
* author:	Atom
* date:		2012/12/04
* file:		astar_8.h
*/
#ifndef ASTAR_8_H
#define ASTAR_8_H

#include "bheap.h"

#define MALLOC(type,n)  (type *)malloc((n)*sizeof(type))

#define MAX(a,b) ((a)>(b))?(a):(b)

#define UP  		-3
#define RIGHT 	1
#define DOWN		3
#define LEFT		-1

int node_distance[9][9];

struct step_node
{
	int step_status[9];
	int f;
	int g;
	int h;
	struct step_node* parent;
};

int _comp(struct Bheap_node* n1, struct Bheap_node* n2);

int _eq(struct Bheap_node* n1, struct Bheap_node* n2);

void astar_8(int start[9], int end[9]);

static int calc_distance(int step_status[9], int end[9]);

static int arr_idx(int n, int step_status[9]);

static void init_node_distance(int node_distance[9][9]);

static int is_end(int step_status[9], int end[9]);

static int is_reachable(int space_idx, int flag);

static int move_space(int step_status[9], int flag);

static void print_step_status(int step_status[9]);

static int check_whether_reach(int start[9], int end[9]);

#endif

astar_8.c
/*
* author:	Atom
* 
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
35数码问题是一个经典的搜索问题,其中包含一个3x3的矩阵,其中包含1-8的数字和一个空格,目标是将所有数字移动到其目标位置,并且空格在最终位置。 A*算法是一种启发式搜索算法,它使用估价函数来确定搜索的方向。在此问题中,我们可以使用曼哈顿距离作为估价函数,该距离表示从当前状态到目标状态所需的最小步数。 以下是A*算法实现步骤: 1. 将起始状态加入开放列表,该状态的代价为0,估价函数为曼哈顿距离。 2. 从开放列表中选择具有最小代价+估价函数的状态。如果该状态是目标状态,则完成搜索。 3. 对于该状态的所有后继状态,计算代价和估价函数,并将其添加到开放列表中。 4. 如果开放列表为空,则搜索失败。 5. 重复步骤2-4,直到找到目标状态。 在此过程中,需要使用一个闭合列表来跟踪已经遍历过的状态,以避免循环。 为了实现A*算法,我们可以使用Python编程语言中的优先队列数据结构来存储开放列表,并使用字典数据结构来存储闭合列表。 以下是35数码问题的A*算法实现的示例代码: ``` from queue import PriorityQueue # 计算曼哈顿距离 def manhattan_distance(state): target = [0, 1, 2, 3, 4, 5, 6, 7, 8] distance = 0 for i in range(9): if state[i] != 0: distance += abs(i // 3 - target.index(state[i]) // 3) + abs(i % 3 - target.index(state[i]) % 3) return distance # 执行A*算法 def astar(start_state): open_list = PriorityQueue() open_list.put((0, start_state, [])) closed_list = {} while not open_list.empty(): current = open_list.get() state = current[1] path = current[2] if state == [0, 1, 2, 3, 4, 5, 6, 7, 8]: return path if str(state) in closed_list: continue closed_list[str(state)] = True for i in range(9): if state[i] == 0: pos = i break if pos > 2: new_state = state[:] new_state[pos], new_state[pos - 3] = new_state[pos - 3], new_state[pos] new_path = path + ['U'] open_list.put((len(new_path) + manhattan_distance(new_state), new_state, new_path)) if pos < 6: new_state = state[:] new_state[pos], new_state[pos + 3] = new_state[pos + 3], new_state[pos] new_path = path + ['D'] open_list.put((len(new_path) + manhattan_distance(new_state), new_state, new_path)) if pos % 3 > 0: new_state = state[:] new_state[pos], new_state[pos - 1] = new_state[pos - 1], new_state[pos] new_path = path + ['L'] open_list.put((len(new_path) + manhattan_distance(new_state), new_state, new_path)) if pos % 3 < 2: new_state = state[:] new_state[pos], new_state[pos + 1] = new_state[pos + 1], new_state[pos] new_path = path + ['R'] open_list.put((len(new_path) + manhattan_distance(new_state), new_state, new_path)) return None # 测试 start_state = [7, 2, 4, 5, 0, 6, 8, 3, 1] path = astar(start_state) print(path) ``` 在上面的示例代码中,我们首先定义了一个函数来计算曼哈顿距离。然后,我们实现了A*算法的主要函数,该函数使用优先队列数据结构来存储开放列表,并使用字典数据结构来存储闭合列表。在算法的主循环中,我们选择具有最小代价+估价函数的状态,并计算其所有后继状态的代价和估价函数,并将它们添加到开放列表中。最后,我们返回找到的路径或None(如果搜索失败)。 在上面的示例中,我们使用了一个具有随机初始状态的测试案例。您可以尝试使用不同的起始状态进行测试,以测试算法的性能和效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值