python爬山法解决n皇后问题

写了两年c了 终于迈出了自己写python的第一步...过程极为折磨...好在最后写出来报告也交了....

直接上可运行代码 有问题可以直接私聊我

nQueens.py:

import random
from pprint import pprint
import time

def input_size_board():
    ''' Take input from user for size of board '''
    n = input("Enter an integer for the size of the board: ")
    return int(n)


def print_board(board, n):
    ''' Helper function for printing board '''
    print('分布:')
    for i in range(len(board)):
        print(str(board[i]) + ' ', end='')
        if (i + 1) % n == 0:
            print()
    print('冲突次数: ', determine_h_cost(board, n))
    print('---------------------')


def generate_random_board(n):
    ''' Generates a random board for initialization, queens have been calculated row-wise '''
    generated_board = []
    for i in range(n):
        j = random.randint(0, n - 1)
        row = [0] * n
        row[j] = 1
        generated_board.extend(row)
    return generated_board


def find_collisions(board, n):
    ''' Helper function for calculating queen position collisions '''
    collisions = 0
    occurences = []
    max_index = len(board)
    for i in range(max_index):
        # For each queen on the board, count collisions with other queens, and which kind of collisions they are
        if board[i] == 1:
            for x in range(1, n):
                # checking above current index
                if (i - n * x >= 0):
                    north = i - n * x
                    # direction north
                    if (board[north] == 1):
                        collisions += 1
                        occurences.append('north: ' + str(i) + ' and ' + str(north))
                    # direction northwest
                    if (int((north - x) / n) == int(north / n)) and (north - x) >= 0:
                        northwest = north - x
                        if (board[northwest] == 1):
                            collisions += 1
                            occurences.append('northwest: ' + str(i) + ' and ' + str(northwest))
                    # direction northeast
                    if (int((north + x) / n) == int(north / n)):
                        northeast = north + x
                        if (board[northeast] == 1):
                            collisions += 1
                            occurences.append('northeast: ' + str(i) + ' and ' + str(northeast))
                # checking below current index
                if (i + n * x < max_index):
                    south = i + n * x
                    # direction south
                    if (board[south] == 1):
                        collisions += 1
                        occurences.append('south: ' + str(i) + ' and ' + str(south))
                    # direction southwest
                    if (int((south - x) / n) == int(south / n)):
                        southwest = south - x
                        if (board[southwest] == 1):
                            collisions += 1
                            occurences.append('southwest: ' + str(i) + ' and ' + str(southwest))
                    # direction southeast
                    if (int((south + x) / n) == int(south / n)) and ((south + x) < max_index):
                        southeast = south + x
                        if (board[southeast] == 1):
                            collisions += 1
                            occurences.append('southeast: ' + str(i) + ' and ' + str(southeast))
                # direction west (for completeness)
                if (int((i - x) / n) == int(i / n)) and (i - x >= 0):
                    west = i - x
                    if (board[west] == 1):
                        collisions += 1
                        occurences.append('west: ' + str(i) + ' and ' + str(west))
                # direction east (for completeness)
                if (int((i + x) / n) == int(i / n)) and (i + x < max_index):
                    east = i + x
                    if (board[east] == 1):
                        collisions += 1
                        occurences.append('east: ' + str(i) + ' and ' + str(east))
    return [collisions, occurences]


def determine_h_cost(board, n, verbose=False):
    ''' Function to determine heuristic - total collisions on the board '''
    collisions, occurences = find_collisions(board, n)
    if verbose:
        pprint(occurences)
    # return half the collisions, since each colliding position is counted twice from the helper function
    return int(collisions / 2)


