八数码问题的算法实现(python)


# coding: utf-8

# In[1]:


def reversenum(node):
    """计算状态对应的逆序数,奇偶性一致则有解"""
    Sum = 0
    for i in range(1,9):
        num = 0
        for j in range(0,i):
          if node[j]>node[i] and node[i] != '0':
              num = num + 1
        Sum += num
    return Sum

def Hn(node):
    """h(n)函数,用于计算估价函数f(n),这里的h(n)选择的是与目标状态相比错位的数目"""
    global goal
    hn = 0
    for i in range(0,9):
        if node[i] != goal[i]:
            hn += 1
    return hn
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
def expand_node(node):
    """拓展node状态对应的子结点"""
    global expand
    tnode = []
    state = node.index("0")  # 返回"0"的位置
    elist = expand[state]  # 得知0可以移动的情况
    j = state
    for i in elist:
        j = state
        if i>j:
            i,j  =  j,i
        re =  node[:i] + node[j] + node[i+1:j] + node[i] + node[j+1:]  # 用切片拼出子节点
        tnode.append(re)
#     print(tnode)
    return tnode

def print_step(result):
    """将最后的结果按格式输出"""
    for i in range(len(result)):
            print("step--" + str(i+ 1))
            print(result[i][:3])
            print(result[i][3:6])
            print(result[i][6:])

def select_min(opened):
    """选择opened表中的最小的估价函数值对应的状态"""
    fn_dict = {}  # 字典
    for node in opened:
        fn = Fn[node]  
        fn_dict[node] = fn
    min_node = min(fn_dict, key = fn_dict.get) # 获得字典fn_dict中value的最小值所对应的键
    return min_node

def a_star(start, goal):
    if start == goal:
        print("初始状态和目标状态一致!")
        
    # 判断从初始状态是否可以达到目标状态
    if (reversenum(start)%2) != (reversenum(goal)%2):
        print("该目标状态不可达!")
        return None
        
    else:
        parent[start] = -1                # 初始结点的父结点存储为-1
        Gn[start] = 0                     # 初始结点的g(n)为0
        Fn[start] = Gn[start] + Hn(start)  # 计算初始结点的估价函数值 f(n)  =  g(n) + h(n)

        while opened:
            current = select_min(opened)  # 选择估价函数值最小的状态
            del Fn[current]              # 对代价清零
            opened.remove(current)       # 将要遍历的结点取出opened表

            if current == goal:
                break
                
            if current not in closed:
                closed.append(current)     # 存入closed表 
                Tnode = expand_node(current)    # 扩展子结点.用来遍历节点n所有的邻近节点
                for node in Tnode:
                    # 如果子结点在opened和closed表中都未出现,则存入opened表
                    # 并求出对应的估价函数值
                    if node not in opened and node not in closed:
                        Gn[node] = Gn[current]+1
                        Fn[node] = Gn[node]+Hn(node)
                        parent[node] = current
                        opened.append(node)
                    else:
                        # 若子结点已经在opened表中,则判断估价函数值更小的一个路径
                        # 同时改变parent字典和Fn字典中的值
                        if node in opened:
                            fn = Gn[current] + 1 + Hn(node)
                            if fn < Fn[node]:
                                Fn[node] = fn
                                parent[node] = current

        result = []  # 用来存放路径
        result.append(current)
        
        while parent[current] != -1:  # 根据parent字典中存储的父结点提取路径中的结点
            current  = parent[current]
            result.append(current)
        result.reverse()  # 逆序即为运行时的过程
        return result

if __name__ == "__main__":  
    # expand中存储的是九宫格中每个位置对应的可以移动的情况
    # 当定位了0的位置就可以得知可以移动的情况
    expand = {0:[1, 3],
              1:[0, 2, 4],
              2:[1, 5],
              3:[0,4,6], 
              4:[3,1,5,7], 
              5:[4,2,8],
              6:[3,7],  
              7:[6,4,8], 
              8:[7,5]}

    start = input("请输入初始状态(从左至右,从上到下,如:153246708):")
    goal  = input("请输入目标状态(从左至右,从上到下,如:123456780):")
    
    # 初始化
    opened = [start]
    closed = []
    Fn = {}  # 状态对应的估价函数值 f(n)  =  g(n) + h(n)
    Gn = {}  # 初始结点到当前结点n的实际代价,即路径长度
    parent = {}  # 用来存储状态对应的父结点
    
    result = a_star(start, goal)
    if result != None:
        print_step(result)# 按格式输出结果

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值