Python实现 《算法导论 第三版》中的算法 第4章 分治策略

版权声明:本文为博主原创文章,如有转载请标注来源。 https://blog.csdn.net/shengchaohua163/article/details/82810189

第4章 分治策略

1. 最大子数组问题

P40。认真读一下4.1节,有一定算法基础就可以看懂。这个问题的全称是最大连续子数组问题。那么,最大子数组究竟是什么呢?

要想弄清楚最大(连续)子数组是什么,首先需要明白(连续)子数组。(连续)子数组是数组中连续的几个元素组成的数组。此时会有两种不同理解,子数组可以为空和子数组不能为空。

  1. 子数组可以为空,意思是把空数组也看成子数组。子数组如果为空,认为它的和就是0。所以在这种理解下,该问题的结果不可能为负, 最小值为0。对于元素全负的数组,最大子数组为0。一般以该种理解为主,因为在集合论中,一个集合的子集可以为空。
  2. 子数组不能为空,意思是不把空数组看成子数组。那么,子数组至少含有一个元素。在这种理解下,对于元素全负的数组,该问题的结果为负。

对于既有正数也有负数或只有正数的数组,两种理解的结果相同。只有对于元素全负的数组,这两种理解的结果才不同。

书中的方法采取的是第2种理解方法,它对于元素全负的数组输出一个负数结果。

def find_maximum_subarray(A, low, high):
    if low == high:
        return low, high, A[low]
    else:
        mid = (low + high)// 2
        left_low, left_high, left_sum = find_maximum_subarray(A, low, mid)
        right_low, right_high, right_sum = find_maximum_subarray(A, mid+1, high)
        cross_low, cross_high, cross_sum = find_max_crossing_subarray(A, low, mid, high)
        if left_sum >= right_sum and left_sum >= cross_sum:
            return left_low, left_high, left_sum
        elif right_sum >= left_sum and right_sum >= cross_sum:
            return right_low, right_high, right_sum
        else:
            return cross_low, cross_high, cross_sum
        
        
def find_max_crossing_subarray(A, low, mid, high):
    import math
    left_sum = -math.inf
    sum1 = 0
    left_ind = 0
    for i in range(mid, low-1, -1):
        sum1 += A[i]
        if sum1 > left_sum:
            left_sum = sum1
            left_ind = i
    
    right_sum = -math.inf
    sum2 = 0
    right_ind = 0
    for j in range(mid+1, high+1):
        sum2 += A[j]
        if sum2 > right_sum:
            right_sum = sum2
            right_ind = j
    
    return left_ind, right_ind, left_sum + right_sum


def main():
    A = [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7]
    print(find_maximum_subarray(A, 0, len(A)-1))
    B = [-6 -5, -2, -1, -3] 
    print(find_maximum_subarray(B, 0, len(B)-1))
    
if __name__ == '__main__':
    main()

2. 最大子数组问题的其他解法

P42练习4.1-2。题目要求暴力求解,运行时间为 Θ ( n 2 ) \Theta(n^2) 。下面代码中第一个函数符合题目要求,第二个函数是线性时间复杂度 Θ ( n ) \Theta(n)

def find_maximum_subarray_n2(nums):
    import math
    maximum = -math.inf
    for i in range(0, len(nums)):
        temp = 0
        for j in range(i, len(nums)):
            temp += nums[j]
            if temp > maximum:
                maximum = temp
    return maximum


def find_maximum_subarray_n(nums):
    import math
    maximum = -math.inf
    temp = 0
    for n in nums:
        temp += n
        if temp > maximum:
            maximum = temp
        if temp < 0:
            temp = 0
    return maximum


def main():
    A = [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7]
    print(find_maximum_subarray_n2(A))
    print(find_maximum_subarray_n(A))
    B = [-6 -5, -2, -1, -3] 
    print(find_maximum_subarray_n2(B))
    print(find_maximum_subarray_n(B))
  

if __name__ == '__main__':
    main()

P42练习4.1-4和4.1-5。此时采用第1种理解方法,允许子数组为空。给出三种方法,时间复杂度分别是 Θ ( n 3 ) \Theta(n^3) Θ ( n 2 ) \Theta(n^2) Θ ( n ) \Theta(n)