def find_child(board, n, sideways_move=False):
    ''' Function to find the successor from all the children by comparing the heuristic values of moving the queens row-wise '''
    child = []
    current_h_cost = determine_h_cost(board, n)
    same_cost_children = []

    for row in range(n):
        for col in range(n):
            # Build a temporary board which changes the position of the queen in the current board
            temp_board = []
            temp_board.extend(board[:row * n])
            new_row = [0] * n
            new_row[col] = 1
            temp_board.extend(new_row)
            temp_board.extend(board[(row + 1) * n:])
            temp_h_cost = determine_h_cost(temp_board, n)
            if (sideways_move):
                # if sideways moves are allowed, and the generated child heuristic cost is less than or equal to the current lowest heuristic cost, save generated child and update current lowest heuristic cost
                if (temp_board != board):
                    if (temp_h_cost < current_h_cost):
                        child = temp_board.copy()
                        current_h_cost = temp_h_cost
                    elif (temp_h_cost == current_h_cost):
                        same_cost_children.append(temp_board)
                        x = random.randint(0, len(same_cost_children) - 1)
                        child = same_cost_children[x]
            else:
                # if sideways moves are not allowed, and the generated child heuristic cost is less than the current lowest heuristic cost, save generated child and update current lowest heuristic cost
                if (temp_board != board) and (temp_h_cost < current_h_cost):
                    child = temp_board.copy()
                    current_h_cost = temp_h_cost
    return child


def steepest_hill_climbing(board, n, max_iterations=200, verbose=False):
    count = 0
    ''' Steepest Hill climbing without sideways move, returns the current steps and whether the run succeeded or not '''
    steps = 0
    success = False
    current_board = board.copy()

    if (verbose):
        print_board(current_board, n)

    # Until maximum iterations are reached, search for a solution
    for i in range(max_iterations):
        # Get the least heuristic child from the find child helper function
        next_node = find_child(current_board, n).copy()
        count += 1#add
        if (verbose and len(next_node) != 0):
            print_board(next_node, n)

        # Update the steps taken for this run
        steps += 1
        # If we have a child and its heuristic cost is zero, we have a solution
        if (len(next_node) != 0) and (determine_h_cost(next_node, n) == 0):
            success = True
            break
        # If we do not get a child, we cannot get a solution
        if (len(next_node) == 0):
            break
        # Make the current child the next node
        current_board = next_node.copy()
    return steps, success ,count


def steepest_hill_climbing_with_sideways_move(board, n, max_iterations=200, verbose=False):
    ''' Steepest Hill climbing with sideways move, returns the current steps and whether the run succeeded or not '''
    count = 0
    steps = 0
    success = False
    current_board = board.copy()

    if (verbose):
        print_board(current_board, n)

    # Until maximum iterations are reached, search for a solution
    for i in range(max_iterations):
        # Get the least heuristic child from the find child helper function
        next_node = find_child(current_board, n, sideways_move=True).copy()
        count += 1#add
        if (verbose and len(next_node) != 0):
            print_board(next_node, n)

        # Update the steps taken for this run
        steps += 1
        # If we have a child and its heuristic cost is zero, we have a solution
        if (len(next_node) != 0) and (determine_h_cost(next_node, n) == 0):
            success = True
            break
        # If we do not get a child, we cannot get a solution
        if (len(next_node) == 0):
            break
        # Make the current child the next node
        current_board = next_node.copy()
    return steps, success ,count#add


def steepest_hill_climbing_with_random_restart(board, n, max_iterations=200, verbose=False):
    ''' Steepest Hill climbing with random restart and without sideways move, returns the current steps and whether the run succeeded or not '''
    count = 0
    steps = 0
    success = False
    rr = 0
    current_board = board.copy()

    if (verbose):
        print_board(current_board, n)

    # Until maximum iterations are reached, search for a solution
    for i in range(max_iterations):
        # Get the least heuristic child from the find child helper function
        next_node = find_child(current_board, n).copy()
        count += 1
        if (verbose and len(next_node) != 0):
            print_board(next_node, n)

        # Update the steps taken for this run
        steps += 1
        # If we do not get a child, restart the search by generating another random board
        if (len(next_node) == 0):
            next_node = generate_random_board(n)
            # Maintain count of restarts made
            rr += 1
        # If the current node's heuristic cost is zero, we have a solution
        if (determine_h_cost(next_node, n) == 0):
            success = True
            break
        # Make the current child the next node
        current_board = next_node.copy()
    return steps, success, rr ,count


