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
仅供学习交流,代码风格较差,望勿借鉴