# Chap 1 递归与分治

# 整数划分

将正整数 nn 表示成一系列正整数之和

正整数 nn 的一个这种表示称为正整数 nn 的一个划分,记 为正整数 nn 的不同的划分个数

解:

nn 的所有不同的划分中,将最大加数 不大于 mm 的划分个数记作 , 而

有递归关系如下:

  • : 任何正整数只有一种划分形式

  • : 最大加数实际上不能大于nn

  • :

  • : 不大于 mm 的划分由 组成

有递归函数如下:

q(n,m)=\left\{ \begin{array}{rcl} 1 & & {n=1,m=1}\\ q(n,n) & & {n \lt m}\\ 1+ q(n,n-1) & & {n = m}\\ q(n,m-1)+q(n-m,m) & & {n \gt m \gt 1} \end{array} \right.

# 汉诺塔

# 三柱汉诺塔

image-20200324111847564

规则:每次移动一个圆盘,任意时刻不允许大圆盘压在小圆盘上

问题:从A到B的移动次数 + 移动过程

解答:

, 则移动一次 即可

,则将其分解成三个操作,一是将 个盘借助 BB 移动到 CC,二是将最大的盘从 AA 移到 BB,三是借助 AA 个盘从 CC 移到 BB

故递归函数如下:

T(n)=\left\{ \begin{array}{rcl} 1 & & {n=1}\\ 2 \times T(n-1)+1 & & {n \gt 1}\\ \end{array} \right.

Python 实现如下:

from dataclasses import dataclass

@dataclass
class Hanoi:
    name: str
    count: int

def move(n, A, B):
    print('第{}个块从{}移到{}'.format(n, A.name, B.name))

def hanoi(n, A, B, C):
    if n > 0:
        hanoi(n-1, A, C, B)
        move(n, A, B)
        hanoi(n-1, C, B, A)

n = int(input('个数:'))
A = Hanoi('A', n)
B = Hanoi('B', 0)
C = Hanoi('C', 0)

hanoi(n, A, B, C)
# 四柱汉诺塔

算法:1941 Frame

1、将A柱上部分的n-r个碟子通过C柱和D柱移到B柱上 ( 步)

2、将A柱上剩余的n个碟子通过C柱移到D柱上 ( 步)

3、将B柱上的n-r个碟子通过A柱和C柱移到D柱上 ( 步)

递归方程如下:

2004年的一篇论文 (opens new window)证明了 时,取到最小值

# 多柱汉诺塔

留坑

# 大整数乘法

image-20200324121753654

上式可知进行了 3次n/2位整数的乘法(AC,BD,(A-B)(D-C)),6次加减法 和 2次移位,故有递推式如下:

T(n)=\left\{ \begin{array}{rcl} O(1) & & {n=1}\\ 3T(n/2) + O(n) & & {n \gt 1}\\ \end{array} \right.

解得 , 与传统乘法的 相比,有了较大提升

# Strassen矩阵乘法

考虑n阶方阵的乘法运算 ; 传统方法使用 ,算法复杂度为

考虑 nn22 的幂,重写矩阵乘法为:\begin{bmatrix} C_{11} & C_{12} \\ C_{21} & C_{22} \end{bmatrix} = \begin{bmatrix} A_{11} & A_{12} \\ A_{21} & A_{22} \end{bmatrix} \quad \begin{bmatrix} B_{11} & B_{12} \\ B_{21} & B_{22} \end{bmatrix}

下面的传统分治策略使用了8次乘法4次加法

\begin{gathered} C_{11} = A_{11}B_{11}+A_{12}B_{21} \quad C_{12} = A_{11}B_{12}+A_{12}B_{22} \quad C_{21} = A_{21}B_{11}+A_{22}B_{21} \quad C_{22} = A_{21}B_{12}+A_{22}B_{22} \\ \\ T(n)=\left\{ \begin{array}{rcl} O(1) & & {n=2}\\ 8T(n/2) + O(n^2) & & {n \gt 2}\\ \end{array} \right.\end{gathered}

递归下去还是的复杂度,没有改进

Strassen提出了7次乘法的方法, 得到 , 有很大改进

目前最好的计算时间上界是 ,最好下界是它的平凡上界

# 棋盘覆盖

个方格的棋盘中,缺失一个方格,使用L型骨牌覆盖棋盘,求解一种覆盖方式

考虑在中间放置一块L型牌,将一个 边长的缺失棋盘分割成4个的缺失棋盘

递归方程为:T(k)=\left\{ \begin{array}{rcl} O(1) & & {k=0}\\ 4T(k-1) + O(1) & & {k \gt 0}\\ \end{array} \right.,解得 ,为渐进意义下的最优算法

# 线性时间选择

给定线性序集中 nn 个元素和整数 kk ,要求找出 nn 个元素中第 kk 小的元素

当 k = 1 或 k = n 时,即为找最大最小值,可以在 时间内找出

时,通过最大堆或最小堆排序算法,能在 时间内找出

考虑一般情况,使用快排中的切分法,每次找正确的一半,也能在 中的时间给出正确答案

def partition(a, start, end):
  '使第j个位置拥有正确的数'
  i = start
  j = end + 1
  x = a[start]
  while True:
    while a[++i] < x
    while a[--j] > x
    if i >= j:
      break
    a[i], a[j] = a[j], a[i]
  a[start] = a[j]
  a[j] = x
  return j

def quicksort(a, start, end):
  '先搞定第j个位置,再处理前面和后面的位置'
  if start < end:
    q = partition(a, start, end)
    quicksort(a, start, q-1)
    quicksort(a, q+1, end)


def randomizedPartition(a, start, end):
  i = Random(start, end)
  a[i], a[start] = a[start], a[i]
  return partition(a, start, end)

def randomizedSelect(a, start, end, k):
  if start == end:
    return a[start]
  i = randomizedPartition(a, start, end)
  j = i - start + 1
  if k <= j:
    return randomizedSelect(a, start, i, k)
  else:
    return randomizedSelect(a, i+1, end, k)
 
# 计算第k小的数
randomizedSelect(a, 0, n-1, k)

# 最接近点对问题

设有 nn 个点,若两两计算距离,则时间复杂度为

考虑 nn 个点的横坐标中位数是 , 用垂线 将点集分割为大小大致相等的两个子集

递归处理 ,分别得到 中的最小距离

SS 的最近点对 之间的距离小于 dd ,则 不在同一点集中,给定 pp ,则 qq 必定落在一个 矩形 RR

image-20200324173204453

dd 的意义可知,中任何两个 SS 中的点的距离都不小于 dd,我们将矩形 RR 的长边三等份,短边二等分,形成6个小区域,若矩形 RR 中有多于 6个 SS 中的点,则根据抽屉原理,必定存在一个抽屉,有两个点,而同一抽屉中最大距离为对角线,长度为 ,矛盾,故最多只有6个点需要检验。在分治法的合并中,我们最多需要检查 个候选者,而不是 个 ,故时间复杂度递推式为 T(n)=\left\{ \begin{array}{rcl} O(1) & & {n \lt 4}\\ 2T(n/2) + O(n) & & {n \geq 4}\\ \end{array} \right.

易得 , 为渐进意义下的最优算法

最后更新: 5/31/2022, 6:43:40 AM