如何理解分治思想

简介: 分治思想就是把复杂问题、拆分成诺干个相同的小问题,然后将问题逐步解决掉,合并到一起的过程,就是分治思想。简单来说,分治思想就是“分而治之”,将复杂问题拆分成诺干个相同的小问题进行解决。

分治思想就是把复杂问题、拆分成诺干个相同的小问题,然后将问题逐步解决掉,合并到一起的过程,就是分治思想。简单来说,分治思想就是“分而治之”,将复杂问题拆分成诺干个相同的小问题进行解决。

image.png

那么如何实现分治思维去解决问题呢?首先分解的问题要与整个问题的规则要一致,否则就无法使用分治去解决问题,总体可总结为:

分解:将原问题分解为若干个规模较小,相对独立,与原问题形式相同的子问题。

解决:若子问题规模较小且易于解决时,则直接解。否则,递归地解决各子问题。

合并:将各子问题的解合并为原问题的解。

有哪些场景中使用到了分治去解决问题呢,在上文中我们讲解了排序、当时我们只讲解了冒牌排序、选择排序,插入排序,高级一点的排序并没有涉及到,因为像归并排序、推排序、快速排序涉及到更多的知识点需要去讲解和个人去了解堆概念和递归思想。今天应用的分治思想就是完全适用于归并排序,归并排序同时还要去理解递归思想。

如果对递归不理解的,需要去学习下,要不没办法继续下去,分治思想最著名的体现就是汉诺塔。

image.png

相信大家都玩过汉诺塔吧,那么汉诺塔是如何来的呢?

传说越南河内某间寺院有三根银棒,上串 64 个金盘。寺院里的僧侣依照一个古老的预言,以上述规则移动这些盘子;预言说当这些盘子移动完毕, 世界就会灭亡。这个传说叫做梵天寺之塔问题(Tower of Brahma puzzle)。但不知道是卢卡斯自创的这个传说,还是他受他人启发。 若传说属实,僧侣们需要二的64次方减去一步才能完成这个任务;若他们每秒可完成一个盘子的移动,就需要 5849 亿年才能完成。整个宇宙现在也不过 137 亿年。这就是汉诺塔的由来。

算法求解
解法的基本思想是递归。假设有 A、B、C 三个塔,A塔有N块盘,目标是把这些盘全部移到 C 塔。那么先把 A 塔顶部的N-1块盘移动到 B 塔,再把 A 塔剩下的大盘移到 C,最后把B塔的N-1块盘移到 C。

汉诺塔问题
从左到右有A、B、C三根柱子,其中A柱子 上面有从小叠到大的n个圆盘,现要求将A柱子上的圆盘移到C柱子上去,期间只有一个原则:一次只能移到 一个盘子且大盘子不能在小盘子上面,求移动的步骤和移动的次数

移动的规则:

1.每次只能移动一个圆盘;

2.大盘不能叠在小盘上面。

def hanoi(n, a, b, c):
    if n == 1:
        print(a, '-->', c)
    else:
        hanoi(n - 1, a, c, b)
        hanoi(1, a, b, c)
        hanoi(n - 1, b, a, c)


if __name__ == "__main__":
    print(hanoi(2, 'A', 'B', 'C'))

图解解释
可以用无向图来表示汉诺塔, 在表示的时候会更加地直观和清晰, 虽然说理解上有一点点小难度.

现在规定, 每一个节点表示盘子的位置一种可能性, 每一条边表示一种移动的方法.

注: 这里不考虑在两个柱子之间的, 没有意义的, 来回移动的情况.

对于只有一个盘子的汉诺塔,可以表示为:

image.png

对于有两个盘子的汉诺塔, 可以表示为:

相互连接的三个三角形, 组成了一个较大三角形的三个角。

每一个节点的第二个字母表示更大的盘子, 且最初时没有被移动。

对于每一个顶端的小三角形, 表示两个盘子的一种移动的方法:

image.png

外围的三角形的每一个节点, 表示在一个柱子上盘子的所有分布可能.。

对于 h+1 个盘子, 就可以”复制” h 个盘子时候的三角形图, 然后拼成一个新的大三角形图, 稍微改动一下,

这个大的三角形图就可以用来表示 h+1 个盘子时的情况了.

所以当有三个盘子时, 图形为:

image.png

A, b 和 c 表示三个柱子

按照从小到大的顺序, 从左到右地列出的盘子的位置.

最外面的三角形的边, 表示了盘子从一个柱子移动到另一个柱子最快的方式. 最大的三角形可以沿着中线分成三个次小的三角形, 就是上面由二级的汉诺塔组成三级的汉诺塔的逆向操作, 次小三角形相互之间的连线, 表示着最大的盘子的移动方式.

同理, 在这次三角形的也可以沿其中线分割成为三个次次三角形, 一样的, 次次小三角形相互之间的连线, 表示着次大的盘子的移动方式. 继续下去, 也就可以表示出一个汉诺塔的移动方式.