def steepest_hill_climbing_with_random_restart_and_sideways_move(board, n, max_iterations=200, verbose=False):
    ''' Steepest Hill climbing with random restart and sideways move, returns the current steps and whether the run succeeded or not '''
    steps = 0
    success = False
    rr = 0
    current_board = board.copy()

    if (verbose):
        print_board(current_board, n)

    # Until maximum iterations are reached, search for a solution
    for i in range(max_iterations):
        # Get the least heuristic child from the find child helper function
        next_node = find_child(current_board, n, sideways_move=True).copy()
        if (verbose and len(next_node) != 0):
            print_board(next_node, n)

        # Update the steps taken for this run
        steps += 1
        # If we do not get a child, restart the search by generating another random board
        if (len(next_node) == 0):
            next_node = generate_random_board(n)
            # Maintain count of restarts made
            rr += 1
        # If the current node's heuristic cost is zero, we have a solution
        if (determine_h_cost(next_node, n) == 0):
            success = True
            break
        # Make the current child the next node
        current_board = next_node.copy()
    return steps, success, rr

iterations = 200
n = input_size_board()

# Script for running functions
start = time.time()
print('爬山法:')
success_rate_steepest_hill_climbing = False
step_count_rate_steepest_hill_climbing_success = 0
step_count_rate_steepest_hill_climbing_failure = 0
sum = 0
for i in range(3):
    print('运行 ' + str(i + 1) + ':')
    step_count, success, count = steepest_hill_climbing(generate_random_board(n), n, verbose=True)
    if (success):
        print('成功.')
        sum += count
        step_count_rate_steepest_hill_climbing_success += step_count

    else:
        print('失败.')
        sum += count
        step_count_rate_steepest_hill_climbing_failure += step_count
    success_rate_steepest_hill_climbing += success

for i in range(3, iterations):
    step_count, success, count = steepest_hill_climbing(generate_random_board(n), n)
    sum += count
    if (success):
        step_count_rate_steepest_hill_climbing_success += step_count
    else:
        step_count_rate_steepest_hill_climbing_failure += step_count
    success_rate_steepest_hill_climbing += success
print('成功率: ' + str(success_rate_steepest_hill_climbing / iterations))
print('失败率: ' + str(1 - (success_rate_steepest_hill_climbing / iterations)))
print('平均成功步数: ' + str(
    step_count_rate_steepest_hill_climbing_success / success_rate_steepest_hill_climbing))
print('平均失败步数: ' + str(
    step_count_rate_steepest_hill_climbing_failure / (iterations - success_rate_steepest_hill_climbing)))
print('平均结点数:', sum / iterations)
end1 = time.time()
running_time1 = (end1 - start) / iterations
print('爬山法花费时间: %.5f seconds' % running_time1)


print('侧移爬山法:')
sum = 0
success_rate_steepest_hill_climbing_sm = False
step_count_rate_steepest_hill_climbing_success_sm = 0
step_count_rate_steepest_hill_climbing_failure_sm = 0
for i in range(3):
    print('运行 ' + str(i + 1) + ':')
    step_count, success, count = steepest_hill_climbing_with_sideways_move(generate_random_board(n), n,
                                                                           verbose=True)
    if (success):
        print('成功.')
        sum += count
        step_count_rate_steepest_hill_climbing_success_sm += step_count
    else:
        print('失败.')
        sum += count
        step_count_rate_steepest_hill_climbing_failure_sm += step_count
    success_rate_steepest_hill_climbing_sm += success
for i in range(3, iterations):
    step_count, success, count = steepest_hill_climbing_with_sideways_move(generate_random_board(n), n)
    sum += count
    if (success):
        step_count_rate_steepest_hill_climbing_success_sm += step_count
    else:
        step_count_rate_steepest_hill_climbing_failure_sm += step_count
    success_rate_steepest_hill_climbing_sm += success
