动态规划的思想和数学归纳法非常相似。
归纳推理的过程如下:
- 首先证明P(1)成立,即公式在n = 1时成立。
- 然后证明从P(m) 成立可以推导出P(m+1) 也成立。(这里实际应用的是演绎推理法)
- 根据上两条从P(1)成立可以推导出P(1+1),也就是P(2)成立。
- 继续推导,可以知道P(3)成立。
- 从P(3)成立可以推导出P(4)也成立。
- 不断不断不断的重复推導下一命題成立的步驟。(这就是所谓“归纳”推理的地方)
- 我们便可以下结论:对于任意自然数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]
背包问题的解法
定义背包问题:
先用动态规划解。
我们设剩余个物品,剩余可用重量为, 目前最大价值为
- 当 时
- 当 时,
- 当 或 时,返回
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)