八數碼問題的三種解法

北民大人工智能作業,其他相關作業資料和問題可留言交流
  這裏沒有用課本上的open表和close表的方式,這個問題完全可以看圖找規律解出來,而且三個代碼(廣度、A、A*)沒有多少區別,代碼相似度極高,將每個操作步驟抽成方法後A和A*就只有一個入隊規則不一樣而已,廣度就是直接去掉這個規則。

  這裏的規律就是根據課本上給出的啓發公式(格式爲F=A+B;A是棋盤所在的深度;B在A算法中是不在位的元素個數,在A*算法中就是所有元素要移動的步數和)來設計入隊規則,因爲同一層上的棋盤一起考慮,所以落實到代碼中A部分的深度屬性就沒用了,因爲同一層上的這個參數都是一樣的,只需考慮參數B就行。另外就是從第二層上的棋盤開始,往下的分支會出現它父節點的父節點棋盤狀態,這個要剔除掉,否則會陷入無限的循環當中。

  關於自己代碼的評價:優點是便於理解,更貼合初學者或者是沒有參考課本思路的人的想法;個人覺得比課本上的open表close表的方式要簡單的多,其次目前A和A算法運行效率要比網上用open、close表的代碼強非常多。缺點是:在最初的設想時沒有考慮周全,棋盤的表示已經確定,但是發現棋盤分支會出現它父節點的父節點棋盤狀態時,想用回溯的方法就需要大的改動,所以乾脆用一個列表存儲所有出現過的棋盤,後續棋盤狀態不允許出現列表中已有的棋盤,以此來回避回溯問題,這就帶來了效率上的漏洞,A和A因爲有入隊條件限制,所以感覺不出問題,但是放到廣度算法裏邊,有些棋盤狀態的演算,跑了14個小時依然沒有結果,這個問題回隨着演算步驟的加深瘋狂的放大。解決辦法自然是使用回溯方式,爲每個棋盤增加一個父節點屬性,用來存儲父棋盤,每次只要根據棋盤的父節點屬性找到上兩步的棋盤與之對比即可,就無需比對所有出現過的棋盤了。

  這裏先給出三個實現代碼,下邊再附上課本上的三幅圖已經這三個代碼大致的流程走向圖。

廣度算法

import numpy as np
import copy
import time

"""
1. 空洞用數字0表示,其他各個位置用1...8表示
2. 同一層deep都一樣,所以直接不考慮
3. 注意深淺拷貝問題
4. 注意閉包問題
5. 注意stack不是棧,是隊列queue,temp是step代表步數,懶得改了
6. print("this state no answer!")已經沒用了,最初是驗證是否會出現沒解的情況,現在有了judge_have_answer()方法
7. 使用規則:輸入初始和最終棋盤元素,橫着從左到右挨着輸入,空洞爲0,其他爲1...8
8. 注意日期打印時.format()方式不能使用
"""

final_checkerboard = np.zeros((3, 3), dtype=int)  # 記錄最終的棋盤狀態
total_stack = []
final_stack = []
mid_stack = []
step = 0
start_time = 0

def create_checkerboard(checkerboard):
    order = 0
    for i in range(3):
        for j in range(3):
            order += 1
            element = int(input("請輸入第{}個數:".format(order)))
            checkerboard[i][j] = element

def move_checkerboard(before_state):
    global mid_stack

    # 首先確定空洞的位置
    position = np.argwhere(before_state == 0)
    first_position = position[0][0]
    second_position = position[0][1]

    if first_position == 0:
        if second_position == 0:
            zuoyi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)
        elif second_position == 2:
            youyi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)
        else:
            youyi(before_state, first_position, second_position)
            zuoyi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)
    elif first_position == 2:
        if second_position == 0:
            xiayi(before_state, first_position, second_position)
            zuoyi(before_state, first_position, second_position)
        elif second_position == 2:
            youyi(before_state, first_position, second_position)
            xiayi(before_state, first_position, second_position)
        else:
            youyi(before_state, first_position, second_position)
            xiayi(before_state, first_position, second_position)
            zuoyi(before_state, first_position, second_position)
    else:
        if second_position == 0:
            zuoyi(before_state, first_position, second_position)
            xiayi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)
        elif second_position == 2:
            xiayi(before_state, first_position, second_position)
            youyi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)
        else:
            youyi(before_state, first_position, second_position)
            xiayi(before_state, first_position, second_position)
            zuoyi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)