print('成功率: ' + str(success_rate_steepest_hill_climbing_sm / iterations))
print('失败率: ' + str(1 - (success_rate_steepest_hill_climbing_sm / iterations)))
print('平均成功步数: ' + str(
    step_count_rate_steepest_hill_climbing_success_sm / success_rate_steepest_hill_climbing_sm))
print('平均失败步数: ' + str(
    step_count_rate_steepest_hill_climbing_failure_sm / (iterations - success_rate_steepest_hill_climbing_sm)))
print('平均结点数:', sum / iterations)
end2 = time.time()
running_time2 = (end2 - end1) / iterations
print('侧移爬山法花费时间 : %.5f seconds' % running_time2)

print('随机重启爬山法:')
sum = 0
success_rate_steepest_hill_climbing_rr = False
step_count_rate_steepest_hill_climbing_success_rr = 0
step_count_rate_steepest_hill_climbing_failure_rr = 0
random_restarts = 0
for i in range(iterations):
    step_count, success, rr, count = steepest_hill_climbing_with_random_restart(generate_random_board(n), n)
    random_restarts += rr
    if (success):
        print('成功.')
        sum += count
        step_count_rate_steepest_hill_climbing_success_rr += step_count
    else:
        print('失败.')
        sum += count
        step_count_rate_steepest_hill_climbing_failure_rr += step_count
    success_rate_steepest_hill_climbing_rr += success
print('成功率: ' + str(success_rate_steepest_hill_climbing_rr / iterations))
print('失败率: ' + str(
    step_count_rate_steepest_hill_climbing_success_rr / success_rate_steepest_hill_climbing_rr))
print('随机启动次数: ' + str(random_restarts))
print('平均结点数:', sum / iterations)
end3 = time.time()
running_time3 = (end3 - end2) / iterations
print('随机重启爬山法花费时间 : %.5f seconds' % running_time3)

hill_climbing.py:

# _*_ coding:utf-8 _#_
# 开发团队:抛夏
# 开发人员:抛夏
# 开发时间:2022/11/19 21:52
# 文件名称:hill_climbing.py
# 开发工具:PyCharm


# based on code from: https://github.com/aimacode/aima-python

from ast import While
import random


class NQueensProblem:
    """
    The problem of placing N queens on an NxN board with none attacking
    each other. A state is represented as an N-element array, where
    a value of r in the c-th entry means there is a queen at column c,
    row r.

    This class operates on complete state descriptions where all queens are
    on the board in each state (one in each column c). This is in contrast to
    the NQueensProblem implementation in aima-python, whose initial state has
    no queens on the board, and whose actions method generates all the valid
    positions to place a queen in the first free column.
    """

    def __init__(self, N=None, state=None):
        if N is None:
            N = len(state)
        if state is None:
            state = tuple(0 for _ in range(N))
        assert N == len(state)
        self.N = N
        self.initial = state

    def actions(self, state: tuple) -> list:
        ######################
        ### Your code here ###
        ######################
        p = list(state)
        l = []
        m = []
        for j in range(len(p)):

            for i in range(1, len(p)):
                m = p[:]
                m[j] = (i + p[j]) % len(p)
                l.append(m)
        return l

    def result(self, state: tuple, action) -> tuple:
        """Return the result of applying `action` to `state`.

        Move the queen in the column specified by `action` to the row specified by `action`.
        Node.expand calls `result` on each action returned by `actions`.
        """
        ######################
        ### Your code here ###
        ######################
        q = tuple(action)
        return q

    def goal_test(self, state):
        """Check if all columns filled, no conflicts."""
        return self.value(state) == 0

    def value(self, state):
        """Return 0 minus the number of conflicts in `state`."""
        return -self.num_conflicts(state)

    def num_conflicts(self, state):
        """Return the number of conflicts in `state`."""
        num_conflicts = 0
        for (col1, row1) in enumerate(state):
            for (col2, row2) in enumerate(state):
                if (col1, row1) != (col2, row2):
                    num_conflicts += self.conflict(row1, col1, row2, col2)
        return num_conflicts

    def conflict(self, row1, col1, row2, col2):
        """Would putting two queens in (row1, col1) and (row2, col2) conflict?"""
        return (row1 == row2 or  # same row
                col1 == col2 or  # same column
                row1 - col1 == row2 - col2 or  # same \ diagonal
                row1 + col1 == row2 + col2)  # same / diagonal

    def random_state(self):
        """Return a new random n-queens state.

        Use this to implement hill_climbing_random_restart.
        """
        return tuple(random.choice(range(self.N)) for _ in range(self.N))


