矩阵连乘积
定义:给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2,…n-1。考察这n个矩阵的连乘积A1A2…An。
- 由于矩阵乘法满足结合律,所以计算矩阵的连乘- 可以有许多不同的计算次序。
- 这种计算次序可以用加括号的方式来确定。
- 若一个矩阵连乘积的计算次序完全确定,也就是说该连乘积已完全加括号,则可以依此次序反复调用2个矩阵相乘的标准算法计算出矩阵连乘积
完全加括号的矩阵连乘积可递归地定义为:
- 单个矩阵是完全加括号的;
- 矩阵连乘积A是完全加括号的,则A可表示为2个完全加括号的矩阵连乘积B和C的乘积并加括号,即A=(BC)
设有四个矩阵A, B, C, D,它们的维数分别是:
A=50×10, B=10×40, C=40×30, D=30×5
矩阵A和B可乘的条件: 矩阵A的列数等于矩阵B的行数。
设A是p×q的矩阵, B是q×r的矩阵, 乘积是p×r的矩阵;计算量是pqr。
上述5种完全加括号方式的计算工作量为:
(A((BC)D)) | (A(B(CD))) | ((AB)(CD)) | (((AB)C)D) | ((A(BC)D)) |
---|---|---|---|---|
16000 | 10500 | 36000 | 87500 | 34500 |
BC: 10×40×30 = 12000,
(BC)D: 10×30×5 = 1500,
(A((BC)D)): 50×10×5 = 2500
给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少?
- 穷举法:列举出所有可能的计算次序,并计算出每一种计算次序相应需要的数乘次数,从中找出一种数乘次数最少的计算次序。
算法复杂度分析:
对于n个矩阵的连乘积,设其不同的计算次序为P(n)。
由于每种加括号方式都可以分解为两个子矩阵的加括号问题:(A1…Ak)(Ak+1…An)可以得到关于P(n)的递推式如下:
最优解的结构
将矩阵连乘积AiAi+1…Aj 简记为A[i:j], 这里i≤j;
考察计算A[1:n]的最优计算次序。
- 设这个计算次序在矩阵Ak和Ak+1之间将矩阵链断开,1≤k<n,则其相应完全加括号方式为(A1A2…Ak)(Ak+1Ak+2…An)
- 计算量:A[1:k]的计算量加上A[k+1:n]的计算量,再加上A[1:k]和A[k+1:n]相乘的计算量
特征:计算A[1:n]的最优次序所包含的计算矩阵子链 A[1:k]和A[k+1:n]的次序也是最优的。
矩阵连乘计算次序问题的最优解包含着其子问题的最优解。
- 这种性质称为最优子结构性质。
- 问题的最优子结构性质是该问题可用动态规划算法求解的显著特征。
递归关系
设计算A[i: j],1≤i≤j≤n,所需要的最少数乘次数m[i, j],则原问题的最优值为m[1,n]
当i=j时,A[i: j]=Ai,因此,m[i, i]=0,i=1,2,…,n
当i<j 时,
这里Ai的维数是Pi-1×Pi
m[i][j]给出了最优值,最优断开位置为k:
若将对应于m[i, j]的断开位置k记为s[i, j],在计算出最优值m[i, j]后,可递归的由s[i, j]构造出相应的最优解。
最优值
对于1≤i≤j≤n不同的有序对(i, j)对应于不同的子问题。因此,不同子问题的个数最多只有
递归计算时,许多子问题被重复计算多次。
- 这也是该问题可用动态规划算法求解的又一显著特征。
用动态规划算法解此问题,可依据其递归式以自底向上的方式进行计算。
- 在计算过程中,保存已解决的子问题答案。
- 每个子问题只计算一次,在后面需要时只要简单查一下,从而避免大量的重复计算,最终得到多项式时间的算法。
#define NUM 51
int p[NUM];
int m[NUM][NUM];
int s[NUM][NUM];
void MatrixChain (int n)
{
for (int i=1; i<=n; i++) m[i][i] = 0;
for (int r=2; r<=n; r++)
for (int i=1; i<=n-r+1; i++)
{
int j=i+r-1;
//计算初值,从i处断开
m[i][j] = m[i+1][j]+p[i-1]*p[i]*p[j];
s[i][j] = i;
for (int k=i+1; k<j; k++)
{
int t = m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
if (t < m[i][j]) {m[i][j] = t; s[i][j] = k;}
}
}
}
最优解
s[i][j]已经存储了构造最优解所需要的足够的信息。
每个部分的最优加括号方式可以根据数组s的相应元素得出。
照此递推下去,最终可以确定A[1:n]的最优完全加括号方式,即构造出问题的一个最优解。
void TraceBack(int i, int j)
{
if(i==j) printf("A%d", i);
else
{
printf("(");
TraceBack(i,s[i][j]);
TraceBack(s[i][j]+1,j);
printf(")");
}
}
慕课
选最大与最小
- 输入:集合 L (含n个不等的实数)
- 输出:L中第 i 小元素
i=1, 称为最小元素
i=n,称为最大元素
位置处在中间的元素,称为中位元素
n为奇数,中位数唯一,i = (n+1)/2
n为偶数,可指定 i = n/2+1
分组算法
伪码
算法 Findmax
输入:n 个数的数组 L
输出:max, k
- max<-L[1]
- for i<-2 to n do
- if max < L[i]
- then max<-L[i]
- k<-i
- return max,k
分治算法
- 将数组 L从中间划分为两个子数组 L1 和 L2
- 递归地在L1中求最大 max1 和min1
- 递归地在L2中求最大max2和min2
- max<-max{ max1 , max2}
- min<-min{ min1, min2}
比较次数 3n/2-2
比较次数 :3n/2-2
选第二大
- 输入:n个数的数组 L
- 输出:第二大的数 second
通常算法
顺序比较
1.顺序比较找到最大 max
2.从剩下 n-1个数中找最大,就是第二大second
时间复杂度:n-1+n-2=2n-3
锦标赛算法
提高效率的途径:
•成为第二大数的条件:仅在与最大 数的比较中被淘汰.
•要确定第二大数,必须知道最大数.
•在确定最大数的过程中记录下被最大数直接淘汰的元素.
•在上述范围(被最大数直接淘汰的数)内的最大数就是第二大数。
设计思想:空间换时间
算法:
1.两两分组比较,大者进入下一轮,直到剩下1个元素max 为止
2.在每次比较中淘汰较小元素,将被淘汰元素记录在淘汰它的元素的链表上
3.检查 max 的链表,从中找到最大元,即second
伪码:
算法 FindSecond
输入: n个数的数组 L, 输出: second
- k <- n // 参与淘汰的元素数
- 将k个元素两两1组, 分成⌊k/2⌋ 组
- 每组的2个数比较,找到较大数
- 将被淘汰数记入较大数的链表
- if k 为奇数 then k <- ⌊k/2⌋ +1
- else k< - ⌊k/2⌋
- if k>1 then goto 2
- max <- 最大数
- second <- max的链表中的最大
时间复杂度: n+⌈logn⌉-2
一般选择问题的算法设计
选第 k 小的算法:
• 分治策略
• 确定m*
• 用m*划分数组归约为子问题
• 递归实现
选择问题的算法分析
伪码
算法 Select (S, k)
- 将S分5个一组, 共 nM = ⌈n/5⌉ 组
- 每组排序,中位数放到集合 M
- m* <- Select(M, ⌈ |M|/2⌉ ) //S分A,B,C,D
- A,D元素小于m* 放 S1,大于m*放S2
- S1 <- S1∪C;S2 <- S2∪B
- if k = |S1| + 1 then 输出 m*
- else if k<=|S1|
- then Select (S1, k)
- else Select (S2, k-|S1|-1)
时间复杂度: Θ(nlogn)
卷积及应用
卷积计算
快速傅立叶变换FF
平面点集的凸包