(C语言)A-Star算法解决8数码问题

一、引言

        A算法是一种启发式搜索算法,常用于解决问题的最短路径。对于8数码问题,A算法可以用于找到从初始状态到目标状态的最短路径。

        八数码问题是在 3×3 的九宫格棋盘上,摆有 8 个刻有 1 8 数码的将牌。棋盘中有一个格,允许紧邻空格的某一将牌可以移到空格中,这样通过平移将牌可以将某一将牌布局变换为另一布局。针对给定的一种初始布局或结构(目标状态),问如何移动将牌,实现从初始状态到目标状态的转变。如下图表示了一个具体的八数码问题求解。

二、代码

2.1  解决8数码问题

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define N 3

// 结点定义
typedef struct Node {
    int puzzle[N][N];
    struct Node* parent;
    int cost; // g(n) - 实际代价
    int heuristic; // h(n) - 启发式代价
} Node;

// 创建新结点
Node* createNode(int puzzle[N][N], int cost, int heuristic, Node* parent) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    if (newNode == NULL) {
        printf("Memory allocation failed.\n");
        exit(EXIT_FAILURE);
    }

    // 复制拼图状态
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            newNode->puzzle[i][j] = puzzle[i][j];
        }
    }

    newNode->parent = parent;
    newNode->cost = cost;
    newNode->heuristic = heuristic;

    return newNode;
}

// 计算启发式代价(这里使用曼哈顿距离)
int calculateHeuristic(int puzzle[N][N], int goal[N][N]) {
    int heuristic = 0;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            if (puzzle[i][j] != 0) {
                int goalX, goalY;
                goalX = (puzzle[i][j] - 1) / N;
                goalY = (puzzle[i][j] - 1) % N;
                heuristic += abs(i - goalX) + abs(j - goalY);
            }
        }
    }
    return heuristic;
}

// 检查两个状态是否相同
bool areStatesEqual(int state1[N][N], int state2[N][N]) {
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            if (state1[i][j] != state2[i][j]) {
                return false;
            }
        }
    }
    return true;
}

// 检查结点是否已经在开放列表中
bool isInOpenList(Node* node, Node** openList, int openListSize) {
    for (int i = 0; i < openListSize; i++) {
        if (areStatesEqual(node->puzzle, openList[i]->puzzle)) {
            return true;
        }
    }
    return false;
}

// 检查结点是否已经在关闭列表中
bool isInClosedList(Node* node, Node** closedList, int closedListSize) {
    for (int i = 0; i < closedListSize; i++) {
        if (areStatesEqual(node->puzzle, closedList[i]->puzzle)) {
            return true;
        }
    }
    return false;
}

// 查找空格位置
void findBlankPosition(int puzzle[N][N], int* blankX, int* blankY) {
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            if (puzzle[i][j] == 0) {
                *blankX = i;
                *blankY = j;
                return;
            }
        }
    }
}

// 移动方块
void move(int puzzle[N][N], int x1, int y1, int x2, int y2) {
    int temp = puzzle[x1][y1];
    puzzle[x1][y1] = puzzle[x2][y2];
    puzzle[x2][y2] = temp;
}

// 生成子结点
Node* generateChildNode(Node* parent, int x1, int y1, int x2, int y2, int cost, int goal[N][N]) {
    int childPuzzle[N][N];
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            childPuzzle[i][j] = parent->puzzle[i][j];
        }
    }

    move(childPuzzle, x1, y1, x2, y2);

    int heuristic = calculateHeuristic(childPuzzle, goal);
    return createNode(childPuzzle, parent->cost + cost, heuristic, parent);
}