class Node:
    """
    A node in a search tree. Contains a pointer to the parent (the node
    that this is a successor of) and to the actual state for this node.
    Delegates problem specific functionality to self.problem.
    """

    def __init__(self, problem, state, parent=None, action=None):
        """Create a search tree Node, derived from a parent by an action."""
        self.problem = problem
        self.state = state
        self.parent = parent
        self.action = action
        self.depth = 0
        if parent:
            self.depth = parent.depth + 1

    def __repr__(self):
        return "".format(self.state)

    def __lt__(self, node):
        return self.state < node.state

    def __eq__(self, node):
        return self.state == node.state

    def value(self):
        return self.problem.value(self.state)

    def goal_test(self):
        return self.problem.goal_test(self.state)

    def expand(self):
        """List the nodes reachable from this node."""
        state = self.state
        problem = self.problem
        return [
            Node(
                state=problem.result(state, action),
                problem=problem,
                parent=self,
                action=action,
            )
            for action in problem.actions(state)
        ]

    def best_of(self, nodes):
        """Return the best Node from a list of Nodes, based on problem.value.

        Sorting the nodes is not the best for runtime or search performance,
        but it ensures that the result is deterministic for the purpose of
        this assignment.
        """
        return max(
            sorted(nodes),
            key=lambda node: node.value(),
        )


def hill_climbing(problem):
    """
    [Figure 4.2] in the textbook.
    From the initial node, keep choosing the neighbor with highest value,
    stopping when no neighbor is better.
    """
    current = Node(problem=problem, state=problem.initial)
    while True:
        if current.goal_test():
            break
        neighbours = current.expand()
        if not neighbours:
            break
        neighbour = current.best_of(neighbours)
        if neighbour.value() <= current.value():
            break
        current = neighbour
    return current.state


def hill_climbing_instrumented(problem):
    """
    Find the same solution as `hill_climbing`, and return a dictionary
    recording the number of nodes expanded, and whether the problem was
    solved.
    """
    ######################
    ### Your code here ###
    ######################
    expand = 0
    bool1 = False
    current = Node(problem=problem, state=problem.initial)
    while True:
        if current.goal_test():
            bool1 = True
            break
        neighbours = current.expand()
        expand = expand + 1
        if not neighbours:
            break
        neighbour = current.best_of(neighbours)
        if neighbour.value() <= current.value():
            break
        current = neighbour
    return {
        "expanded": expand,
        "solved": bool1,
        "best_state": current.state,
    }


