zoukankan      html  css  js  c++  java
  • 分治法

    分治法:

    分治算法 这个博客介绍了分治的基本内容

    首先回答分治法的基本思想:在解决一个问题的时候,可以把这个问题分成子问题,子问题的求解方式和原问题基本相同,这样可以不断划分,直到问题能够以最小的形式解决,然后将子问题的结果合并起来就是原问题的解决方法。

    分治法的适用情况:1:问题规模足够小的时候能够解决。 2:该问题可以划分为规模较小的若干问题。 3:子问题的解合并是原来问题的解。 4:各个子问题相互独立。

    分治法的执行过程:1:问题拆分。 2:子问题求解。 3:合并。

    算法复杂性分析:复杂性是一个递归公式,$T(n) = aT(frac{n}{b}) + f(n)$ b为子问题相对于原问题的规模,a为子问题个数,f(n)表示剩余程序的复杂度。

    若f(n)复杂度为$O(n^d)$,这个时候公式变为了$T(n) = aT(frac{n}{b}) + O(n^d)$  ,一般用下面的主方法进行分析。

    $$ T(x)=left{ egin{aligned} O(n^d)  & & a< b^d\ O(n^d log n)  & & a = b^d \ O(n^{log_b a}) & & a>b^d end{aligned} ight. $$

    下面是一些分治法的例子:

    归并排序

    若将一个数组排序,1:需要将其拆分为左右两部分 2:左右两部分都需要进行排序  3:合并的时候使用外排的方法进行合并。

    def merge_sort(array):
        """归并排序
    
        使用分治法的思想来进行排序, 使用递归的方法来进行实现
    
        时间复杂度:使用master公式  a=2, b=2。除去递归,其它问题的复杂度,也就是merge的复杂度o(n),
                  所以,整体的复杂度为O(n*logn)
        空间复杂度:归并排序每次递归需要用到一个辅助表,长度与待排序的表相等,
                  虽然递归次数是O(log2n),但每次递归都会释放掉所占的辅助空间,
                  所以下次递归的栈空间和辅助空间与这部分释放的空间就不相关了,因而空间复杂度还是O(n)
        稳定性: 稳定性算法。在merge的时候,如果两个值相等,可以控制让左边先进来,然后右边再进来。
        """
        if len(array) <= 1:
            return array
        mid = int(len(array)/2)
        left = merge_sort(array[:mid])
        right = merge_sort(array[mid:])
        return merge(left, right)
    
    def merge(left, right):
        """合并算法
    
        刚开始并没与价差两个数组的长度,而是直接相减,直到有一个为为止
    
        时间复杂度:o(n)  需要遍历一边left和right
        空间复杂度为 o(n) 需要一个result数组来存储结果
    
        """
        result  = []
        l, r = 0, 0
        while(l < len(left) and r < len(right)):
            if left[l] < right[r]:
                result.append(left[l])
                l += 1
            else:
                result.append(right[r])
                r += 1
    
        result += (left[l:])
        result += (right[r:])
        return result

    最大子数组问题:

      来自算法导论4.1章,问题是这样的,给定一组数组,求这个数组当中连续数组和的最大值,和数组的边界。比如在数组

    [13, -3, -25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7] 中最大子数组是从18到12的连续4个数,它们的和为43。

      将原来的数组划分为两个数组,这样最大子数组只可能有三种情况:完全位于左边数组,完全位于右边数组,位于两个数组中间,对于前两个问题,递归的进行求解,对于第三个问题可以设计单独的函数求解。

    采用分治法的方法来解决这个问题:1:可以将一个数组分为左右两个部分。 2:变成三个子问题:分别求左边部分中的最大子数组、右边部分的最大子数组 ,还有包含中间节点的最大子数组。3:合并这三种情况。上面划线的分析是错误的,2:变为两个子问题,求解左边部分的最大子数组,求解右边部分的最大子数组,使用递归的方式来解。 3:考虑最大子数组的三种情况,然后进行合并,因为求解包含中间节点的最大子数组没有调用递归算法,所以不属于子问题,而划分到合并里面。

    def find_maximum_subarray(arr, low, high):
        """发现最大子数组"""
        if low == high:
            return (low, high, arr[low])
    
        mid = int((low+high)/2)
        left_low, left_high, left_sum = find_maximum_subarray(arr, low, mid)          # 划分并且求解左子部分
        right_low, right_high, right_sum = find_maximum_subarray(arr, mid + 1, high)  # 划分并且求解右子部分
        mid_low, mid_high, mid_sum = find_max_crossing_subarray(arr, low, mid, high)  # 求解出现在中间的情况
    
        # 合并子问题
        if left_sum >= right_sum and left_sum >= mid_sum:
            return left_low, left_high, left_sum
        elif right_sum >= left_sum and right_sum >= mid_sum:
            return right_low, right_high, right_sum
        else:
            return mid_low, mid_high, mid_sum
    
    
    
    def find_max_crossing_subarray(arr, low, mid, high):
        """当数组最大值包含mid的时候的求解方法"""
    
        # 先求解low到min的最大子数组
        left_sum = float('-inf')
        all_sum = 0
        left_index = mid
        for index in range(mid, low-1, -1):
            all_sum += arr[index]
            if all_sum > left_sum:
                left_sum = all_sum
                left_index = index
    
        # 然后求解mid到high的最大子数组
        right_sum = float('-inf')
        all_sum = 0
        right_index = mid+1
        for index in range(mid+1, high+1):
            all_sum += arr[index]
            if all_sum > right_sum:
                right_sum = all_sum
                right_index = index
        # 将两部分的结果合并
        return (left_index, right_index, left_sum+right_sum)
    
    def max_subarray(arr):
        return find_maximum_subarray(arr, 0, len(arr)-1)
    
    
    if __name__ == '__main__':
        arr = [13, -3, -25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7]
        x = max_subarray(arr)
        print(x)

    使用动态规划的方式来解:

    def dp_max_sub_array(arr):
        if len(arr) == 0:
            return 0
    
        max_sub = arr[0]
        tmp_sub = arr[0]
        for i in range(1, len(arr)):
            tmp_sub = max(tmp_sub + arr[i], arr[i])
            max_sub = max(max_sub, tmp_sub)
        return max_sub

     

  • 相关阅读:
    为什么世界上没有安全的工作?
    GIT学习----第六节:撤销修改
    vue-cli中koa输出console.log报错的解决方案
    简易版promise源码实现
    bind函数的模拟实现
    Es6语法实现的转盘抽奖效果——可配置转盘的抽奖概率
    探寻Object.assign内部的奥秘
    promise基本使用——简单的运动效果
    数组sort方法源码解析
    ES5数组一些常用的方法源码实现
  • 原文地址:https://www.cnblogs.com/jiaxin359/p/9270741.html
Copyright © 2011-2022 走看看