// 输出拼图状态 
void printPuzzle(int puzzle[N][N]) {
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            printf("%d ", puzzle[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

// 查找可行的移动并生成子结点
void findAndGenerateChildren(Node* parent, Node** openList, int* openListSize,
                              Node** closedList, int* closedListSize, int goal[N][N]) {
    int blankX, blankY;
    findBlankPosition(parent->puzzle, &blankX, &blankY);

    // 向上移动
    if (blankX > 0) {
        Node* childNode = generateChildNode(parent, blankX, blankY, blankX - 1, blankY, 1, goal);
        if (!isInClosedList(childNode, closedList, *closedListSize) &&
            !isInOpenList(childNode, openList, *openListSize)) {
            openList[*openListSize] = childNode;
            (*openListSize)++;
        } else {
            free(childNode);
        }
    }

    // 向下移动
    if (blankX < N - 1) {
        Node* childNode = generateChildNode(parent, blankX, blankY, blankX + 1, blankY, 1, goal);
        if (!isInClosedList(childNode, closedList, *closedListSize) &&
            !isInOpenList(childNode, openList, *openListSize)) {
            openList[*openListSize] = childNode;
            (*openListSize)++;
        } else {
            free(childNode);
        }
    }

    // 向左移动
    if (blankY > 0) {
        Node* childNode = generateChildNode(parent, blankX, blankY, blankX, blankY - 1, 1, goal);
        if (!isInClosedList(childNode, closedList, *closedListSize) &&
            !isInOpenList(childNode, openList, *openListSize)) {
            openList[*openListSize] = childNode;
            (*openListSize)++;
        } else {
            free(childNode);
        }
    }

    // 向右移动
    if (blankY < N - 1) {
        Node* childNode = generateChildNode(parent, blankX, blankY, blankX, blankY + 1, 1, goal);
        if (!isInClosedList(childNode, closedList, *closedListSize) &&
            !isInOpenList(childNode, openList, *openListSize)) {
            openList[*openListSize] = childNode;
            (*openListSize)++;
        } else {
            free(childNode);
        }
    }
}
// 计算逆序数
int countInversions(int puzzle[N][N]) {
    int inversions = 0;
    int flatArray[N * N];

    // 将二维数组展平成一维数组
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            flatArray[i * N + j] = puzzle[i][j];
        }
    }

    // 计算逆序数
    for (int i = 0; i < N * N - 1; i++) {
        for (int j = i + 1; j < N * N; j++) {
            if (flatArray[i] > flatArray[j] && flatArray[i] != 0 && flatArray[j] != 0) {
                inversions++;
            }
        }
    }

    return inversions;
}

// A*算法核心函数
void aStarAlgorithm(Node* initialState, int goal[N][N]) {
    Node* openList[N * N * N * N]; // 最坏情况下可能有 N^4 个结点
    int openListSize = 0;

    Node* closedList[N * N * N * N];
    int closedListSize = 0;

    openList[openListSize++] = initialState;

    while (openListSize > 0) {
        Node* currentNode = openList[0];
        int currentIndex = 0;

        // 在开放列表中找到启发式代价最小的结点
        for (int i = 1; i < openListSize; i++) {
            if ((currentNode->cost + currentNode->heuristic) > (openList[i]->cost + openList[i]->heuristic)) {
                currentNode = openList[i];
                currentIndex = i;
            }
        }
    

        // 将当前结点从开放列表中移除
        for (int i = currentIndex; i < openListSize - 1; i++) {
            openList[i] = openList[i + 1];
        }
        openListSize--;

        // 将当前结点添加到关闭列表中
        closedList[closedListSize++] = currentNode;

        // 如果当前结点达到目标状态,结束算法
        if (areStatesEqual(currentNode->puzzle, goal)) {
            // Output the path from the goal to the initial state
            printf("Goal state reached!\n");
            Node* path[N * N * N * N];  // 用于存储路径上的节点
            int pathSize = 0;
            while (currentNode != NULL) {
                path[pathSize++] = currentNode;
                currentNode = currentNode->parent;
            }
            // Reverse the path (to get it from initial to goal)
            for (int i = pathSize - 1; i >= 0; i--) {
                for (int j = 0; j < N; j++) {
                    for (int k = 0; k < N; k++) {
                        printf("%d ", path[i]->puzzle[j][k]);
                    }
                    printf("\n");
                }
                printf("\n");
            }

            printf("Shortest path:\n");
			for (int i = pathSize - 1; i >= 0; i--) {
    			printf("Step %d:\n", pathSize - i - 1);
    			printPuzzle(path[i]->puzzle);  // Use the printPuzzle function to print the puzzle configuration
    			printf("\n");
			}
            
            // 释放内存 
            for (int i = 0; i < closedListSize; i++) {
                free(closedList[i]);
            }
            return;
        }

        // 生成子结点并添加到开放列表中
        findAndGenerateChildren(currentNode, openList, &openListSize, closedList, &closedListSize, goal);
    }

    // 释放内存 
    for (int i = 0; i < closedListSize; i++) {
        free(closedList[i]);
    }
}

int main() {
    int initial[N][N] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; // 初始状态
    printf("请输入初始状态S:");
    for(int i=0; i<N; i++){
    	for(int j=0; j<N; j++){
    		scanf("%d,", &initial[i][j]);
		}
	}
    int goal[N][N] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};    // 目标状态
	printf("请输入终点状态S:");
    for(int i=0; i<N; i++){
    	for(int j=0; j<N; j++){
    		scanf("%d,", &goal[i][j]);
		}
	}
	
	int inversions = countInversions(initial);
    int goalInversions = countInversions(goal);

    // 检查逆序数奇偶性
    if ((inversions % 2 == 0 && goalInversions % 2 == 0) || (inversions % 2 == 1 && goalInversions % 2 == 1)) {
        printf("问题有解\n");
        Node* initialState = createNode(initial, 0, calculateHeuristic(initial, goal), NULL);
        aStarAlgorithm(initialState, goal);
    } else {
    	printf("初始状态的逆序对的数量为%d, 目标状态的逆序对的数量为%d\n", inversions, goalInversions);
        printf("问题无解\n");
        return 0;
    }
    
    
    Node* initialState = createNode(initial, 0, calculateHeuristic(initial, goal), NULL);

    aStarAlgorithm(initialState, goal);

    return 0;
}

  2.2 解决15数码问题

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define N 4

