首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Astar算法解决八数码问题Python实现(GUI)

Astar算法解决八数码问题Python实现(GUI)

作者头像
里克贝斯
发布于 2021-05-21 06:48:29
发布于 2021-05-21 06:48:29
1.7K00
代码可运行
举报
文章被收录于专栏:图灵技术域图灵技术域
运行总次数:0
代码可运行

简介

八数码问题:在3*3的方格棋盘上,摆放着1到8这八个数码,有1个方格是空的,其初始状态如图1所示,要求对空格执行空格左移、空格右移、空格上移和空格下移这四个操作使得棋盘从初始状态到目标状态。

状态图搜索:

  • 搜索树:搜索过程中经过的节点和边按原图的连接关系构成一个树型的有向图,称为搜索树。
  • 搜索方式
    • 树式搜索:记录搜索过程中所经过的所有节点和边
  • 路径的获得
    • 树式搜索:反向求解

树式搜索算法:

  • 步1 把初始节点放入OPEN表;
  • 步2 检查OPEN表,若为空,则问题无解,退出;
  • 步3 移出OPEN表中第一个节点N并放入CLOSED表中,并编号为n;
  • 步4 考察节点N是否为目标节点,若是,则搜索成功,退出;
  • 步5 若N不可扩展,则转步2;
  • 步6 扩展节点N,生成所有子节点,对这组子节点作如下处理:
  • (1)、如果有节点N的先辈节点,则删除;
  • (2)、如果有已存在于OPEN表的节点,也删除;但删除之前要比较其返回初始节点的新路径与原路径,如果新路径“短”,则修改这些节点在OPEN表中的原指向父节点的指针,使其指向新的父节点。
  • (3)、如果有已存在于CLOSED表中的节点,则作与(2)同样的处理,并且再将其移出CLOSED表,放入OPEN表重新扩展;
  • (4)、对其余子节点,配上指向父节点n的指针后放入OPEN表,对OPEN表按某种搜索策略排序后转步2。

启发式搜索的实验原理:

启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标。这样可以省略大量无谓的搜索路径,提高了效率。评估函数f(x)定义为:从初始节点S0出发,约束地经过节点X到达目标节点Sg的所有路径中最小路径代价的估计值。

其一般形式为:

f(x)=g(x)+h(x)

其中g(x)表示从初始节点S0到节点X的实际代价;h(x)表示从X到目标节点Sg的最优路径的估计代价。但是实际的形式要根据问题特性确定。

Closed表和open表

——closed表对树式搜索来说存储的是正在成长的搜索树,对线式搜索来说存储的是不断伸长的折现,本省就是所求的路径。

——open表存储当前待考察的节点。