def hill_climbing_sideways(problem, max_sideways_moves):
    """
    When the search would terminate because the best neighbour doesn't
    have a higher value than the current state, continue the search if
    the the best neighbour's value is equal to that of the current state.

    But don't do this more than `max_sideways_moves` times. Watch out for
    off by one errors, and don't forget to return early if the search finds
    a goal state.
    """
    ######################
    ### Your code here ###
    ######################
    current = Node(problem=problem, state=problem.initial)
    expand = 0
    move = 0
    bool1 = False
    while True:
        if current.goal_test():
            bool1 = True
            break
        neighbours = current.expand()
        expand = expand + 1
        if not neighbours:
            break
        neighbour = current.best_of(neighbours)
        if neighbour.value() <= current.value():
            a = list(current.state)
            '''q = random.randint(0,len(current.state)-1)
            p = random.randint(0,len(current.state)-1)
            while(p==q):
                p = random.randint(0,len(current.state)-1)'''
            t = 0
            t = a[0]
            a[0] = a[1]
            a[1] = t
            if move >= max_sideways_moves:
                break
            move = move + 1
            current.state = tuple(a)
        if neighbour.value() >= current.value():
            current = neighbour

    return {
        "expanded": expand,
        "solved": bool1,
        "best_state": current.state,
        "sideways_moves": move,
    }


def hill_climbing_random_restart(problem, max_restarts):
    """
    When the search would terminate because the best neighbour doesn't
    have a higher value than the current state, generate a new state to
    continue the search from (using problem.random_state).

    But don't do this more than `max_restarts` times. Watch out for
    off by one errors, and don't forget to return early if the search finds
    a goal state.

    To get consistent results each time, call random.seed(YOUR_FAVOURITE_SEED)
    before calling this function.
    """
    ######################
    ### Your code here ###
    ######################
    current = Node(problem=problem, state=problem.initial)
    expand = 0
    bool1 = False
    restart = 0
    x = len(current.state)
    while True:
        if current.goal_test():
            bool1 = True
            break
        neighbours = current.expand()
        expand = expand + 1
        if not neighbours:
            break
        neighbour = current.best_of(neighbours)

        if neighbour.value() <= current.value():
            a = tuple(random.choice(range(x)) for _ in range(x))
            if (restart >= max_restarts):
                break
            current.state = a
            restart = restart + 1
        else:
            current = neighbour
    return {
        "expanded": expand,
        "solved": bool1,
        "best_state": current.state,
        "restarts": restart,
    }

test.py(作业要求,自己用可以不要这个,可以参考里面的运行函数):

# _*_ coding:utf-8 _#_
# 开发团队:抛夏
# 开发人员:抛夏
# 开发时间:2022/11/19 21:49
# 文件名称:test.py
# 开发工具:PyCharm

"""
Run test_all() to test NQueensProblem, hill_climbing_instrumented, hill_climbing_sideways and hill_climbing_random_restart.

Or run their individual tests. Methods to be tested are imported from hill_climbing.py in the same directory as this file.
"""

import random
from functools import wraps

from hill_climbing import (
    NQueensProblem,
    hill_climbing_instrumented,
    hill_climbing_sideways,
    hill_climbing_random_restart,
)


SEED = 1
VERBOSE = True


# {state: problem.result(state, problem.actions(state))}
action_and_result_examples = {
    (0, 0): [(1, 0), (0, 1)],
    (0, 1): [(1, 1), (0, 0)],
    (0, 0, 0, 0): [  # 4 queens, all in row 0
        (1, 0, 0, 0),
        (2, 0, 0, 0),
        (3, 0, 0, 0),
        (0, 1, 0, 0),
        (0, 2, 0, 0),
        (0, 3, 0, 0),
        (0, 0, 1, 0),
        (0, 0, 2, 0),
        (0, 0, 3, 0),
        (0, 0, 0, 1),
        (0, 0, 0, 2),
        (0, 0, 0, 3),
    ],
    (0, 1, 2, 3): [  # 4 queens, arranged diagonally
        (1, 1, 2, 3),
        (2, 1, 2, 3),
        (3, 1, 2, 3),
        (0, 0, 2, 3),
        (0, 2, 2, 3),
        (0, 3, 2, 3),
        (0, 1, 0, 3),
        (0, 1, 1, 3),
        (0, 1, 3, 3),
        (0, 1, 2, 0),
        (0, 1, 2, 1),
        (0, 1, 2, 2),
    ],
}