// 结点定义
typedef struct Node {
    int puzzle[N][N];
    struct Node* parent;
    int cost; // g(n) - 实际代价
    int heuristic; // h(n) - 启发式代价
} Node;

// 创建新结点
Node* createNode(int puzzle[N][N], int cost, int heuristic, Node* parent) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    if (newNode == NULL) {
        printf("Memory allocation failed.\n");
        exit(EXIT_FAILURE);
    }

    // 复制拼图状态
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            newNode->puzzle[i][j] = puzzle[i][j];
        }
    }

    newNode->parent = parent;
    newNode->cost = cost;
    newNode->heuristic = heuristic;

    return newNode;
}

// 计算启发式代价(这里使用曼哈顿距离)
int calculateHeuristic(int puzzle[N][N], int goal[N][N]) {
    int heuristic = 0;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            if (puzzle[i][j] != 0) {
                int goalX, goalY;
                goalX = (puzzle[i][j] - 1) / N;
                goalY = (puzzle[i][j] - 1) % N;
                heuristic += abs(i - goalX) + abs(j - goalY);
            }
        }
    }
    return heuristic;
}

// 检查两个状态是否相同
bool areStatesEqual(int state1[N][N], int state2[N][N]) {
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            if (state1[i][j] != state2[i][j]) {
                return false;
            }
        }
    }
    return true;
}

// 检查结点是否已经在开放列表中
bool isInOpenList(Node* node, Node** openList, int openListSize) {
    for (int i = 0; i < openListSize; i++) {
        if (areStatesEqual(node->puzzle, openList[i]->puzzle)) {
            return true;
        }
    }
    return false;
}

// 检查结点是否已经在关闭列表中
bool isInClosedList(Node* node, Node** closedList, int closedListSize) {
    for (int i = 0; i < closedListSize; i++) {
        if (areStatesEqual(node->puzzle, closedList[i]->puzzle)) {
            return true;
        }
    }
    return false;
}

// 查找空格位置
void findBlankPosition(int puzzle[N][N], int* blankX, int* blankY) {
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            if (puzzle[i][j] == 0) {
                *blankX = i;
                *blankY = j;
                return;
            }
        }
    }
}

// 移动方块
void move(int puzzle[N][N], int x1, int y1, int x2, int y2) {
    int temp = puzzle[x1][y1];
    puzzle[x1][y1] = puzzle[x2][y2];
    puzzle[x2][y2] = temp;
}

// 生成子结点
Node* generateChildNode(Node* parent, int x1, int y1, int x2, int y2, int cost, int goal[N][N]) {
    int childPuzzle[N][N];
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            childPuzzle[i][j] = parent->puzzle[i][j];
        }
    }

    move(childPuzzle, x1, y1, x2, y2);

    int heuristic = calculateHeuristic(childPuzzle, goal);
    return createNode(childPuzzle, parent->cost + cost, heuristic, parent);
}