def equal_all_position(first_checkerboard):
    for t in total_stack:
        num = 0
        for i in range(3):
            for j in range(3):
                if first_checkerboard[i][j] == t[i][j]:
                    num += 1
        if num == 9:
            return 0
        else:
            pass

    return 1


def shangyi(checkerboard, first_position, second_position):
    after_state = copy.deepcopy(checkerboard)
    after_state[first_position][second_position] = after_state[first_position + 1][second_position]  # 數字上移
    after_state[first_position + 1][second_position] = 0
    tap = equal_all_position(after_state)
    if tap:
        statistics(after_state)  # 這裏保留是爲了判斷是否達到最終狀態
        ruzhan(after_state)

def xiayi(checkerboard, first_position, second_position):
    after_state = copy.deepcopy(checkerboard)
    after_state[first_position][second_position] = after_state[first_position -1][second_position]  # 數字下移
    after_state[first_position - 1][second_position] = 0
    tap = equal_all_position(after_state)
    if tap:
        statistics(after_state)
        ruzhan(after_state)

def zuoyi(checkerboard, first_position, second_position):
    after_state = copy.deepcopy(checkerboard)
    after_state[first_position][second_position] = after_state[first_position][second_position + 1]  # 數字左移
    after_state[first_position][second_position + 1] = 0
    tap = equal_all_position(after_state)
    if tap:
        statistics(after_state)
        ruzhan(after_state)

def youyi(checkerboard, first_position, second_position):
    after_state = copy.deepcopy(checkerboard)
    after_state[first_position][second_position] = after_state[first_position][second_position - 1]  # 數字右移
    after_state[first_position][second_position - 1] = 0
    tap = equal_all_position(after_state)
    if tap:
        statistics(after_state)
        ruzhan(after_state)

def ruzhan(checkerboard):
    global mid_stack
    global step
    step += 1
    print(step)
    mid_stack.append(checkerboard)
    total_stack.append(checkerboard)

    print(checkerboard)
    print("====================")

# 用來統計當前棋盤有多少個元素不在目標位置
def statistics(checkboard):
    number = 0

    for i in range(3):
        for j in range(3):
            if checkboard[i][j] == final_checkerboard[i][j]:
                number += 1

    if number == 9:
        print("最終結果爲:")
        print(checkboard)
        end_time = time.time()
        print("end_time=",end_time)
        global start_time
        print("用時=",end_time - start_time)
        exit()
    return 9 - number

def iterator_all_elements(final_stack):
    for i in final_stack:
        move_checkerboard(i)

# 這裏的教訓告訴我們沒事不要瞎用不熟悉的庫,最後還是numpy轉list
def judge_have_answer(initial_checkerboard):
    initial_checkerboard_result = 0
    final_checkerboard_result = 0

    initial_checkerboard_backup = copy.deepcopy(initial_checkerboard)
    final_checkerboard_backup = copy.deepcopy(final_checkerboard)

    new_initial = initial_checkerboard_backup.reshape((1,9)).tolist()[0]
    new_final = final_checkerboard_backup.reshape((1,9)).tolist()[0]

    new_initial.remove(0)
    new_final.remove(0)

    for i in range(8):
        for j in range(i):
            if new_initial[j] > new_initial[i]:
                initial_checkerboard_result += 1

    for i in range(8):
        for j in range(i):
            if new_final[j] > new_final[i]:
                final_checkerboard_result += 1

    return initial_checkerboard_result, final_checkerboard_result

