二阶魔方还原 Rubik’s Cube 双向广度优先搜索

1. 算法简介

        使用搜索算法完成二阶魔方从任意初始状态向目标状态的操作转换。
        根据已有的研究,二阶魔方的上帝之数为11(进行FTM计数)或14(进行QTM计数),本算法采用QTM计数对魔方进行双向广度优先搜索,得到了快速的运行速度和准确的结果。
QTM:Quarter turn metric的略称,是3阶魔方里面转动步数的一种计数方法,无论是转动单层或双层,转动90度算作一步、转动180度算两步,但单独转动中间层要加倍。 比如:r2 U R' U' M U R U' R' r'共12步。
HTM:Half turn metric的略称(也叫FTM,face turn metric),是3阶魔方里面转动步数的一种计数方法,即转动单层或双层时,无论是转动90度或者180度都算作一步,但单独转动中间层要加倍,HTM是主流计数方法。比如r2 U R' U' M U R U' R' r'共11步。

2. 使用说明 

        1、按照附录中的格式,按数字0~23的顺序,3个颜色一组输入魔方每个面的颜色,简化输入为red-r、green-g、blue-b、orange-o、white-w、yellow-y,
例如,“r g b”(两个字母之间包含空格)。
        2、因为本算法不涉及对魔方整体空间位置旋转的操作,所以输入前必须调整魔方位置,保证两组状态中[18][19][20]三个面的颜色完全相同,如上图所示。
        3、每次输入一组3个颜色,程序会自动判断输入合法性,如果输入不合法,则会显示错误,需重新输入该组数据;每次输入一个魔方24个颜色后,程序会自动判断输入合法性,如果输入不合法,则会显示错误,需重新输入该魔方数据。
        4、对于正确的输入,程序会得到初始状态向目标状态的操作步骤,例如
        The solution to this cube is:
        1U 1f 1f 1r 1F 1u 2R 2f 2U 2U 2R 2u
其代表的含义见“3(4)魔方的六种基本操作”,字母前的数字仅代表前向和后向bfs,应忽略。需要注意的是,操作时手持魔方的状态应和输入时参考的展开图状态一致,即U对应操作魔方的U层面。
        对于无解的输入,如魔方进行过转角、魔方色块分布无法仅通过旋转得到等,程序不会无限搜索下去,而是在双向各搜索8层却无解后,输出"There is no solution to this cube, please check your input.",并退出程序。

3. 结构说明

(1)输入魔方初始状态、目标状态
以编号“18、19、20”的三个面的位置及颜色为基准,如上图所示展开魔方,并按顺序填入各编号内魔方的颜色,red-r、green-g、blue-b、orange-o、white-w、yellow-y
(2)魔方状态表示转换
(3)计算魔方状态对应数值
每一个色块(共24个)看成6进制数的一位,那么一个24位的6进制数就可以表示二阶魔方的唯一状态。将六种颜色定义为012345,那么可计算出魔方状态对应的唯的十进制数值。
(4)魔方的六种基本操作
上面顺时针90°(U),上面逆时针90°(u)
前面顺时针90°(F),前面逆时针90°(f)
右面顺时针90°(R),右面逆时针90°(r)
(5)双向广度优先搜索
(6)操作步骤生成&输出

4. 问题说明

1、编码时间有限,在魔方旋转操作函数和双向广度优先算法核心代码段的函数DOUBFS_FORWARD()和DOUBFS_BACKWARD()中,使用了相同语句结构的复制粘贴,未封装简化代码结构。

5.源代码

import numpy as np
import copy
import sys

######输入魔方初始状态、目标状态######
#以编号“18、19、20”的三个面的位置及颜色为基准,如上图所示展开魔方,并按顺序填入各编号内魔方的颜色
#red-r、green-g、blue-b、orange-o、white-w、yellow-y

color = ['r', 'b', 'g', 'o', 'w', 'y']       #颜色列表
count = 0
init = np.zeros((8, 3), dtype='string_')     #建立8行3列矩阵存储魔方初始状态
target = np.zeros((8, 3), dtype='string_')   #建立8行3列矩阵存储魔方目标状态

def input_init():#输入初始状态函数
    global count
    count = 0
    for i in range(8):
        print('The color of cube[%d][%d][%d]:' % (3*i, 3*i+1, 3*i+2))
        value = [temp for temp in input().split()]
        while input_error(value):
            print('Invalid state, please check your input.')
            value = [temp for temp in input().split()]
        init[i,0] = value[0]
        init[i,1] = value[1]
        init[i,2] = value[2]
    if count != 60:
        print("Invalid initial state, please check the input and retry.")
        input_init()
    else:
        print("Initial state is above.")
        