// 输出拼图状态 
void printPuzzle(int puzzle[N][N]) {
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            printf("%d ", puzzle[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

// 查找可行的移动并生成子结点
void findAndGenerateChildren(Node* parent, Node** openList, int* openListSize,
                              Node** closedList, int* closedListSize, int goal[N][N]) {
    int blankX, blankY;
    findBlankPosition(parent->puzzle, &blankX, &blankY);

    // 向上移动
    if (blankX > 0) {
        Node* childNode = generateChildNode(parent, blankX, blankY, blankX - 1, blankY, 1, goal);
        if (!isInClosedList(childNode, closedList, *closedListSize) &&
            !isInOpenList(childNode, openList, *openListSize)) {
            openList[*openListSize] = childNode;
            (*openListSize)++;
        } else {
            free(childNode);
        }
    }

    // 向下移动
    if (blankX < N - 1) {
        Node* childNode = generateChildNode(parent, blankX, blankY, blankX + 1, blankY, 1, goal);
        if (!isInClosedList(childNode, closedList, *closedListSize) &&
            !isInOpenList(childNode, openList, *openListSize)) {
            openList[*openListSize] = childNode;
            (*openListSize)++;
        } else {
            free(childNode);
        }
    }

    // 向左移动
    if (blankY > 0) {
        Node* childNode = generateChildNode(parent, blankX, blankY, blankX, blankY - 1, 1, goal);
        if (!isInClosedList(childNode, closedList, *closedListSize) &&
            !isInOpenList(childNode, openList, *openListSize)) {
            openList[*openListSize] = childNode;
            (*openListSize)++;
        } else {
            free(childNode);
        }
    }

    // 向右移动
    if (blankY < N - 1) {
        Node* childNode = generateChildNode(parent, blankX, blankY, blankX, blankY + 1, 1, goal);
        if (!isInClosedList(childNode, closedList, *closedListSize) &&
            !isInOpenList(childNode, openList, *openListSize)) {
            openList[*openListSize] = childNode;
            (*openListSize)++;
        } else {
            free(childNode);
        }
    }
}
// 计算逆序数
int countInversions(int puzzle[N][N]) {
    int inversions = 0;
    int flatArray[N * N];

    // 将二维数组展平成一维数组
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            flatArray[i * N + j] = puzzle[i][j];
        }
    }

    // 计算逆序数
    for (int i = 0; i < N * N - 1; i++) {
        for (int j = i + 1; j < N * N; j++) {
            if (flatArray[i] > flatArray[j] && flatArray[i] != 0 && flatArray[j] != 0) {
                inversions++;
            }
        }
    }

    return inversions;
}

// A*算法核心函数
void aStarAlgorithm(Node* initialState, int goal[N][N]) {
    Node* openList[N * N * N * N]; // 最坏情况下可能有 N^4 个结点
    int openListSize = 0;

    Node* closedList[N * N * N * N];
    int closedListSize = 0;

    openList[openListSize++] = initialState;

    while (openListSize > 0) {
        Node* currentNode = openList[0];
        int currentIndex = 0;

        // 在开放列表中找到启发式代价最小的结点
        for (int i = 1; i < openListSize; i++) {
            if ((currentNode->cost + currentNode->heuristic) > (openList[i]->cost + openList[i]->heuristic)) {
                currentNode = openList[i];
                currentIndex = i;
            }
        }
    

        // 将当前结点从开放列表中移除
        for (int i = currentIndex; i < openListSize - 1; i++) {
            openList[i] = openList[i + 1];
        }
        openListSize--;

        // 将当前结点添加到关闭列表中
        closedList[closedListSize++] = currentNode;

        // 如果当前结点达到目标状态,结束算法
        if (areStatesEqual(currentNode->puzzle, goal)) {
            // Output the path from the goal to the initial state
            printf("Goal state reached!\n");
            Node* path[N * N * N * N];  // 用于存储路径上的节点
            int pathSize = 0;
            while (currentNode != NULL) {
                path[pathSize++] = currentNode;
                currentNode = currentNode->parent;
            }
            // Reverse the path (to get it from initial to goal)
            for (int i = pathSize - 1; i >= 0; i--) {
                for (int j = 0; j < N; j++) {
                    for (int k = 0; k < N; k++) {
                        printf("%d ", path[i]->puzzle[j][k]);
                    }
                    printf("\n");
                }
                printf("\n");
            }

            printf("Shortest path:\n");
			for (int i = pathSize - 1; i >= 0; i--) {
    			printf("Step %d:\n", pathSize - i - 1);
    			printPuzzle(path[i]->puzzle);  // Use the printPuzzle function to print the puzzle configuration
    			printf("\n");
			}
            
            // 释放内存 
            for (int i = 0; i < closedListSize; i++) {
                free(closedList[i]);
            }
            return;
        }

        // 生成子结点并添加到开放列表中
        findAndGenerateChildren(currentNode, openList, &openListSize, closedList, &closedListSize, goal);
    }

    // 释放内存 
    for (int i = 0; i < closedListSize; i++) {
        free(closedList[i]);
    }
}

int main() {
    int initial[N][N] = {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}; // 初始状态
    printf("请输入初始状态S:");
    for(int i=0; i<N; i++){
    	for(int j=0; j<N; j++){
    		scanf("%d,", &initial[i][j]);
		}
	}
    int goal[N][N] = {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};    // 目标状态
	printf("请输入终点状态S:");
    for(int i=0; i<N; i++){
    	for(int j=0; j<N; j++){
    		scanf("%d,", &goal[i][j]);
		}
	}
	
	int inversions = countInversions(initial);
    int goalInversions = countInversions(goal);

    // 检查逆序数奇偶性
    if ((inversions % 2 == 0 && goalInversions % 2 == 0) || (inversions % 2 == 1 && goalInversions % 2 == 1)) {
        printf("问题有解\n");
        Node* initialState = createNode(initial, 0, calculateHeuristic(initial, goal), NULL);
        aStarAlgorithm(initialState, goal);
    } else {
    	printf("初始状态的逆序对的数量为%d, 目标状态的逆序对的数量为%d\n", inversions, goalInversions);
        printf("问题无解\n");
        return 0;
    }
    
    
    Node* initialState = createNode(initial, 0, calculateHeuristic(initial, goal), NULL);

    aStarAlgorithm(initialState, goal);

    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值