使用一种启发式搜索方法(A算法)编程求解八数码问题(初始状态任选,并对实验结果进行分析得出合理的结论。

流程图

代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import sys
import random
from enum import IntEnum
from PyQt5.QtWidgets import QLabel, QWidget, QApplication, QGridLayout, QMessageBox
from PyQt5.QtGui import QFont, QPalette
from PyQt5.QtCore import Qt
import copy

solution = []
solutionStep = 0
times = 0

# 用枚举类表示方向
class Direction(IntEnum):
    UP = 0
    DOWN = 1
    LEFT = 2
    RIGHT = 3


class NumberHuaRong(QWidget):
    def __init__(self):
        super().__init__()
        self.blocks = []
        self.zero_row = 0
        self.zero_column = 0
        self.gltMain = QGridLayout()
        self.initUI()

    def initUI(self):
        # 设置方块间隔
        self.gltMain.setSpacing(10)

        self.onInit()

        # 设置布局
        self.setLayout(self.gltMain)
        # 设置宽和高
        self.setFixedSize(400, 400)
        # 设置标题
        self.setWindowTitle('八数码问题')
        # 设置背景颜色
        self.setStyleSheet("background-color:gray;")
        self.show()

    # 初始化布局
    def onInit(self):
        # 产生顺序数组
        self.numbers = list(range(1, 9))
        self.numbers.append(0)

        # 将数字添加到二维数组
        for row in range(3):
            self.blocks.append([])
            for column in range(3):
                temp = self.numbers[row * 3 + column]

                if temp == 0:
                    self.zero_row = row
                    self.zero_column = column
                self.blocks[row].append(temp)
        print(self.blocks)
        # 打乱数组
        for i in range(500):
            random_num = random.randint(0, 3)
            self.move(Direction(random_num))

        self.updatePanel()

    # 检测按键
    def keyPressEvent(self, event):
        key = event.key()
        if key == Qt.Key_Up or key == Qt.Key_W:
            self.move(Direction.UP)
        if key == Qt.Key_Down or key == Qt.Key_S:
            self.move(Direction.DOWN)
        if key == Qt.Key_Left or key == Qt.Key_A:
            self.move(Direction.LEFT)
        if key == Qt.Key_Right or key == Qt.Key_D:
            self.move(Direction.RIGHT)
        if key == Qt.Key_Enter or key == Qt.Key_Space:
            global solution
            solutionLen = len(solution)
            global solutionStep
            global times
            self.blocks = solution[solutionLen-solutionStep-3]
            print(self.blocks)
            solutionStep = solutionStep + 1
            times += 1
        self.updatePanel()
        if self.checkResult():
            if QMessageBox.Ok == QMessageBox.information(self, '挑战结果', '恭喜您完成挑战!总步数:' + str(times)):
                self.onInit()

    # 方块移动算法
    def move(self, direction):
        if(direction == Direction.UP): # 上
            if self.zero_row != 2:
                self.blocks[self.zero_row][self.zero_column] = self.blocks[self.zero_row + 1][self.zero_column]
                self.blocks[self.zero_row + 1][self.zero_column] = 0
                self.zero_row += 1
        if(direction == Direction.DOWN): # 下
            if self.zero_row != 0:
                self.blocks[self.zero_row][self.zero_column] = self.blocks[self.zero_row - 1][self.zero_column]
                self.blocks[self.zero_row - 1][self.zero_column] = 0
                self.zero_row -= 1
        if(direction == Direction.LEFT): # 左
            if self.zero_column != 2:
                self.blocks[self.zero_row][self.zero_column] = self.blocks[self.zero_row][self.zero_column + 1]
                self.blocks[self.zero_row][self.zero_column + 1] = 0
                self.zero_column += 1
        if(direction == Direction.RIGHT): # 右
            if self.zero_column != 0:
                self.blocks[self.zero_row][self.zero_column] = self.blocks[self.zero_row][self.zero_column - 1]
                self.blocks[self.zero_row][self.zero_column - 1] = 0
                self.zero_column -= 1

    def updatePanel(self):
        for row in range(3):
            for column in range(3):
                self.gltMain.addWidget(Block(self.blocks[row][column]), row, column)

        self.setLayout(self.gltMain)

    # 检测是否完成
    def checkResult(self):
        # 先检测最右下角是否为0
        if self.blocks[2][2] != 0:
            return False

        for row in range(3):
            for column in range(3):
                # 运行到此处说名最右下角已经为0,pass即可
                if row == 2 and column == 2:
                    pass
                #值是否对应
                elif self.blocks[row][column] != row * 3 + column + 1:
                    return False

        return True

class Block(QLabel):
    """ 数字方块 """

    def __init__(self, number):
        super().__init__()

        self.number = number
        self.setFixedSize(80, 80)

        # 设置字体
        font = QFont()
        font.setPointSize(30)
        font.setBold(True)
        self.setFont(font)

        # 设置字体颜色
        pa = QPalette()
        pa.setColor(QPalette.WindowText, Qt.white)
        self.setPalette(pa)

        # 设置文字位置
        self.setAlignment(Qt.AlignCenter)

        # 设置背景颜色\圆角和文本内容
        if self.number == 0:
            self.setStyleSheet("background-color:white;border-radius:10px;")
        else:
            self.setStyleSheet("background-color:blue;border-radius:10px;")
            self.setText(str(self.number))

#########################################
app = QApplication(sys.argv)
ex = NumberHuaRong()
start = ex.blocks
start.append(0)
start.append(0)
target = [[1, 2, 3], [4, 5, 6], [7, 8, 0], -1]


def evaluate(state):
    global target
    f = 0
    for i in range(3):
        for j in range(3):
            if state[i][j] != target[i][j]:
                f += 1
    return f


def findSpace(state):
    for i in range(3):
        for j in range(3):
            if state[i][j] == 0:
                return i, j


def expand(state, t):
    x = findSpace(state)[0]
    y = findSpace(state)[-1]
    expState = []
    # up
    if x-1 >= 0 and t == 0:
        up = copy.deepcopy(state)
        up[x-1][y], up[x][y] = up[x][y], up[x-1][y]
        expState.append(up)
        return up
    if x+1 <= 2 and t == 1:
        down = copy.deepcopy(state)
        down[x+1][y], down[x][y] = down[x][y], down[x+1][y]
        expState.append(down)
        return down
    if y-1 >= 0 and t == 2:
        left = copy.deepcopy(state)
        left[x][y-1], left[x][y] = left[x][y], left[x][y-1]
        expState.append(left)
        return left
    if y+1 <= 2 and t == 3:
        right = copy.deepcopy(state)
        right[x][y+1], right[x][y] = right[x][y], right[x][y+1]
        expState.append(right)
        return right


def findBestID():
    global openValue
    tmin = 10
    flag = 0
    for i in range(len(openList)):
        if openList[i][-1] == 0 and openValue[i] < tmin:
            tmin = openValue[i]
            flag = i
    return flag


def judgeSame(state1, state2):
    for i in range(3):
        for j in range(3):
            if state1[i][j] != state2[i][j]:
                return False
    return True


def evaOpenValue():
    global openList
    global openValue
    openValue = []
    for i in range(len(openList)):
        temp = evaluate(openList[i])
        openValue.append(temp)

def evaCloseValue():
    global closeList
    global closeValue
    closeValue = []
    for i in range(len(closeList)):
        temp = evaluate(closeList[i])
        closeValue.append(temp)


openList = []
openValue = []
closeList = []
closeValue = []
openList.append(start)

tarID = 0
while openList is not None:
    evaOpenValue()
    minID = findBestID()
    if judgeSame(openList[minID], target):
        print('ok')
        tarID = minID
        break
    for t in range(4):
        expTemp = expand(openList[minID], t)
        if expTemp is not None:
            evai = evaluate(expTemp)

            expTemp[3] = minID
            expTemp[-1] = 0
            flag1 = 0
            for j in range(len(openList)):
                if judgeSame(expTemp, openList[j]) and openList[j][-1] == 0:
                    flag1 = 1
                    break
            flag2 = 0
            for j in range(len(closeList)):
                if judgeSame(expTemp, closeList[j]):
                    flag2 = 1
                    break
            if flag2 == 0 and flag1 == 0:
                openList.append(expTemp)
                openValue.append(evai)
    closeList.append(openList[minID])
    openList[minID][-1] = 1


temp = openList[tarID]

while temp[-2] != 0:
    for i in range(3):
        for j in range(3):
            print(temp[i][j], end=' ')
        print('\n')
    print('---------')
    solution.append(temp[:3])
    temp = openList[temp[-2]]


solution.append(temp[:3])
print(temp[:3])
solution.append(start[:3])
print(start[:3])
solutionStep = len(solution) - 1
print(solution)
sys.exit(app.exec_())

效果如顶部的图所示

注意GUI部分方向键自己控制,空格键利用算法自动走。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-01-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
A搜索算法(python)之八数码问题
评估函数f(x)定义为:从初始节点S0出发,约束地经过节点X到达目标节点Sg的所有路径中最小路径代价的估计值。 其一般形式为f(x)=g(x)+h(x),g(x)表示从初始节点S0到节点X的实际代价;h(x)表示从X到目标节点Sg的最优路径的估计代价。
李小白是一只喵
2020/04/24
5K0
看了《最强大脑》,我决定做这个游戏
今年年初,新一季的《最强大脑》开播了,第一集选拔的时候大家做了一个数字游戏,名叫《数字华容道》,当时何猷君以二十几秒的成绩夺得该项目的冠军,来看一下当时的比赛:
王强
2018/08/09
1.3K0
看了《最强大脑》,我决定做这个游戏
A*算法之八数码问题 python解法
人工智能课程中学习了A*算法,在耗费几小时完成了八数码问题和野人传教士问题之后,决定写此文章来记录一下,避免忘记
全栈程序员站长
2022/07/22
3.2K0
A*算法之八数码问题 python解法
python解决八数码问题
八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始状态转变成目标状态的移动棋子步数最少的移动步骤 一开始也是两眼一抹黑,连八数码是什么都不知道,经过度娘得到如上结果。那该如何实现呢?如果移动数字的话,8个数字,每次移动有4种选择,那就是32个种移动方案。那移动空格就只有4种选择,一下子清楚了很多。至于存储方案当然是数组了,交换起
听城
2018/04/27
2.6K0
A*算法解决八数码问题
八数码游戏包括一个33的棋盘,棋盘上摆放着8个数字的棋子,留下一个空位。与空位相邻的棋子可以滑动到空位中。游戏的目的是要达到一个特定的目标状态。标注的形式化如下:
全栈程序员站长
2022/07/23
1.6K0
【小白学游戏常用算法】二、A*启发式搜索算法
  在上一篇博客中,我们一起学习了随机迷宫算法,在本篇博客中,我们将一起了解一下寻路算法中常用的A*算法。
马三小伙儿
2018/09/12
1.2K0
【小白学游戏常用算法】二、A*启发式搜索算法
Python游戏开发,pygame模块,Python实现2048小游戏
"使用方向键移动方块,两个数字相同的方块撞在一起后,将会合并为一个数字是原来两倍的新方块。游戏的时候尽可能多地合并这些数字方块就行了。" 大概了解了游戏规则之后,我们就可以开始写这个游戏啦~首先,进行一下游戏初始化操作并播放一首自己喜欢的游戏背景音乐:
玖柒的小窝
2021/12/14
1.1K0
Python游戏开发,pygame模块,Python实现2048小游戏
人工智能大作业—-八数码问题
加深对搜索策略的理解,尤其是对启发式搜索的基本原理的理解,使学生能够通过编程实现图搜索的基本方法和启发式搜索算法,并能够解决一些应用问题。
全栈程序员站长
2022/09/14
2K0
多种方法求解八数码问题
AI的实验报告,改了改发上来。希望路过的大牛不吝赐教。非常是纳闷我的ida*怎么还没有双搜快。还有发现基于不在位启示的A*和Ida*都挺慢。尤其是ida* 搜索31步的竟然要十几秒。是我写的代码有问题吗?忘路过的大牛指导啊!!!!
全栈程序员站长
2022/07/13
8030
多种方法求解八数码问题
使用C++解决八数码问题
问题描述:通过单步移动把下面的矩阵移动成1-8环绕一周的矩阵(即0在中间,1-8顺序排成一圈,1在哪无所谓)
全栈程序员站长
2022/09/14
7100
80道高频算法题Python版
掌握这80道题,99%的测试岗位算法考试都能通过。建议收藏后反复练习。本文为Python版本答案,对于Java版本答案,请在电子书《算法挑战》目录中查看。
dongfanger
2023/09/30
8750
80道高频算法题Python版
【说站】python A*算法是什么
2、A*算法是启发式算法,采用最佳优先搜索策略(Best-first),基于评估函数对每个搜索位置的评估结果,猜测最佳优先搜索位置。
很酷的站长
2022/11/24
5470
【说站】python A*算法是什么
7种方法求解八数码问题
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
全栈程序员站长
2022/07/22
1.2K0
7种方法求解八数码问题
路径规划算法 | A* 搜索算法
动机:为了在现实生活中近似求解最短路径,例如地图、游戏等存在许多障碍物的情况。我们可以考虑一个含有多个障碍物的二维网格图,我们从起始单元格(下方红色标记)开始,朝着目标单元格(下方绿色标记)前进。
一点人工一点智能
2024/04/24
4040
路径规划算法 | A* 搜索算法
DES算法的python实现
最后的结果实际上也是存在一些问题,在个人后面的验证中也没有找清楚问题出在了哪里?但是大致思路应该没问题
十二惊惶
2024/02/28
1630
A*(A星)算法Go lang实现
package main import ( "container/heap" "fmt" "math" "strings" ) import "strconv" type OpenList []*_AstarPoint func (self OpenList) Len() int { return len(self) } func (self OpenList) Less(i, j int) bool { return self[i].fVal < sel
大师级码师
2021/11/01
4290
python-剑指offer41-62
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
西西嘛呦
2020/08/26
4760
a算法解决八数码实验报告_人工智能核心算法
熟悉和掌握启发式搜索的定义、估价函数和算法过程,并利用A*算法求解N数码难题,理解求解流程和搜索顺序。
全栈程序员站长
2022/11/17
3.7K0
a算法解决八数码实验报告_人工智能核心算法
数列排序算法总结(Python实现)
非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序。
用户7886150
2021/01/14
5720
八数码问题高效算法-HDU 1043
八数码问题是bfs中的经典问题,经常也会遇到与其相似的题目。用到的思想是bfs+hash;主要是由于状态分散,无法直接用一个确定的数表示。所以导致bfs时,无法去判断一个状态是否已经被搜过。也无法用d数组去求解。这个时候就需要用到hash的方法判断当前状态是否已经被搜过。并按照搜索的顺序给每个状态编号(用这个编号代替对应的状态,与状态一一对应,为了求d[]),将所有的状态存起来,供hash查找。
ACM算法日常
2018/08/07
5630
八数码问题高效算法-HDU 1043
相关推荐
A搜索算法(python)之八数码问题
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档