def main():
    initial_checkerboard = np.zeros((3, 3), dtype=int) #記錄初始的棋盤狀態

    print("空棋盤如下:")
    print(initial_checkerboard)

    print("請輸入初始狀態棋盤數據:")
    create_checkerboard(initial_checkerboard)

    print("初始棋盤狀態如下:")
    print(initial_checkerboard)

    print("請輸入終止狀態棋盤數據:")
    create_checkerboard(final_checkerboard)

    print("最終棋盤狀態如下:")
    print(final_checkerboard)
    print("-------------------")

    number1, number2 = judge_have_answer(initial_checkerboard)

    if (number1 % 2) != (number2 % 2):
        print("該起始狀態無法達到最終狀態!")
        exit()

    global start_time
    start_time = time.time()
    print("start_time=", start_time)

    statistics(initial_checkerboard)

    global final_stack
    global mid_stack
    mid_checkerboard = copy.deepcopy(initial_checkerboard)
    final_stack.append(mid_checkerboard)
    total_stack.append(mid_checkerboard)
    while(len(final_stack) != 0):
        iterator_all_elements(final_stack)
        final_stack = copy.deepcopy(mid_stack)
        mid_stack = []
    print("this state no answer!")

if __name__ == '__main__':
    main()

A算法

import numpy as np
import copy

"""
1. 空洞用數字0表示,其他各個位置用1...8表示
2. 同一層deep都一樣,所以直接不考慮
3. 注意深淺拷貝問題
4. 注意閉包問題
5. 注意stack不是棧,是隊列queue,temp是step代表步數,懶得改了
6. print("this state no answer!")已經沒用了,最初是驗證是否會出現沒解的情況,現在有了judge_have_answer()方法
7. 使用規則:輸入初始和最終棋盤元素,橫着從左到右挨着輸入,空洞爲0,其他爲1...8
"""

final_checkerboard = np.zeros((3, 3), dtype=int)  # 記錄最終的棋盤狀態
min_element_number = 0
total_stack = []
final_stack = []
mid_stack = []
temp = 0

def create_checkerboard(checkerboard):
    order = 0
    for i in range(3):
        for j in range(3):
            order += 1
            element = int(input("請輸入第{}個數:".format(order)))
            checkerboard[i][j] = element

def move_checkerboard(before_state):
    # 首先確定空洞的位置
    position = np.argwhere(before_state == 0)
    first_position = position[0][0]
    second_position = position[0][1]

    if first_position == 0:
        if second_position == 0:
            zuoyi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)
        elif second_position == 2:
            youyi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)
        else:
            youyi(before_state, first_position, second_position)
            zuoyi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)
    elif first_position == 2:
        if second_position == 0:
            xiayi(before_state, first_position, second_position)
            zuoyi(before_state, first_position, second_position)
        elif second_position == 2:
            youyi(before_state, first_position, second_position)
            xiayi(before_state, first_position, second_position)
        else:
            youyi(before_state, first_position, second_position)
            xiayi(before_state, first_position, second_position)
            zuoyi(before_state, first_position, second_position)
    else:
        if second_position == 0:
            zuoyi(before_state, first_position, second_position)
            xiayi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)
        elif second_position == 2:
            xiayi(before_state, first_position, second_position)
            youyi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)
        else:
            youyi(before_state, first_position, second_position)
            xiayi(before_state, first_position, second_position)
            zuoyi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)

def equal_all_position(first_checkerboard):
    for t in total_stack:
        num = 0
        for i in range(3):
            for j in range(3):
                if first_checkerboard[i][j] == t[i][j]:
                    num += 1
        if num == 9:
            return 0
        else:
            pass
    return 1

def shangyi(checkerboard, first_position, second_position):
    after_state = copy.deepcopy(checkerboard)
    after_state[first_position][second_position] = after_state[first_position + 1][second_position]  # 數字上移
    after_state[first_position + 1][second_position] = 0
    tap = equal_all_position(after_state)
    if tap:
        number = statistics(after_state)
        ruzhan(after_state, number)