def input_target():#输入目标状态函数
    global count
    count = 0
    for i in range(8):
        print('The color of cube[%d][%d][%d]:' % (3*i, 3*i+1, 3*i+2))
        value = [temp for temp in input().split()]
        while input_error(value):
            print('Invalid state, please check your input.')
            value = [temp for temp in input().split()]
        target[i,0] = value[0]
        target[i,1] = value[1]
        target[i,2] = value[2]
    if count != 60:
        print("Invalid initial state, please check the input.")
        input_target()
    else:
        print("Target state is above.")

def input_error(x):#输入状态合法性判断函数
    global count 
    if len(x) < 3:
        return True
    for ch in x:
        if ch in color:
            if ch == "r":
                count += 0
            if ch == "g":
                count += 1
            if ch == "b":
                count += 2
            if ch == "o":
                count += 3        
            if ch == "w":
                count += 4
            if ch == "y":
                count += 5
        else:
            return True
    return False
        
input_init()
input_target()

######魔方状态表示转换######
init_state = np.zeros((8, 3), dtype='i')
target_state = np.zeros((8, 3), dtype='i')

for i in range(8):#将初始状态字符转换为整型数值0-5
    for j in range(3):
        if init[i][j] == b'r':
            init_state[i][j] = 0
        elif init[i][j] == b'g':
            init_state[i][j] = 1
        elif init[i][j] == b'b':
            init_state[i][j] = 2
        elif init[i][j] == b'o':
            init_state[i][j] = 3  
        elif init[i][j] == b'w':
            init_state[i][j] = 4
        elif init[i][j] == b'y':
            init_state[i][j] = 5

for i in range(8):#将初始状态字符转换为整型数值0-5
    for j in range(3):
        if target[i][j] == b'r':
            target_state[i][j] = 0
        elif target[i][j] == b'g':
            target_state[i][j] = 1
        elif target[i][j] == b'b':
            target_state[i][j] = 2
        elif target[i][j] == b'o':
            target_state[i][j] = 3  
        elif target[i][j] == b'w':
            target_state[i][j] = 4
        elif target[i][j] == b'y':
            target_state[i][j] = 5

######计算魔方状态对应数值######
#每一个色块(共24个)看成6进制数的一位,那么一个24位的6进制数就可以表示二阶魔方的唯一状态。将六种颜色定义为012345,那么可计算出魔方状态对应的唯的十进制数值。
def cal(temp):#计算魔方状态对应的数值
    value = np.longlong(0)
    n = 0
    for i in range(8):
        for j in range(3):
            value += pow(temp[i][j],n)
            n +=1
    return value

init_value = cal(init_state)
target_value = cal(target_state)

######魔方的六种基本操作######
'''上面顺时针90°(U),上面逆时针90°(u)
前面顺时针90°(F),前面逆时针90°(f)
右面顺时针90°(R),右面逆时针90°(r)'''

def Rotate_U(state):#上面顺时针90
    temp = copy.deepcopy(state)
    temp[0][1] = state[1][0]  #1-->3
    temp[4][2] = state[0][0]  #14-->0
    temp[1][0] = state[5][1]  #3-->16
    temp[0][0] = state[1][2]  #0-->5
    temp[5][1] = state[4][0]  #16-->12
    temp[1][2] = state[5][0]  #5-->15
    temp[4][0] = state[0][1]  #12-->1
    temp[5][0] = state[4][2]  #15-->14
    
    temp[0][2] = state[1][1]  #2-->4
    temp[1][1] = state[5][2]  #4-->17
    temp[5][2] = state[4][1]  #17-->13
    temp[4][1] = state[0][2]  #13-->2
    return temp


def Rotate_u(state):#上面逆时针90
    temp = copy.deepcopy(state)
    temp[0][1] = state[4][0]  #1-->12
    temp[4][2] = state[5][0]  #14-->15
    temp[1][0] = state[0][1]  #3-->1
    temp[0][0] = state[4][2]  #0-->14
    temp[5][1] = state[1][0]  #16-->3
    temp[1][2] = state[0][0]  #5-->0
    temp[4][0] = state[5][1]  #12-->16
    temp[5][0] = state[1][2]  #15-->5
    
    temp[0][2] = state[4][1]  #2-->13
    temp[4][1] = state[5][2]  #13-->17
    temp[5][2] = state[1][1]  #17-->4
    temp[1][1] = state[0][2]  #4-->2

    return temp


def Rotate_F(state):#前面顺时针90,已化简操作
    temp = copy.deepcopy(state)
    temp[0] = state[2]
    temp[1] = state[0]
    temp[2] = state[3]
    temp[3] = state[1]
    return temp


