微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在 Python 中为 15-Puzzle 问题实现 IDA* 算法? 参考:

如何解决如何在 Python 中为 15-Puzzle 问题实现 IDA* 算法? 参考:

我正在尝试使用 IDA* 算法和曼哈顿启发式算法来解决 15-Puzzle 问题。 我已经从这个维基百科页面 (link) 中的伪代码实现了算法。

这是我目前的代码

def IDA(initial_state,goal_state):
    initial_node = Node(initial_state)
    goal_node = Node(goal_state)
    
    threshold = manhattan_heuristic(initial_state,goal_state)
    path = [initial_node]

    while 1:
        tmp = search(path,goal_state,threshold)
        if tmp == True:
            return path,threshold
        elif tmp == float('inf'):
            return False
        else:
            threshold = tmp


def search(path,g,threshold):
    node = path[-1]
    f = g + manhattan_heuristic(node.state,goal_state)

    if f > threshold:
        return f

    if np.array_equal(node.state,goal_state):
        return True

    minimum = float('inf')  
    for n in node.nextnodes():
        if n not in path:
            path.append(n)
            tmp = search(path,g + 1,threshold)
            if tmp == True:
                return True
            if tmp < minimum:
                minimum = tmp
            path.pop()

    return minimum


def manhattan_heuristic(state1,state2):
    size = range(1,len(state1) ** 2)
    distances = [count_distance(num,state1,state2) for num in size]

    return sum(distances)


def count_distance(number,state2):
    position1 = np.where(state1 == number)
    position2 = np.where(state2 == number)

    return manhattan_distance(position1,position2)


def manhattan_distance(a,b):
    return abs(b[0] - a[0]) + abs(b[1] - a[1])


class Node():
    def __init__(self,state):
        self.state = state

    def nextnodes(self):
        zero = np.where(self.state == 0)
        
        y,x = zero
        y = int(y)
        x = int(x)
        
        up = (y - 1,x) 
        down = (y + 1,x)
        right = (y,x + 1)
        left = (y,x - 1)

        arr = []
        for direction in (up,down,right,left):
            if len(self.state) - 1 >= direction[0] >= 0 and len(self.state) - 1 >= direction[1] >= 0:
                tmp = np.copy(self.state)
                tmp[direction[0],direction[1]],tmp[zero] = tmp[zero],tmp[direction[0],direction[1]]
                arr.append(Node(tmp))

        return arr

我正在用 3x3 拼图测试此代码,这是无限循环!由于递归,我在测试代码时遇到了一些麻烦...

我认为错误可能在这里tmp = search(path,threshold)(在 search 函数中)。我只在 g 成本值上加了一个。不过应该是正确的,因为我只能将图块移动 1 个位置。

以下是调用 IDA() 函数方法

initial_state = np.array([8 7 3],[4 1 2],[0 5 6])
goal_state = np.array([1 2 3],[8 0 4],[7 6 5])
IDA(initial_state,goal_state)

有人可以帮我吗?

解决方法

您的 IDA* 实现中有几个问题。首先,变量path的目的是什么?我在您的代码中发现了 path 的两个用途:

  1. 用作标志/地图以保留已访问过的棋盘状态。
  2. 用作堆栈来管理递归状态。

但是,不可能通过使用单个数据结构同时完成这两项任务。因此,您的代码需要进行的第一次修改:

  • 修复 1:将当前 node 作为参数传递给 search 方法。
  • 修正 2:flag 应该是一种可以高效执行 not in 查询的数据结构。

现在,fix-1 很简单,因为我们可以在搜索方法中将当前访问节点作为参数传递。对于 fix-2,我们需要将 flag 的类型从 list 更改为 set 为:

  • listx in s 的平均案例复杂度为:O(n)
  • set
    • x in s 的平均案例复杂度为:O(1)
    • x in s 的最坏情况复杂度是:O(n)

您可以查看有关 performance for testing memberships: list vs sets 的更多详细信息以了解更多详情。

现在,要将 Node 信息保存到 set 中,您需要在 __eq__ 类中实现 __hash__Node。下面附上修改后的代码。

import timeit
import numpy as np

def IDA(initial_state,goal_state):
    initial_node = Node(initial_state)
    goal_node = Node(goal_state)
    
    threshold = manhattan_heuristic(initial_state,goal_state)
    
    #print("heuristic threshold: {}".format(threshold))
    
    loop_counter = 0

    while 1:
        path = set([initial_node])
        
        tmp = search(initial_node,goal_state,threshold,path)
        
        #print("tmp: {}".format(tmp))
        if tmp == True:
            return True,threshold
        elif tmp == float('inf'):
            return False,float('inf')
        else:
            threshold = tmp


def search(node,g,path):
    #print("node-state: {}".format(node.state))
    f = g + manhattan_heuristic(node.state,goal_state)

    if f > threshold:
        return f

    if np.array_equal(node.state,goal_state):
        return True

    minimum = float('inf')  
    for n in node.nextnodes():
        if n not in path:
            path.add(n)
            tmp = search(n,g + 1,path)
            if tmp == True:
                return True
            if tmp < minimum:
                minimum = tmp

    return minimum


def manhattan_heuristic(state1,state2):
    size = range(1,len(state1) ** 2)
    distances = [count_distance(num,state1,state2) for num in size]

    return sum(distances)


def count_distance(number,state2):
    position1 = np.where(state1 == number)
    position2 = np.where(state2 == number)

    return manhattan_distance(position1,position2)


def manhattan_distance(a,b):
    return abs(b[0] - a[0]) + abs(b[1] - a[1])


class Node():
    def __init__(self,state):
        self.state = state
    
    def __repr__(self):
        return np.array_str(self.state.flatten())

    def __hash__(self):
        return hash(self.__repr__())
        
    def __eq__(self,other):
        return self.__hash__() == other.__hash__()

    def nextnodes(self):
        zero = np.where(self.state == 0)
        
        y,x = zero
        y = int(y)
        x = int(x)
        
        up = (y - 1,x) 
        down = (y + 1,x)
        right = (y,x + 1)
        left = (y,x - 1)

        arr = []
        for direction in (up,down,right,left):
            if len(self.state) - 1 >= direction[0] >= 0 and len(self.state) - 1 >= direction[1] >= 0:
                tmp = np.copy(self.state)
                tmp[direction[0],direction[1]],tmp[zero] = tmp[zero],tmp[direction[0],direction[1]]
                arr.append(Node(tmp))

        return arr


initial_state = np.array([[8,7,3],[4,1,2],[0,5,6]])
goal_state = np.array([[1,2,[8,4],[7,6,5]])

start = timeit.default_timer()
is_found,th = IDA(initial_state,goal_state)
stop = timeit.default_timer()

print('Time: {} seconds'.format(stop - start))

if is_found is True:
    print("Solution found with heuristic-upperbound: {}".format(th))
else:
    print("Solution not found!")

节点:请仔细检查您的 Node.nextnodes()manhattan_heuristic() 方法,因为我在这些方面没有太注意。您可以检查此 GitHub repository 以了解其他算法实现(即 A*IDSDLS)以解决此问题。

参考:

  1. Python Wiki: Time Complexity
  2. TechnoBeans: Performance for testing memberships: list vs tuples vs sets
  3. GitHub: Puzzle Solver (by using problem solving techniques)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。