def xiayi(checkerboard, first_position, second_position):
    after_state = copy.deepcopy(checkerboard)
    after_state[first_position][second_position] = after_state[first_position -1][second_position]  # 數字下移
    after_state[first_position - 1][second_position] = 0
    tap = equal_all_position(after_state)
    if tap:
        number = statistics(after_state)
        ruzhan(after_state, number)

def zuoyi(checkerboard, first_position, second_position):
    after_state = copy.deepcopy(checkerboard)
    after_state[first_position][second_position] = after_state[first_position][second_position + 1]  # 數字左移
    after_state[first_position][second_position + 1] = 0
    tap = equal_all_position(after_state)
    if tap:
        number = statistics(after_state)
        ruzhan(after_state, number)

def youyi(checkerboard, first_position, second_position):
    after_state = copy.deepcopy(checkerboard)
    after_state[first_position][second_position] = after_state[first_position][second_position - 1]  # 數字右移
    after_state[first_position][second_position - 1] = 0
    tap = equal_all_position(after_state)
    if tap:
        number = statistics(after_state)
        ruzhan(after_state, number)

def ruzhan(checkerboard,number):
    global mid_stack
    global min_element_number
    if len(mid_stack) == 0:
        mid_stack.append(checkerboard)
        total_stack.append(checkerboard)
        min_element_number = number
    else:
        if number > min_element_number:
            print("未插入")
        elif number == min_element_number:
            mid_stack.append(checkerboard)
            total_stack.append(checkerboard)
            min_element_number = number
        elif number < min_element_number:
            mid_stack = []
            mid_stack.append(checkerboard)
            total_stack.append(checkerboard)
            min_element_number = number

    print(checkerboard)
    print("====================")

# 用來統計當前棋盤有多少個元素不在目標位置
def statistics(checkboard):
    number = 0

    for i in range(3):
        for j in range(3):
            if checkboard[i][j] == final_checkerboard[i][j]:
                number += 1

    if number == 9:
        print("最終結果爲:")
        print(checkboard)
        exit()
    return 9 - number

def iterator_all_elements(final_stack):
    for i in final_stack:
        move_checkerboard(i)

# 這裏的教訓告訴我們沒事不要瞎用不熟悉的庫,最後還是numpy轉list
def judge_have_answer(initial_checkerboard):
    initial_checkerboard_result = 0
    final_checkerboard_result = 0

    initial_checkerboard_backup = copy.deepcopy(initial_checkerboard)
    final_checkerboard_backup = copy.deepcopy(final_checkerboard)

    new_initial = initial_checkerboard_backup.reshape((1,9)).tolist()[0]
    new_final = final_checkerboard_backup.reshape((1,9)).tolist()[0]

    new_initial.remove(0)
    new_final.remove(0)

    for i in range(8):
        for j in range(i):
            if new_initial[j] > new_initial[i]:
                initial_checkerboard_result += 1

    for i in range(8):
        for j in range(i):
            if new_final[j] > new_final[i]:
                final_checkerboard_result += 1

    return initial_checkerboard_result, final_checkerboard_result

def main():
    initial_checkerboard = np.zeros((3, 3), dtype=int) #記錄初始的棋盤狀態

    print("空棋盤如下:")
    print(initial_checkerboard)

    print("請輸入初始狀態棋盤數據:")
    create_checkerboard(initial_checkerboard)

    print("初始棋盤狀態如下:")
    print(initial_checkerboard)

    print("請輸入終止狀態棋盤數據:")
    create_checkerboard(final_checkerboard)

    print("最終棋盤狀態如下:")
    print(final_checkerboard)
    print("-------------------")

    number1, number2 = judge_have_answer(initial_checkerboard)

    if (number1 % 2) != (number2 % 2):
        print("該起始狀態無法達到最終狀態!")
        exit()

    statistics(initial_checkerboard)

    global final_stack
    global mid_stack
    mid_checkerboard = copy.deepcopy(initial_checkerboard)
    final_stack.append(mid_checkerboard)
    total_stack.append(mid_checkerboard)
    global temp
    while(len(final_stack) != 0):
        temp += 1
        iterator_all_elements(final_stack)
        final_stack = copy.deepcopy(mid_stack)
        mid_stack = []
    print("this state no answer!")