通常,对于具有 n 个盘子的图, 有3n个节点; 每个节点都有三条边连接着其他节点, 但是在顶点的的节点只却只有有两条边连接着其他节点.所以说总是下都可以将最小的盘子移动到另外两个柱子中的一个, 对于多数情况, 是可以在两个柱子间移动一个盘子, 除了所有的盘子都在一个柱子上. 边角的节点表示着所有的盘子都在一个柱子的情况, 即可以在 a, b 或 c 柱上堆满盘子, 显然只要三种. 对于n+1个盘子的图, 可以通过表示n给盘子的图 “复制” 三份, 组合在一起的. 因此也就很方便地表示了每一层的汉诺塔移动方式, 每一个次小的三角形表示次小的盘子的所有可能的移动方式和放置状态, 次小的三角形之间的连接表示了大盘子的三种可能的移动方式. 所以图形有3n+1个节点, 基本都有三个与之相连接的边,而顶点只有两个.

在盘子数比较多的时候, 汉诺塔的图像就会开始和分形图比较相似了.

该图较为清楚地表达了:

对于任意的全部盘子在一根柱子的情况下, 将所有盘子移动到另一个柱子的最短路径只有一个.

对于任意的两个盘子分布情况之间转换的时候, 只有一个或者两个不同的最短路径.

对于任意的盘子分布情况, 都有一个或者两个将所有盘子移动到任意一个柱子上的最长不相交路径.

对于任意的两个盘子分布情况之间转换的时候, 只有一个或者两个不同的最长不相交路径.

设Nh是将有h个盘子的塔, 将所有盘子从一个柱子移动到另一个柱子的非相交路径的数量(一开始盘子都在一个柱子上). 可以得出: file

这里的image.png

除了汉诺塔,还有其他的案例

归并排序

二分搜索

大整数乘法

Strassen矩阵乘法

棋盘覆盖

快速排序

线性时间选择

最接近点对问题

循环赛日程表

青蛙跳台阶问题

分治法的复杂性分析

一个分治法将规模为n的问题分成k个规模为n/m的子问题去解。设分解阀值n0=1,且adhoc解规模为1的问题耗费1个单位时间。再设将原问题分解为k个子问题以及用merge将k个子问题的解合并为原问题的解需用f(n)个单位时间。用T(n)表示该分治法解规模为|P|=n的问题所需的计算时间,则有: T(n)= k T(n/m)+f(n) 通过迭代法求得方程的解: 递归方程及其解只给出n等于m的方幂时T(n)的值,但是如果认为T(n)足够平滑,那么由n等于m的方幂时T(n)的值可以估计T(n)的增长速度。通常假定T(n)是单调上升的,从而当 mi≤n<mi+1时,T(mi)≤T(n)<T(mi+1)。

依据分治法设计程序时的思维过程

实际上就是类似于数学归纳法,找到解决本问题的求解方程公式,然后根据方程公式设计递归程序。

1、一定是先找到最小问题规模时的求解方法

2、然后考虑随着问题规模增大时的求解方法

3、找到求解的递归函数式后(各种规模或因子),设计递归程序即可。

我们通常使用分治思想去解决大数据量问题,以及可以给我们一个思考,当我们遇到无法解决的问题,可以将问题拆分开来,逐步解决,这样就可以实现赚它一个亿的小目标。

相关文章
|
7月前
|
人工智能 算法
【算法分析与设计】递归与分治策略(二)
【算法分析与设计】递归与分治策略
|
9天前
|
算法
数据结构与算法-递归思想
数据结构与算法-递归思想
6 0
|
2月前
|
存储 缓存 算法
程序设计中的递归思想与实践
程序设计中的递归思想与实践
13 0
|
7月前
|
机器学习/深度学习 算法 编译器
【算法分析与设计】递归与分治策略(一)
【算法分析与设计】递归与分治策略
|
4月前
|
算法
回溯算法思想
回溯算法思想
15 0
|
7月前
|
算法 搜索推荐 Windows
【算法分析与设计】递归与分治策略(三)
【算法分析与设计】递归与分治策略
|
算法 安全
数据结构与算法之二分查找&&分而治之思想
数据结构与算法之二分查找&&分而治之思想
91 0
数据结构与算法之二分查找&&分而治之思想
|
算法
算法排序问题。每种排序代表着没中思考问题的方式。我们学习了选择排序,冒泡排序,归并排序。让我们去回顾回顾吧。重在思想的领悟。
算法排序问题。每种排序代表着没中思考问题的方式。我们学习了选择排序,冒泡排序,归并排序。让我们去回顾回顾吧。重在思想的领悟。
66 0
算法排序问题。每种排序代表着没中思考问题的方式。我们学习了选择排序,冒泡排序,归并排序。让我们去回顾回顾吧。重在思想的领悟。
【数据结构和算法思想】递归思想
在程序中可以调用函数来完成任务,为了完成相同的任务可以调用同一个函数。如果在函数中调用函数本身,那么改函数就被称为递归函数。递归函数的调用是按层,不是次,有 N 层就同时调用(打开)了 N 个函数,不是 N 次。 无限递归(递而不归、死递归),栈溢出(函数的调用有时间和空间的开销,一个程序中同时调用的函数个数是有限的)。• 递归函数的调用有时间和空间的开销,而且递归的次数受到堆栈大小的限制。 • 循环没有函数调用和返回中的参数传递和返回值的额外开销,更快。 如何在递归和循环之间选择? 一般情况下,当循环方法比较容易实现时,应该避免使用递归。当很难简历一个循环方法时,递归可能是一个很好的选择
|
机器学习/深度学习 算法 搜索推荐
<<算法很美>>——(二)详解递归思想
<<算法很美>>——(二)详解递归思想
<<算法很美>>——(二)详解递归思想