def Rotate_f(state):#前面逆时针90,已化简操作
    temp = copy.deepcopy(state)
    temp[2] = state[0]
    temp[0] = state[1]
    temp[3] = state[2]
    temp[1] = state[3]
    return temp


def Rotate_R(state):#右面顺时针90
    temp = copy.deepcopy(state)
    temp[5][2] = state[1][0]  #17-->3
    temp[1][1] = state[3][0]  #4-->9
    temp[1][0] = state[3][2]  #3-->11
    temp[3][0] = state[7][1]  #9-->22
    temp[3][2] = state[7][0]  #11-->21
    temp[7][1] = state[5][0]  #22-->15
    temp[7][0] = state[5][2]  #21-->17
    temp[5][0] = state[1][1]  #15-->4
    
    temp[1][2] = state[3][1]  #5-->10
    temp[3][1] = state[7][2]  #10-->23
    temp[7][2] = state[5][1]  #23-->16
    temp[5][1] = state[1][2]  #16-->5    
    return temp


def Rotate_r(state):#右面逆时针90
    temp = copy.deepcopy(state)
    temp[1][0] = state[5][2]  #3-->17
    temp[3][0] = state[1][1]  #9-->4
    temp[3][2] = state[1][0]  #11-->3
    temp[7][1] = state[3][0]  #22-->9
    temp[7][0] = state[3][2]  #21-->11
    temp[5][0] = state[7][1]  #15-->22
    temp[5][2] = state[7][0]  #17-->21
    temp[1][1] = state[5][0]  #4-->15
    
    temp[3][1] = state[1][2]  #10-->5
    temp[1][2] = state[5][1]  #5-->16
    temp[5][1] = state[7][2]  #16-->23
    temp[7][2] = state[3][1]  #23-->10
    return temp

######双向广度优先搜索######
list_forward=[init_state]            #前向广度优先搜索队列
list_backward=[target_state]         #后向广度优先搜索队列
visited_forward={init_value:[]}      #前向广度优先搜索字典(存储已检索状态)
visited_backward={target_value:[]}   #后向广度优先搜索字典(存储已检索状态)

def DOUBFS_FORWARD():#前向广度优先搜索函数

    value = np.longlong(0)
    current = list_forward.pop(0)                    #当前首节点出队列
    value = cal(current)                             #计算特征值
    step = copy.deepcopy(visited_forward[value])     #暂存路径
    
    if len(step) == 0 or step[-1] != '1u':
        temp1 = copy.deepcopy(Rotate_U(current))     #旋转操作U
        list_forward.append(temp1)                   #状态入队列
        value = cal(temp1)                           #计算特征值
        visited_forward[value] = copy.deepcopy(step) #存历史步骤
        visited_forward[value].append('1U')          #存当前步骤

    if len(step) == 0 or step[-1] != '1U':    
        temp2 = copy.deepcopy(Rotate_u(current))     #旋转操作u
        list_forward.append(temp2)                   #状态入队列
        value = cal(temp2)                           #计算特征值
        visited_forward[value] = copy.deepcopy(step) #存历史步骤
        visited_forward[value].append('1u')          #存当前步骤
    
    if len(step) == 0 or step[-1] != '1f': 
        temp3 = copy.deepcopy(Rotate_F(current))     #旋转操作F
        list_forward.append(temp3)                   #状态入队列
        value = cal(temp3)                           #计算特征值
        visited_forward[value] = copy.deepcopy(step) #存历史步骤
        visited_forward[value].append('1F')          #存当前步骤
    
    if len(step) == 0 or step[-1] != '1F': 
        temp4 = copy.deepcopy(Rotate_f(current))     #旋转操作f
        list_forward.append(temp4)                   #状态入队列
        value = cal(temp4)                           #计算特征值
        visited_forward[value] = copy.deepcopy(step) #存历史步骤
        visited_forward[value].append('1f')          #存当前步骤
    
    if len(step) == 0 or step[-1] != '1r': 
        temp5 = copy.deepcopy(Rotate_R(current))     #旋转操作R
        list_forward.append(temp5)                   #状态入队列
        value = cal(temp5)                           #计算特征值
        visited_forward[value] = copy.deepcopy(step) #存历史步骤
        visited_forward[value].append('1R')          #存当前步骤
    
    if len(step) == 0 or step[-1] != '1F': 
        temp6 = copy.deepcopy(Rotate_r(current))     #旋转操作r
        list_forward.append(temp6)                   #状态入队列
        value = cal(temp6)                           #计算特征值
        visited_forward[value] = copy.deepcopy(step) #存历史步骤
        visited_forward[value].append('1r')          #存当前步骤


