Python IDDFS 缺少潜在结果

问题描述 投票:0回答:1

我正在使用 bfs 和 iddfs 来找到 8 个瓷砖拼图的最佳解决方案,但是,我的 IDDFS 缺少解决方案,我不知道为什么。我检查过,似乎每个节点都访问它的所有儿子,至少在第一次迭代中,所以我不确定问题的根源可能是什么。将图块排列为 [1,4,5,0,8,2,3,6,7] 我的 iddfs 似乎没有在适当的深度找到正确的解决方案,后来偶然发现了另一个解决方案: BFS 解决路径: [1,4,5,2,8,5,4,1,3,6,7,8,5,4,1]

iddfs 解决路径: [8, 2, 5, 4, 1, 8, 2, 1, 8, 2, 1, 8, 4, 5, 8, 4, 2, 1, 3, 6, 7, 8, 5, 2, 1 ]

请注意,我的 iddfs k(深度)以 1 为间隔增加,因此我应该在我的 bfs 相同的深度找到最佳解决方案,这意味着尽可能早的深度。 我正在添加 iddfs 代码以及运行它所需的其余代码:

    # helper method for the iddfs, solves a dfs to a limited depth k
    def dfs_solve_k(self, node, depth):
        if depth < 0:
            return False

        self.total_count += 1
        board_tuple = tuple(tuple(row) for row in node.board_array)
        self.explored.add(board_tuple)

        if node.check_end_state():
            return True

        # Iterate over possible swap positions in a specific order
        for swappable_pos in node.possible_swap_positions:
            tracker = node.board_array[swappable_pos.i][swappable_pos.j]

            # Perform swap to reach a new board
            new_board = node.swap(swappable_pos)
            new_board_tuple = tuple(tuple(row) for row in new_board)

            # Check if the new board state has not been explored before
            if new_board_tuple not in self.explored:
                new_node = StateNode(new_board, swappable_pos)
                is_solved = self.dfs_solve_k(new_node, depth - 1)

                if is_solved:
                    self.path.append(tracker)
                    return True

        return False

    # iterative deepening dfs
    def iddfs(self, node, max_depth):
        search_depth = 1

        while search_depth <= max_depth:
            self.explored.clear()
            self.path = []
            is_solved = self.dfs_solve_k(node, search_depth)

            if is_solved:
                self.path = self.path[::-1]
                return True
            else:
                search_depth += 1
        return False

用于测试的附加代码:

rows = 3
columns = 3
goal_state = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]


class Position:
    def __init__(self, i, j):
        self.i = i
        self.j = j


class Directions(Enum):
    LEFT = Position(0, -1)
    RIGHT = Position(0, 1)
    UP = Position(-1, 0)
    DOWN = Position(1, 0)


def get_inv_count(board):
    inv_count = 0
    empty_value = 0
    n = len(board)

    for i in range(n * n):
        for j in range(i + 1, n * n):
            row_i, col_i = divmod(i, n)
            row_j, col_j = divmod(j, n)

            val_i = board[row_i][col_i]
            val_j = board[row_j][col_j]

            if val_i != empty_value and val_j != empty_value and val_i > val_j:
                inv_count += 1

    return inv_count


# This function returns true if the given puzzle is solvable.
def is_solvable(puzzle):
    # Count inversions in the given puzzle
    inv_count = get_inv_count(puzzle)

    # Return true if the inversion count is even.
    return inv_count % 2 == 0


def find_position(value, board):
    for i in range(3):
        for j in range(3):
            if board[i][j] == value:
                return Position(i, j)


def calculate_distance(position1, position2):
    x1, y1 = position1
    x2 = position2.i
    y2 = position2.j
    distance = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
    return distance


def heuristic(board_state):
    distance_score = 0

    for i in range(3):
        for j in range(3):
            cell_value = board_state[i][j]
            if cell_value != 0:  # Skip the empty tile (represented by 0)
                target_position = find_position(cell_value, goal_state)
                current_position = (i, j)
                distance = calculate_distance(current_position, target_position)
                distance_score += distance

    return distance_score


class StateNode:
    def __init__(self, board, zero_pos, score=0, parent=None):
        self.board_array = board
        self.zero_pos = zero_pos
        self.score = score
        self.possible_swap_positions = []
        self.parent_node = parent

        for direction in Directions:
            new_zero_pos = Position(self.zero_pos.i + direction.value.i,
                                    self.zero_pos.j + direction.value.j)
            if (
                    0 <= new_zero_pos.i < len(self.board_array) and
                    0 <= new_zero_pos.j < len(self.board_array)
            ):
                self.possible_swap_positions.append(new_zero_pos)

    def get_value(self, position):
        return self.board_array[position.i][position.j]

    def swap(self, swappable_pos):
        # deep copy our array
        new_board = deepcopy(self.board_array)
        temp_value = self.board_array[swappable_pos.i][swappable_pos.j]
        # change current zero to value in new position
        new_board[self.zero_pos.i][self.zero_pos.j] = temp_value
        # change swapped position to zero
        new_board[swappable_pos.i][swappable_pos.j] = 0
        return new_board

    def check_end_state(self):
        for board_row, end_state_row in zip(self.board_array, goal_state):
            if board_row != end_state_row:
                return False

        return True


class GameBoard:
    def __init__(self, values_array):
        self.total_count = 0
        self.rows = rows
        self.columns = columns
        self.path = []
        self.explored = set()
        expected_size = rows * columns
        self.game_board = [[0] * columns for _ in range(rows)]
        # Check if the dimensions of values_array match the dimensions of the game_board
        try:
            values_array = [int(value) for value in values_array]
        except ValueError:
            raise ValueError("All elements in values_array must be convertible to integers.")
        if len(values_array) == expected_size:
            self.game_board = [values_array[i:i + self.columns] for i in range(0, expected_size, self.columns)]
        else:
            raise ValueError(
                "The dimensions of the provided values_array do not match the dimensions of the game board.")
python algorithm depth-first-search traversal graph-traversal
1个回答
0
投票

要小心如何定义某个状态是否已被探索。 可能有一条较长的路径具有不同的起点,在最终达到解决方案状态之前达到与解决方案路径共享的中间状态。 当以解决方案的最大深度(此处为 15)运行 DFS 时,您可能会首先遵循这条较长的路径,并访问中间状态,但无法到达最终状态,因为替代路径的长度超过 15。 然后,当遵循所需解决方案路径的第一步时,您将到达中间已访问过的状态,并决定不扩展您的搜索。

总之,如果存在的话,你的 IDDFS 会找到一个解决方案,但不一定是最短的。

© www.soinside.com 2019 - 2024. All rights reserved.