一、基本概念
将一个复杂的问题分解为多个相同或相似的子问题,通过递归的方式,将子问题继续分解,直到子问题容易解决。
二、特征
1.分治法所能解决的问题一般具有以下几个特征:
1. 该问题的规模缩小到一定的程度就可以容易地解决
2. 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
3. 利用该问题分解出的子问题的解可以合并为该问题的解;
4. 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
2.特征解释
1. 第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;
2. 第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归;
3. 第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法;
4. 第四条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。
三、使用场景
问题规模线性增长,带动的计算量为非线性增长,且满足分治算法的三个特征(2、3、4),则可使用分支算法。
四、矩阵乘法
1.矩阵乘法公式
C(i,j)=∑k=1nA(i,k)×B(k,j)
2. 是否符合分治算法
1.条件(1)显然满足
2.条件(2)(3)满足,证明如下:
C=A×B=[A0A2A1A3]×[B0B2B1B3]=[A0×B0+A1×B2A2×B0+A3×B2A0×B1+A1×B3A2×B1+A3×B3]
A0A1A2A3与 B0B1B2B3为矩阵
3. 使用分治算法的思想
将N×N阶的矩阵转换为(N/2)×(N/2)阶的矩阵,采用递归的方式不断缩小矩阵相乘时矩阵的阶数,直到方便求解。
4. C# 代码实现
/// <summary>
/// 直接求两个矩阵的乘积
/// C[i,j] = Sum(A[i,k] * B[k,j]) (k = 1~n)
/// </summary>
/// <param name="p"></param>
/// <param name="q"></param>
/// <returns></returns>
int[,] DirectMultiplication(int[,] p,int[,] q)
{
/// p的列数 == q的行数 才能相乘
if (p.GetLength(1) != q.GetLength(0))
{
return null;
}
int[,] result = new int[p.GetLength(0),q.GetLength(1)];
for (int i = 0; i < p.GetLength(0); i++)
{
for (int j = 0; j < q.GetLength(1); j++)
{
result[i,j] = 0;
for (int k = 0; k < p.GetLength(1); k++)
{
result[i,j] += p[i,k] * q[k,j];
}
}
}
return result;
}
/// <summary>
/// 递归计算矩阵相乘
/// </summary>
/// <param name="p"></param>
/// <param name="q"></param>
/// <param name="n"></param>
/// <returns></returns>
int[,] DivideAndConquer(int[,] p, int[,] q, int n)
{
if (!IsPowerValueOfTwo(n))
{
return DirectMultiplication(p, q);
}
else
{
if (n == 2)
{
return DirectMultiplication(p, q);
}
if (n > 2)
{
int[,] result = new int[n,n];
int[][,] pMatrix = PartitionMatrix(p, n);
int[][,] qMatrix = PartitionMatrix(q, n);
int[][,] rMatrix = PartitionMatrix(result, n);
/// 核心
/// A[0] * B[0] + A[1] * B[2]
/// A[0] * B[1] + A[1] * B[3]
/// A[2] * B[0] + A[3] * B[2]
/// A[2] * B[1] + A[3] * B[3]
for (int i = 0; i < rMatrix.GetLength(0); i++)
{
int row = 2 * (i / 2);
int col = i % 2;
rMatrix[i] = AddMatrix(DivideAndConquer(pMatrix[row], qMatrix[col], n / 2),
DivideAndConquer(pMatrix[row + 1], qMatrix[col + 2], n / 2),
n / 2);
}
/// 合并矩阵
result = MergeMatrix(rMatrix, n / 2);
return result;
}
else
{
return null;
}
}
}
/// <summary>
/// 是否为2的幂次方
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
bool IsPowerValueOfTwo(int n)
{
while (true)
{
if (n == 1) return true;
if (n % 2 == 1) return false;
else n = n / 2;
}
}
/// <summary>
/// 矩阵加法
/// </summary>
/// <param name="p"></param>
/// <param name="q"></param>
/// <param name="n"></param>
/// <returns></returns>
int[,] AddMatrix(int[,] p, int[,] q, int n)
{
int[,] result = new int[n,n];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
result[i,j] = p[i,j] + q[i,j];
}
}
return result;
}
/// <summary>
/// 分解矩阵
/// </summary>
/// <param name="matrix"></param>
/// <param name="n"></param>
/// <returns></returns>
int[][,] PartitionMatrix(int[,] matrix,int n)
{
int newLen = n/2;
/// 初始化数组
int[][,] pMatrix = new int[4][,];
for (int i = 0; i < 4; i++)
{
pMatrix[i] = new int[newLen, newLen];
}
for (int k = 0; k < 4; k++)
{
int startRow = (k / 2) * newLen;
int startCol = (k % 2) * newLen;
for (int i = 0; i < newLen; i++)
{
for (int j = 0; j < newLen; j++)
{
pMatrix[k][i,j] = matrix[i + startRow,j + startCol];
}
}
}
return pMatrix;
}
/// <summary>
/// 合并矩阵
/// </summary>
/// <param name="rMatrix"></param>
/// <param name="n"></param>
/// <returns></returns>
int[,] MergeMatrix(int[][,] rMatrix,int n)
{
int[,] matrix = new int[2 * n,2 * n];
for (int k = 0; k < rMatrix.GetLength(0); k++)
{
int startRow = (k / 2) * n;
int startCol = (k % 2) * n;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
matrix[i + startRow,j + startCol] = rMatrix[k][i,j];
}
}
}
return matrix;
}