def DOUBFS_BACKWARD():#后向广度优先搜索函数
    value = np.longlong(0)
    current = list_backward.pop(0)                   #当前首节点出队列
    value = cal(current)                             #计算特征值
    step = copy.deepcopy(visited_backward[value])    #暂存路径
    
    if len(step) == 0 or step[-1] != '2u':
        temp1 = copy.deepcopy(Rotate_U(current))     #旋转操作U
        list_backward.append(temp1)                  #状态入队列
        value = cal(temp1)                           #计算特征值    
        visited_backward[value] = copy.deepcopy(step)#存历史步骤
        visited_backward[value].append('2U')         #存当前步骤
        if value in visited_forward:
            return value
        
    if len(step) == 0 or step[-1] != '2U':
        temp2 = copy.deepcopy(Rotate_u(current))     #旋转操作u
        list_backward.append(temp2)                  #状态入队列
        value = cal(temp2)                           #计算特征值    
        visited_backward[value] = copy.deepcopy(step)#存历史步骤
        visited_backward[value].append('2u')         #存当前步骤
        if value in visited_forward:
            return value
        
    if len(step) == 0 or step[-1] != '2f':
        temp3 = copy.deepcopy(Rotate_F(current))     #旋转操作F
        list_backward.append(temp3)                  #状态入队列
        value = cal(temp3)                           #计算特征值    
        visited_backward[value] = copy.deepcopy(step)#存历史步骤
        visited_backward[value].append('2F')         #存当前步骤
        if value in visited_forward:
            return value
        
    if len(step) == 0 or step[-1] != '2F':
        temp4 = copy.deepcopy(Rotate_f(current))     #旋转操作f
        list_backward.append(temp4)                  #状态入队列
        value = cal(temp4)                           #计算特征值    
        visited_backward[value] = copy.deepcopy(step)#存历史步骤
        visited_backward[value].append('2f')         #存当前步骤
        if value in visited_forward:
            return value
        
    if len(step) == 0 or step[-1] != '2r':
        temp5 = copy.deepcopy(Rotate_R(current))     #旋转操作R
        list_backward.append(temp5)                  #状态入队列
        value = cal(temp5)                           #计算特征值    
        visited_backward[value] = copy.deepcopy(step)#存历史步骤
        visited_backward[value].append('2R')         #存当前步骤
        if value in visited_forward:
            return value
        
    if len(step) == 0 or step[-1] != '2R':
        temp6 = copy.deepcopy(Rotate_r(current))     #旋转操作r
        list_backward.append(temp6)                  #状态入队列
        value = cal(temp6)                           #计算特征值    
        visited_backward[value] = copy.deepcopy(step)#存历史步骤
        visited_backward[value].append('2r')         #存当前步骤
        if value in visited_forward:
            return value
        
    return False

if init_value == target_value:      #无需旋转
    print("No need to rotate.")
else:
    while(1):
        meet = False                #搜索路径相遇判断清零
        if len(list_forward) > len(list_backward):  #先搜索较短队列
            meet = DOUBFS_BACKWARD()                #返回搜索路径相遇情况
        else:
            DOUBFS_FORWARD()

        if meet:                     #搜索路径相遇,退出循环
            break

        z = len(visited_forward[cal(list_forward[0])])    #当前前向搜索层数
        m = len(visited_backward[cal(list_backward[0])])  #当前后向搜索层数

        if z >= 8 and m >= 8 :      #两个list的第一个元素len >= 8(双向均进行了8次及以上转动),退出循环
            print("There is no solution to this cube, please check your input.")
            sys.exit(0)

######操作步骤生成&输出######
step_list = []    #建立操作步骤列表

i = len(visited_forward[meet]) - 1
j = 0

while(j <= i):   #将正向搜索结果顺序存入操作步骤列表
    step_list.append(visited_forward[meet][j])   
    j = j + 1

i = len(visited_backward[meet]) - 1

while(i >= 0):       #将后向路径操作反转,得到正向操作,并存入操作步骤列表
    if visited_backward[meet][i] == '2U':
        step_list.append('2u')
    elif visited_backward[meet][i] == '2u':
        step_list.append('2U')
    elif visited_backward[meet][i] == '2F':
        step_list.append('2f')    
    elif visited_backward[meet][i] == '2f':
        step_list.append('2F')
    elif visited_backward[meet][i] == '2R':
        step_list.append('2r')
    elif visited_backward[meet][i] == '2r':
        step_list.append('2R')
    i = i - 1
    
    
print("The solution to this cube is:")
print(" ".join(step_list))

声明

全部程序均独立编码完成,魔方状态的表示方式参考自博客:
QT C++ 算法 广搜BFS 最下步数复原二阶魔方
https://blog.csdn.net/weixin_38640921/article/details/89513868

仅供学习交流,代码风格较差,望勿借鉴

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值