# {args: f(*args)}
# {(state,): hill_climbing_instrumented(state)}
hill_climbing_instrumented_examples = {
    ((0, 0, 0, 0),): {
        "expanded": 3,
        "solved": True,
        "best_state": (2, 0, 3, 1),
    },
    ((0, 1, 2, 3),): {
        "expanded": 3,
        "solved": False,
        "best_state": (1, 0, 2, 3),
    },
}


# {args: f(*args)}
# {(state, max_sideways_moves): hill_climbing_sideways(state, max_sideways_moves)}
hill_climbing_sideways_examples = {
    ((0, 0, 0, 0), 1): {
        "expanded": 3,
        "solved": True,
        "best_state": (2, 0, 3, 1),
        "sideways_moves": 0,
    },
    ((0, 1, 2, 3), 1): {
        "expanded": 5,
        "solved": False,
        "best_state": (1, 2, 0, 3),
        "sideways_moves": 1,
    },
    ((0, 0, 0, 0), 10): {
        "expanded": 3,
        "solved": True,
        "best_state": (2, 0, 3, 1),
        "sideways_moves": 0,
    },
    ((0, 1, 2, 3), 10): {
        "expanded": 6,
        "solved": True,
        "best_state": (1, 3, 0, 2),
        "sideways_moves": 2,
    },
    ((0, 0, 0, 0, 0, 0, 0, 0), 10): {
        "expanded": 17,
        "solved": False,
        "best_state": (4, 0, 0, 3, 6, 2, 7, 1),
        "sideways_moves": 10,
    },
    ((0, 1, 2, 3, 4, 5, 6, 7), 10): {
        "expanded": 18,
        "solved": False,
        "best_state": (3, 0, 7, 4, 1, 5, 2, 6),
        "sideways_moves": 10,
    },
    ((0, 0, 0, 0, 0, 0, 0, 0), 100): {
        "expanded": 107,
        "solved": False,
        "best_state": (4, 0, 0, 3, 6, 2, 7, 1),
        "sideways_moves": 100,
    },
    ((0, 1, 2, 3, 4, 5, 6, 7), 100): {
        "expanded": 108,
        "solved": False,
        "best_state": (3, 0, 7, 4, 1, 5, 2, 6),
        "sideways_moves": 100,
    },
}


# {args: f(*args)}
# {(state, max_restarts): hill_climbing_random_restart(state, max_restarts)}
hill_climbing_random_restart_examples = {
    ((0, 0, 0, 0), 1): {
        "expanded": 3,
        "solved": True,
        "best_state": (2, 0, 3, 1),
        "restarts": 0,
    },
    ((0, 1, 2, 3), 1): {
        "expanded": 5,
        "solved": False,
        "best_state": (1, 3, 2, 0),
        "restarts": 1,
    },
    ((0, 0, 0, 0), 10): {
        "expanded": 3,
        "solved": True,
        "best_state": (2, 0, 3, 1),
        "restarts": 0,
    },
    ((0, 1, 2, 3), 10): {
        "expanded": 8,
        "solved": True,
        "best_state": (2, 0, 3, 1),
        "restarts": 2,
    },
    ((0, 0, 0, 0, 0, 0, 0, 0), 10): {
        "expanded": 24,
        "solved": True,
        "best_state": (5, 1, 6, 0, 2, 4, 7, 3),
        "restarts": 4,
    },
    ((0, 1, 2, 3, 4, 5, 6, 7), 10): {
        "expanded": 24,
        "solved": True,
        "best_state": (5, 1, 6, 0, 2, 4, 7, 3),
        "restarts": 4,
    },
}


def verbose_test(test):
    @wraps(test)
    def wrapper(*args, **kwargs):
        errors = test(*args, **kwargs)
        if not VERBOSE:
            return errors
        if not errors:
            print("PASSED:", test.__name__)
            return errors
        print()
        print("FAILED:", test.__name__)
        for problem, error in errors.items():
            print("\t", problem, sep="")
            print("\tyour result:", error["result"])
            print("\ttrue result:", error["example_result"])
            if "keys" in error:
                print("\tthese keys were in error:", error["keys"])
            print()
        return errors
    return wrapper


