返回 登录
1

分治算法



一、基本概念

将一个复杂的问题分解为多个相同或相似的子问题,通过递归的方式,将子问题继续分解,直到子问题容易解决。

二、特征

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]

A0A1A2A3B0B1B2B3为矩阵

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;
}
评论