if __name__ == '__main__':
    main()

A*算法

import numpy as np
import copy

"""
1. 空洞用數字0表示,其他各個位置用1...8表示
2. 同一層deep都一樣,所以直接不考慮
3. 注意深淺拷貝問題
4. 注意閉包問題
5. 注意stack不是棧,是隊列queue,temp是step代表步數,懶得改了
6. print("this state no answer!")已經沒用了,最初是驗證是否會出現沒解的情況,現在有了judge_have_answer()方法
7. 使用規則:輸入初始和最終棋盤元素,橫着從左到右挨着輸入,空洞爲0,其他爲1...8
"""

final_checkerboard = np.zeros((3, 3), dtype=int)  # 記錄最終的棋盤狀態
min_element_number = 0
total_stack = []
final_stack = []
mid_stack = []
temp = 0
total_step = 0

def create_checkerboard(checkerboard):
    order = 0
    for i in range(3):
        for j in range(3):
            order += 1
            element = int(input("請輸入第{}個數:".format(order)))
            checkerboard[i][j] = element

def move_checkerboard(before_state):
    # 首先確定空洞的位置
    position = np.argwhere(before_state == 0)
    first_position = position[0][0]
    second_position = position[0][1]

    if first_position == 0:
        if second_position == 0:
            zuoyi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)
        elif second_position == 2:
            youyi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)
        else:
            youyi(before_state, first_position, second_position)
            zuoyi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)
    elif first_position == 2:
        if second_position == 0:
            xiayi(before_state, first_position, second_position)
            zuoyi(before_state, first_position, second_position)
        elif second_position == 2:
            youyi(before_state, first_position, second_position)
            xiayi(before_state, first_position, second_position)
        else:
            youyi(before_state, first_position, second_position)
            xiayi(before_state, first_position, second_position)
            zuoyi(before_state, first_position, second_position)
    else:
        if second_position == 0:
            zuoyi(before_state, first_position, second_position)
            xiayi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)
        elif second_position == 2:
            xiayi(before_state, first_position, second_position)
            youyi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)
        else:
            youyi(before_state, first_position, second_position)
            xiayi(before_state, first_position, second_position)
            zuoyi(before_state, first_position, second_position)
            shangyi(before_state, first_position, second_position)

def equal_all_position(first_checkerboard):
    for t in total_stack:
        num = 0
        for i in range(3):
            for j in range(3):
                if first_checkerboard[i][j] == t[i][j]:
                    num += 1
        if num == 9:
            return 0
        else:
            pass
    return 1

def step_total_number(checkerboard):
    global final_checkerboard
    global total_step
    total_step = 0
    for i in range(3):
        for j in range(3):
            key = checkerboard[i][j]
            if (checkerboard[i][j] == 0):
                continue
            position = np.argwhere(final_checkerboard == key)
            # 這裏是獲取到終止狀態的位置
            horizontal_position = position[0][0]
            vertical_position = position[0][1]
            hang_step = abs(horizontal_position - i)
            lie_step = abs(vertical_position - j)
            total_step += hang_step + lie_step
    if total_step == 0:
        print("最終結果爲:")
        print(checkerboard)
        exit()
    return total_step

def shangyi(checkerboard, first_position, second_position):
    after_state = copy.deepcopy(checkerboard)
    after_state[first_position][second_position] = after_state[first_position + 1][second_position]  # 數字上移
    after_state[first_position + 1][second_position] = 0
    tap = equal_all_position(after_state)
    if tap:
        number = step_total_number(after_state)
        ruzhan(after_state, number)

def xiayi(checkerboard, first_position, second_position):
    after_state = copy.deepcopy(checkerboard)
    after_state[first_position][second_position] = after_state[first_position -1][second_position]  # 數字下移
    after_state[first_position - 1][second_position] = 0
    tap = equal_all_position(after_state)
    if tap:
        number = step_total_number(after_state)
        ruzhan(after_state, number)