class MaximumSubarray:
    def find_maximum_n3(self, A):
        maximum = 0
        for i in range(0, len(A)):
            for j in range(i+1, len(A)):
                temp = sum(A[i:j])
                if temp > maximum:
                    maximum = temp
        return maximum
    
    def find_maximum_n2(self, A):
        maximum = 0
        for i in range(0, len(A)):
            temp = 0
            for j in range(i, len(A)):
                temp += A[j]
                if temp > maximum:
                    maximum = temp
        return maximum
    
    def find_maximum_n(self, A):
        maximum = 0
        temp = 0
        for i in range(0, len(A)):
            temp += A[i]
            if temp > maximum:
                maximum = temp
            elif temp < 0:
                temp = 0
        return maximum
            
    
def main():
    A = [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7]
    m = MaximumSubarray()
    print(m.find_maximum_n3(A))
    print(m.find_maximum_n2(A))
    print(m.find_maximum_n(A))
    B = [-6 -5, -2, -1, -3]
    print(m.find_maximum_n3(B))
    print(m.find_maximum_n2(B))
    print(m.find_maximum_n(B))
  
if __name__ == '__main__':
    main()

3. Strassen算法

矩阵相乘(矩阵为方阵,且行数列数为2的幂次)的第一个暴力算法和第二个分治算法花了好长时间才写完,Strassen算法还要写个一两天,真是太菜了…

# -*- coding: utf-8 -*-
def square_matrix_multiply(A, B):
    n = len(A)
    res = []
    for i in range(n):
        temp = [0] * n
        for j in range(n):
            for k in range(n):
                temp[j] += A[i][k] * B[k][j]
        res.append(temp)
    return res


def square_matrix_multiply_recursive(A, B):
    n = len(A)
    if n == 1:
        return [[A[0][0] * B[0][0]]]
    else:
        mid = n // 2
        A11 = [A[i][0:mid] for i in range(mid)] # Split A and B
        A12 = [A[i][mid:] for i in range(mid)]
        A21 = [A[i][0:mid] for i in range(mid,n)]
        A22 = [A[i][mid:] for i in range(mid,n)]
        B11 = [B[i][0:mid] for i in range(mid)]
        B12 = [B[i][mid:] for i in range(mid)]
        B21 = [B[i][0:mid] for i in range(mid,n)]
        B22 = [B[i][mid:] for i in range(mid,n)]
        
        C11_1 = square_matrix_multiply_recursive(A11, B11) # Compute the partitions of C
        C11_2 = square_matrix_multiply_recursive(A12, B21)
        C12_1 = square_matrix_multiply_recursive(A11, B12)
        C12_2 = square_matrix_multiply_recursive(A12, B22)
        C21_1 = square_matrix_multiply_recursive(A21, B11)
        C21_2 = square_matrix_multiply_recursive(A22, B21)
        C22_1 = square_matrix_multiply_recursive(A21, B12)
        C22_2 = square_matrix_multiply_recursive(A22, B22)
        
        C11 = [[C11_1[i][j] + C11_2[i][j] for j in range(mid)] for i in range(mid)]
        C12 = [[C12_1[i][j] + C12_2[i][j] for j in range(mid)] for i in range(mid)]
        C21 = [[C21_1[i][j] + C21_2[i][j] for j in range(mid)] for i in range(mid)]
        C22 = [[C22_1[i][j] + C22_2[i][j] for j in range(mid)] for i in range(mid)]
        C = [C11[i] + C12[i] for i in range(mid)] + \
            [C21[i] + C22[i] for i in range(mid)]
        
        return C       
        

def main():
    A = [[1,2], [2,3]]
    B = [[1,1], [2,2]]
    print(square_matrix_multiply(A, B))
    print(square_matrix_multiply_recursive(A, B))
    A = [[1,2,3,4], [2,3,4,5], [3,4,5,6], [4,5,6,7]]
    B = [[1,1,1,1], [2,2,2,2], [3,3,3,3], [4,4,4,4]]
    print(square_matrix_multiply(A, B))
    print(square_matrix_multiply_recursive(A, B))
    
    
if __name__ == '__main__':
    main()

猜你喜欢

转载自blog.csdn.net/shengchaohua163/article/details/82810189