动态规划与搜索算法

动态规划的思想和数学归纳法非常相似。

归纳推理的过程如下:

  1. 首先证明P(1)成立,即公式在n = 1时成立。
  2. 然后证明从P(m) 成立可以推导出P(m+1) 也成立。(这里实际应用的是演绎推理法)
  3. 根据上两条从P(1)成立可以推导出P(1+1),也就是P(2)成立。
  4. 继续推导,可以知道P(3)成立。
  5. 从P(3)成立可以推导出P(4)也成立。
  6. 不断不断不断的重复推導下一命題成立的步驟。(这就是所谓“归纳”推理的地方)
  7. 我们便可以下结论:对于任意自然数n,P(n) 成立。

Fibonacci 数列的解法

经典的fibonacci数列动态规划解法:

def fibonacci(n):
    assert n > 0
    if n == 1 or n == 2:
        return 1
    else:
        return fibonacci(n) + fibonacci(n+1)

上述代码问题在于,随着N的增大,展开的节点成指数级增长,很多的节点其实被重复计算了。解决的办法很简单,用一个字典记录之前的计算,下次遇到这种节点就不用再算了,直接取字典值就好,即用空间换时间:

fib = {1: 1, 2: 1}
def fibonacci(n):
    assert n > 0
    if n in fib:
        return fib[n]
    else:
        fib[n] = fibonacci(n-1) + fibonacci(n-2)
        return fib[n]

背包问题的解法

定义背包问题:

先用动态规划解。
我们设剩余个物品,剩余可用重量为, 目前最大价值为

  1. 当 时
  2. 当 时,

  3. 当 或 时,返回
p = [11,21,31,33,43,43,55,65]
w = [1,11,21,23,33,43,45,55]
W = 110
def knapsack(wn, pn, n):
    if wn < w[n-1] or n == 0:
        return pn
    else:
        p_choose = knap_sack(wn - w[n-1], pn + p[n-1], n-1)
        np_choose = knap_sack(wn, pn, n-1)

        if p_choose >= np_choose:
            return p_choose
        else:
            return np_choose

print(knapsack(W, 0, len(p)))

实验结果为151

为了保留搜索过程,我们用Binary Tree来解

class Node:
    def __init__(self, indices, z, w, parrent=None, left=None, right=None):
        self.parrent = parrent
        self.left = left # Choose Current
        self.right = right # Not Choose
        self.indices = indices 
        self.z = z # Current Value
        self.w = w # Current Weight Left
class Solution:

    p = [11,21,31,33,43,43,55,65]
    w = [1,11,21,23,33,43,45,55]
    W = 110

    assert len(p) == len(w)
    N = len(w)



    def build_tree(self, parrent):
        if len(parrent.indices) == 0:
            return
        else:
            index = parrent.indices[0]
            if parrent.w <= self.w[index]:
                return

            # Choose k[0]
            z = parrent.z + self.p[index]
            w = parrent.w - self.w[index]
            left_node = Node(parrent.indices[1:], z, w, parrent)
            
            # Not choose k[0]
            right_node = Node(parrent.indices[1:], parrent.z, parrent.w, parrent)

            parrent.left = left_node
            parrent.right = right_node

            self.build_tree(left_node)
            self.build_tree(right_node)

    def walk_tree(self, node):
        if node.left is None and node.right is None:
            if node.z > self.best.z:
                self.best = node
                return

        else:
            self.walk_tree(node.left)
            self.walk_tree(node.right)


    def print_path(self, node):
        if node.parrent is not None:
            if node.parrent.left == node:
                print("Choose ", node.parrent.indices[0], ", W left:", node.w, ", Current Price", node.z)
            elif node.parrent.right == node:
                print("Not Choose ", node.parrent.indices[0], ", W left:", node.w, ", Current Price", node.z)
            self.print_path(node.parrent)

    def solve(self):
        # Build the tree
        root = Node(list(range(self.N)), 0, self.W)
        self.build_tree(root)

        # Walk to find max
        self.best = root
        self.walk_tree(root)

        # Print result
        self.print_path(self.best)

八皇后问题解法

八皇后问题是指在国际象棋棋盘上放置8个皇后棋子,使得任意两个棋子之间不能互相攻击。
皇后棋子会攻击位于其同行,同列,以及同一条45度斜线上的所有棋子。
问题如出一辙,解法如下

import numpy as np
import random

class BoardNode:
    def __init__(self, board, last_place=None, queen_left=8, parrent=None, children=None):
        self.board = board
        self.parrent = parrent
        self.children = children
        self.queen_left = queen_left
        self.last_place = last_place

    def is_full(self):
        return self.board.sum() == 64

    def place_at(self, i, j):
        board = np.copy(self.board)
        board[i, :] = 1
        board[:, j] = 1

        for dx in range(-8, 8):
            if 0 <= i + dx < 8 and 0 <= j + dx < 8: 
                board[i+dx, j+dx] = 1
            if 0 <= i - dx < 8 and 0 <= j + dx < 8:
                board[i-dx, j+dx] = 1
        return board

    def places(self):
        if self.last_place is None:
            start_i = 0
        else:
            start_i = self.last_place[0]
        return [(i, j) for i in range(start_i, 8) for j in range(8) if self.board[i, j] != 1]

def print_board(board):
    out = ""
    for row in board:
        for x in row:
            if x == 0:
                out += " + "
            else:
                out += " X "
        out += " \n"
    print(out)
    return out


class EightQuees:
    solution = None
    count = 0
    board = np.zeros((8, 8), dtype=np.int)
    places = []

    def build_tree(self, node):
        if node.is_full():
            if node.queen_left == 0:
                self.solution = node
                self.count += 1
                print(self.count)

                self.places = []
                self.board = np.zeros((8, 8), dtype=np.int)
                self.print_node(self.solution)
                print_board(self.board)
                print(self.places)
            return


        elif node.queen_left > 0:
            for place in node.places():
                board = node.place_at(*place)
                child = BoardNode(board, place, node.queen_left-1, node)
                node.children = child
                self.build_tree(child)

        # node.children = children

        # for child in node.children:
        #     self.build_tree(child)
        
    
    def print_node(self, node):
        if node.last_place is not None:
            self.board[node.last_place] = 1
            self.places.append(node.last_place)
        if node.parrent is not None:
            self.print_node(node.parrent)

    def solve(self):
        root = BoardNode(np.zeros((8, 8), dtype=np.int))
        self.build_tree(root)

深度优先,广度优先,ASTAR 搜索算法

你可能感兴趣的:(动态规划与搜索算法)