def zuoyi(checkerboard, first_position, second_position):
    after_state = copy.deepcopy(checkerboard)
    after_state[first_position][second_position] = after_state[first_position][second_position + 1]  # 數字左移
    after_state[first_position][second_position + 1] = 0
    tap = equal_all_position(after_state)
    if tap:
        number = step_total_number(after_state)
        ruzhan(after_state, number)

def youyi(checkerboard, first_position, second_position):
    after_state = copy.deepcopy(checkerboard)
    after_state[first_position][second_position] = after_state[first_position][second_position - 1]  # 數字右移
    after_state[first_position][second_position - 1] = 0
    tap = equal_all_position(after_state)
    if tap:
        number = step_total_number(after_state)
        ruzhan(after_state, number)

def ruzhan(checkerboard,number):
    global mid_stack
    global min_element_number
    if len(mid_stack) == 0:
        mid_stack.append(checkerboard)
        total_stack.append(checkerboard)
        min_element_number = number
    else:
        if number > min_element_number:
            print("未插入")
        elif number == min_element_number:
            mid_stack.append(checkerboard)
            total_stack.append(checkerboard)
            min_element_number = number
        elif number < min_element_number:
            mid_stack = []
            mid_stack.append(checkerboard)
            total_stack.append(checkerboard)
            min_element_number = number

    print(checkerboard)
    print("====================")

def iterator_all_elements(final_stack):
    for i in final_stack:
        move_checkerboard(i)

# 這裏的教訓告訴我們沒事不要瞎用不熟悉的庫,最後還是numpy轉list
def judge_have_answer(initial_checkerboard):
    initial_checkerboard_result = 0
    final_checkerboard_result = 0

    initial_checkerboard_backup = copy.deepcopy(initial_checkerboard)
    final_checkerboard_backup = copy.deepcopy(final_checkerboard)

    new_initial = initial_checkerboard_backup.reshape((1,9)).tolist()[0]
    new_final = final_checkerboard_backup.reshape((1,9)).tolist()[0]

    new_initial.remove(0)
    new_final.remove(0)

    for i in range(8):
        for j in range(i):
            if new_initial[j] > new_initial[i]:
                initial_checkerboard_result += 1

    for i in range(8):
        for j in range(i):
            if new_final[j] > new_final[i]:
                final_checkerboard_result += 1

    return initial_checkerboard_result, final_checkerboard_result

def main():
    initial_checkerboard = np.zeros((3, 3), dtype=int) #記錄初始的棋盤狀態

    print("空棋盤如下:")
    print(initial_checkerboard)

    print("請輸入初始狀態棋盤數據:")
    create_checkerboard(initial_checkerboard)

    print("初始棋盤狀態如下:")
    print(initial_checkerboard)

    print("請輸入終止狀態棋盤數據:")
    create_checkerboard(final_checkerboard)

    print("最終棋盤狀態如下:")
    print(final_checkerboard)
    print("-------------------")

    number1, number2 = judge_have_answer(initial_checkerboard)

    if (number1 % 2) != (number2 % 2):
        print("該起始狀態無法達到最終狀態!")
        exit()

    step_total_number(initial_checkerboard)

    global final_stack
    global mid_stack
    mid_checkerboard = copy.deepcopy(initial_checkerboard)
    final_stack.append(mid_checkerboard)
    total_stack.append(mid_checkerboard)
    global temp
    while(len(final_stack) != 0):
        temp += 1
        iterator_all_elements(final_stack)
        final_stack = copy.deepcopy(mid_stack)
        mid_stack = []
    print("this state no answer!")

if __name__ == '__main__':
    main()

程序大致的流程走向圖
在這裏插入圖片描述
相關方法的簡單介紹:
在這裏插入圖片描述
課本中廣度相關部分圖片:
在這裏插入圖片描述
A算法:
在這裏插入圖片描述
A*算法
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章