def prefix_errors(test):
    @wraps(test)
    def wrapper(*args, **kwargs):
        errors = test(*args, **kwargs)
        return {
            (test.__name__, problem): error
            for problem, error in errors.items()
        }
    return wrapper


def test_all():
    """Run all tests.

    Call get_all_prefixed_errors to get all errors as a dict.
    """
    get_all_prefixed_errors()


def get_all_prefixed_errors():
    return {
        **prefix_errors(test_action_and_result)(),
        **prefix_errors(test_hill_climbing_instrumented)(),
        **prefix_errors(test_hill_climbing_sideways)(),
        **prefix_errors(test_hill_climbing_random_restart)(),
    }


@verbose_test
def test_action_and_result():
    errors = {}
    for state, example_result in action_and_result_examples.items():
        problem = NQueensProblem(state=state)
        result = [
            problem.result(problem.initial, action)
            for action in problem.actions(problem.initial)
        ]
        if set(result) != set(example_result):
            errors[state] = {
                "example_result": example_result,
                "result": result,
            }
    return errors


@verbose_test
def test_hill_climbing_instrumented():
    return hill_climbing_test(hill_climbing_instrumented)


@verbose_test
def test_hill_climbing_sideways():
    return {
        **vanilla_check(hill_climbing_sideways),
        **hill_climbing_test(hill_climbing_sideways),
    }


@verbose_test
def test_hill_climbing_random_restart():
    return {
        **vanilla_check(hill_climbing_random_restart),
        **hill_climbing_test(hill_climbing_random_restart),
    }


@prefix_errors
def vanilla_check(hill_climbing_method, examples=hill_climbing_instrumented_examples):
    """Check if hill_climbing_method with a restart / sideways move limit of 0 returns results identical to hill_climbing_instrumented."""
    def hill_climbing_proxy(problem):
        return hill_climbing_method(problem, 0)
    return hill_climbing_test(hill_climbing_proxy, examples=examples)


@prefix_errors
def hill_climbing_test(hill_climbing_method, examples=None):
    """Check if provided hill_climbing_method has the correct output."""
    if examples is None:
        examples = globals()[hill_climbing_method.__name__ + "_examples"]
    answers = generate_answers(hill_climbing_method, problems=examples)
    errors = {}
    for problem, example_result in examples.items():
        result = answers[problem]
        keys = []
        for key, value in example_result.items():
            if result[key] != value:
                keys.append(key)
        if keys:
            errors[problem] = {
                "example_result": example_result,
                "result": result,
                "keys": keys,
            }
    return errors


def generate_answers(hill_climbing_method, problems=None):
    """Return dictionary of results of running hill_climbing_method on problems."""
    if problems is None:
        examples = globals()[hill_climbing_method.__name__ + "_examples"]
    answers = {}
    for key in problems:
        state, *args = key
        problem = NQueensProblem(state=state)
        random.seed(SEED)
        result = hill_climbing_method(problem, *args)
        answers[key] = result
    return answers


test_action_and_result()
test_hill_climbing_instrumented()
test_hill_climbing_sideways()
test_hill_climbing_random_restart()

简单记一下自己对python看法的改变..以前一直觉得这种能直接使用的库和函数太多的语言(区别于c和c++),不能完全理解原理就应用,很违背自己一贯的思维方式也很难受,但是现在觉得这恰好能更方便处理很多更高级的问题,因为人的精力是有限的不能一直只关注底层的逻辑,更流畅高效地应用各种库也是一种能力..尤其是在学过opencv之后,这种要解决的问题更多的是数学方面的,那简便的函数工具就是非常必要的了。但是作为初学者,还是不能降低对自己的要求 要尽量多花时间弄懂每一个的原理~

你可能感兴趣的:(人工智能,python,python,启发式算法,开发语言)